mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-22 17:51:08 -05:00
okay fine
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Interfaces
|
||||
|
||||
This package implements the Python "scarecrow" proposal.
|
||||
|
||||
The package exports two objects, `Interface` and `Attribute` directly. It also
|
||||
exports several helper methods. Interface is used to create an interface with
|
||||
a class statement, as in:
|
||||
|
||||
class IMyInterface(Interface):
|
||||
'''Interface documentation
|
||||
'''
|
||||
|
||||
def meth(arg1, arg2):
|
||||
'''Documentation for meth
|
||||
'''
|
||||
|
||||
# Note that there is no self argument
|
||||
|
||||
To find out what you can do with interfaces, see the interface
|
||||
interface, `IInterface` in the `interfaces` module.
|
||||
|
||||
The package has several public modules:
|
||||
|
||||
o `declarations` provides utilities to declare interfaces on objects. It
|
||||
also provides a wide range of helpful utilities that aid in managing
|
||||
declared interfaces. Most of its public names are however imported here.
|
||||
|
||||
o `document` has a utility for documenting an interface as structured text.
|
||||
|
||||
o `exceptions` has the interface-defined exceptions
|
||||
|
||||
o `interfaces` contains a list of all public interfaces for this package.
|
||||
|
||||
o `verify` has utilities for verifying implementations of interfaces.
|
||||
|
||||
See the module doc strings for more information.
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
# pylint:disable=wrong-import-position,unused-import
|
||||
from zope.interface.interface import Interface
|
||||
from zope.interface.interface import _wire
|
||||
|
||||
|
||||
# Need to actually get the interface elements to implement the right interfaces
|
||||
_wire()
|
||||
del _wire
|
||||
|
||||
from zope.interface.declarations import Declaration # isort: skip
|
||||
# The following are to make spec pickles cleaner
|
||||
from zope.interface.declarations import Provides
|
||||
from zope.interface.declarations import alsoProvides
|
||||
from zope.interface.declarations import classImplements
|
||||
from zope.interface.declarations import classImplementsFirst
|
||||
from zope.interface.declarations import classImplementsOnly
|
||||
from zope.interface.declarations import directlyProvidedBy
|
||||
from zope.interface.declarations import directlyProvides
|
||||
from zope.interface.declarations import implementedBy
|
||||
from zope.interface.declarations import implementer
|
||||
from zope.interface.declarations import implementer_only
|
||||
from zope.interface.declarations import moduleProvides
|
||||
from zope.interface.declarations import named
|
||||
from zope.interface.declarations import noLongerProvides
|
||||
from zope.interface.declarations import providedBy
|
||||
from zope.interface.declarations import provider
|
||||
from zope.interface.exceptions import Invalid
|
||||
from zope.interface.interface import Attribute
|
||||
from zope.interface.interface import interfacemethod
|
||||
from zope.interface.interface import invariant
|
||||
from zope.interface.interface import taggedValue
|
||||
from zope.interface.interfaces import IInterfaceDeclaration
|
||||
|
||||
|
||||
moduleProvides(IInterfaceDeclaration)
|
||||
|
||||
__all__ = ('Interface', 'Attribute') + tuple(IInterfaceDeclaration)
|
||||
|
||||
assert all(k in globals() for k in __all__)
|
||||
136
.venv/lib/python3.12/site-packages/zope/interface/_compat.py
Normal file
136
.venv/lib/python3.12/site-packages/zope/interface/_compat.py
Normal file
@@ -0,0 +1,136 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2006 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""
|
||||
Support functions for dealing with differences in platforms, including Python
|
||||
versions and implementations.
|
||||
|
||||
This file should have no imports from the rest of zope.interface because it is
|
||||
used during early bootstrapping.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def _normalize_name(name):
|
||||
if isinstance(name, bytes):
|
||||
name = str(name, 'ascii')
|
||||
if isinstance(name, str):
|
||||
return name
|
||||
raise TypeError("name must be a string or ASCII-only bytes")
|
||||
|
||||
|
||||
PYPY = hasattr(sys, 'pypy_version_info')
|
||||
|
||||
|
||||
def _c_optimizations_required():
|
||||
"""
|
||||
Return a true value if the C optimizations are required.
|
||||
|
||||
This uses the ``PURE_PYTHON`` variable as documented in `_use_c_impl`.
|
||||
"""
|
||||
pure_env = os***REMOVED***iron.get('PURE_PYTHON')
|
||||
require_c = pure_env == "0"
|
||||
return require_c
|
||||
|
||||
|
||||
def _c_optimizations_available():
|
||||
"""
|
||||
Return the C optimization module, if available, otherwise
|
||||
a false value.
|
||||
|
||||
If the optimizations are required but not available, this
|
||||
raises the ImportError.
|
||||
|
||||
This does not say whether they should be used or not.
|
||||
"""
|
||||
catch = () if _c_optimizations_required() else (ImportError,)
|
||||
try:
|
||||
from zope.interface import _zope_interface_coptimizations as c_opt
|
||||
return c_opt
|
||||
except catch: # pragma: no cover (only Jython doesn't build extensions)
|
||||
return False
|
||||
|
||||
|
||||
def _c_optimizations_ignored():
|
||||
"""
|
||||
The opposite of `_c_optimizations_required`.
|
||||
"""
|
||||
pure_env = os***REMOVED***iron.get('PURE_PYTHON')
|
||||
return pure_env is not None and pure_env != "0"
|
||||
|
||||
|
||||
def _should_attempt_c_optimizations():
|
||||
"""
|
||||
Return a true value if we should attempt to use the C optimizations.
|
||||
|
||||
This takes into account whether we're on PyPy and the value of the
|
||||
``PURE_PYTHON`` environment variable, as defined in `_use_c_impl`.
|
||||
"""
|
||||
is_pypy = hasattr(sys, 'pypy_version_info')
|
||||
|
||||
if _c_optimizations_required():
|
||||
return True
|
||||
if is_pypy:
|
||||
return False
|
||||
return not _c_optimizations_ignored()
|
||||
|
||||
|
||||
def _use_c_impl(py_impl, name=None, globs=None):
|
||||
"""
|
||||
Decorator. Given an object implemented in Python, with a name like
|
||||
``Foo``, import the corresponding C implementation from
|
||||
``zope.interface._zope_interface_coptimizations`` with the name
|
||||
``Foo`` and use it instead.
|
||||
|
||||
If the ``PURE_PYTHON`` environment variable is set to any value
|
||||
other than ``"0"``, or we're on PyPy, ignore the C implementation
|
||||
and return the Python version. If the C implementation cannot be
|
||||
imported, return the Python version. If ``PURE_PYTHON`` is set to
|
||||
0, *require* the C implementation (let the ImportError propagate);
|
||||
note that PyPy can import the C implementation in this case (and all
|
||||
tests pass).
|
||||
|
||||
In all cases, the Python version is kept available. in the module
|
||||
globals with the name ``FooPy`` and the name ``FooFallback`` (both
|
||||
conventions have been used; the C implementation of some functions
|
||||
looks for the ``Fallback`` version, as do some of the Sphinx
|
||||
documents).
|
||||
|
||||
Example::
|
||||
|
||||
@_use_c_impl
|
||||
class Foo(object):
|
||||
...
|
||||
"""
|
||||
name = name or py_impl.__name__
|
||||
globs = globs or sys._getframe(1).f_globals
|
||||
|
||||
def find_impl():
|
||||
if not _should_attempt_c_optimizations():
|
||||
return py_impl
|
||||
|
||||
c_opt = _c_optimizations_available()
|
||||
if not c_opt: # pragma: no cover (Jython doesn't build extensions)
|
||||
return py_impl
|
||||
|
||||
__traceback_info__ = c_opt
|
||||
return getattr(c_opt, name)
|
||||
|
||||
c_impl = find_impl()
|
||||
# Always make available by the FooPy name and FooFallback
|
||||
# name (for testing and documentation)
|
||||
globs[name + 'Py'] = py_impl
|
||||
globs[name + 'Fallback'] = py_impl
|
||||
|
||||
return c_impl
|
||||
@@ -0,0 +1,36 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Adapter-style interface registry
|
||||
|
||||
See Adapter class.
|
||||
"""
|
||||
from zope.interface import Declaration
|
||||
|
||||
|
||||
def _flatten(implements, include_None=0):
|
||||
|
||||
try:
|
||||
r = implements.flattened()
|
||||
except AttributeError:
|
||||
if implements is None:
|
||||
r = ()
|
||||
else:
|
||||
r = Declaration(implements).flattened()
|
||||
|
||||
if not include_None:
|
||||
return r
|
||||
|
||||
r = list(r)
|
||||
r.append(None)
|
||||
return r
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
1048
.venv/lib/python3.12/site-packages/zope/interface/adapter.py
Normal file
1048
.venv/lib/python3.12/site-packages/zope/interface/adapter.py
Normal file
File diff suppressed because it is too large
Load Diff
121
.venv/lib/python3.12/site-packages/zope/interface/advice.py
Normal file
121
.venv/lib/python3.12/site-packages/zope/interface/advice.py
Normal file
@@ -0,0 +1,121 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Class advice.
|
||||
|
||||
This module was adapted from 'protocols.advice', part of the Python
|
||||
Enterprise Application Kit (PEAK). Please notify the PEAK authors
|
||||
(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or
|
||||
Zope-specific changes are required, so that the PEAK version of this module
|
||||
can be kept in sync.
|
||||
|
||||
PEAK is a Python application framework that interoperates with (but does
|
||||
not require) Zope 3 and Twisted. It provides tools for manipulating UML
|
||||
models, object-relational persistence, aspect-oriented programming, and more.
|
||||
Visit the PEAK home page at http://peak.telecommunity.com for more information.
|
||||
"""
|
||||
|
||||
from types import FunctionType
|
||||
|
||||
|
||||
__all__ = [
|
||||
'determineMetaclass',
|
||||
'getFrameInfo',
|
||||
'isClassAdvisor',
|
||||
'minimalBases',
|
||||
]
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def getFrameInfo(frame):
|
||||
"""Return (kind,module,locals,globals) for a frame
|
||||
|
||||
'kind' is one of "exec", "module", "class", "function call", or "unknown".
|
||||
"""
|
||||
|
||||
f_locals = frame.f_locals
|
||||
f_globals = frame.f_globals
|
||||
|
||||
sameNamespace = f_locals is f_globals
|
||||
hasModule = '__module__' in f_locals
|
||||
hasName = '__name__' in f_globals
|
||||
|
||||
sameName = hasModule and hasName
|
||||
sameName = sameName and f_globals['__name__'] == f_locals['__module__']
|
||||
|
||||
module = hasName and sys.modules.get(f_globals['__name__']) or None
|
||||
|
||||
namespaceIsModule = module and module.__dict__ is f_globals
|
||||
|
||||
if not namespaceIsModule:
|
||||
# some kind of funky exec
|
||||
kind = "exec"
|
||||
elif sameNamespace and not hasModule:
|
||||
kind = "module"
|
||||
elif sameName and not sameNamespace:
|
||||
kind = "class"
|
||||
elif not sameNamespace:
|
||||
kind = "function call"
|
||||
else: # pragma: no cover
|
||||
# How can you have f_locals is f_globals, and have '__module__'
|
||||
# set? # This is probably module-level code, but with a
|
||||
# '__module__' variable.
|
||||
kind = "unknown"
|
||||
return kind, module, f_locals, f_globals
|
||||
|
||||
|
||||
def isClassAdvisor(ob):
|
||||
"""True if 'ob' is a class advisor function"""
|
||||
return isinstance(ob, FunctionType) and hasattr(ob, 'previousMetaclass')
|
||||
|
||||
|
||||
def determineMetaclass(bases, explicit_mc=None):
|
||||
"""Determine metaclass from 1+ bases and optional explicit __metaclass__"""
|
||||
|
||||
meta = [getattr(b, '__class__', type(b)) for b in bases]
|
||||
|
||||
if explicit_mc is not None:
|
||||
# The explicit metaclass needs to be verified for compatibility
|
||||
# as well, and allowed to resolve the incompatible bases, if any
|
||||
meta.append(explicit_mc)
|
||||
|
||||
if len(meta) == 1:
|
||||
# easy case
|
||||
return meta[0]
|
||||
|
||||
candidates = minimalBases(meta) # minimal set of metaclasses
|
||||
|
||||
if len(candidates) > 1:
|
||||
# We could auto-combine, but for now we won't...
|
||||
raise TypeError("Incompatible metatypes", bases)
|
||||
|
||||
# Just one, return it
|
||||
return candidates[0]
|
||||
|
||||
|
||||
def minimalBases(classes):
|
||||
"""Reduce a list of base classes to its ordered minimum equivalent"""
|
||||
candidates = []
|
||||
|
||||
for m in classes:
|
||||
for n in classes:
|
||||
if issubclass(n, m) and m is not n:
|
||||
break
|
||||
else:
|
||||
# m has no subclasses in 'classes'
|
||||
if m in candidates:
|
||||
candidates.remove(m) # ensure that we're later in the list
|
||||
candidates.append(m)
|
||||
|
||||
return candidates
|
||||
@@ -0,0 +1,291 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
|
||||
import itertools
|
||||
from types import FunctionType
|
||||
|
||||
from zope.interface import Interface
|
||||
from zope.interface import classImplements
|
||||
from zope.interface.interface import InterfaceClass
|
||||
from zope.interface.interface import _decorator_non_return
|
||||
from zope.interface.interface import fromFunction
|
||||
|
||||
|
||||
__all__ = [
|
||||
# Nothing public here.
|
||||
]
|
||||
|
||||
# pylint:disable=inherit-non-class,
|
||||
# pylint:disable=no-self-argument,no-method-argument
|
||||
# pylint:disable=unexpected-special-method-signature
|
||||
|
||||
|
||||
class optional:
|
||||
# Apply this decorator to a method definition to make it
|
||||
# optional (remove it from the list of required names), overriding
|
||||
# the definition inherited from the ABC.
|
||||
def __init__(self, method):
|
||||
self.__doc__ = method.__doc__
|
||||
|
||||
|
||||
class ABCInterfaceClass(InterfaceClass):
|
||||
"""
|
||||
An interface that is automatically derived from a
|
||||
:class:`abc.ABCMeta` type.
|
||||
|
||||
Internal use only.
|
||||
|
||||
The body of the interface definition *must* define
|
||||
a property ``abc`` that is the ABC to base the interface on.
|
||||
|
||||
If ``abc`` is *not* in the interface definition, a regular
|
||||
interface will be defined instead (but ``extra_classes`` is still
|
||||
respected).
|
||||
|
||||
Use the ``@optional`` decorator on method definitions if
|
||||
the ABC defines methods that are not actually required in all cases
|
||||
because the Python language has multiple ways to implement a protocol.
|
||||
For example, the ``iter()`` protocol can be implemented with
|
||||
``__iter__`` or the pair ``__len__`` and ``__getitem__``.
|
||||
|
||||
When created, any existing classes that are registered to conform
|
||||
to the ABC are declared to implement this interface. This is *not*
|
||||
automatically updated as the ABC registry changes. If the body of the
|
||||
interface definition defines ``extra_classes``, it should be a
|
||||
tuple giving additional classes to declare implement the interface.
|
||||
|
||||
Note that this is not fully symmetric. For example, it is usually
|
||||
the case that a subclass relationship carries the interface
|
||||
declarations over::
|
||||
|
||||
>>> from zope.interface import Interface
|
||||
>>> class I1(Interface):
|
||||
... pass
|
||||
...
|
||||
>>> from zope.interface import implementer
|
||||
>>> @implementer(I1)
|
||||
... class Root(object):
|
||||
... pass
|
||||
...
|
||||
>>> class Child(Root):
|
||||
... pass
|
||||
...
|
||||
>>> child = Child()
|
||||
>>> isinstance(child, Root)
|
||||
True
|
||||
>>> from zope.interface import providedBy
|
||||
>>> list(providedBy(child))
|
||||
[<InterfaceClass __main__.I1>]
|
||||
|
||||
However, that's not the case with ABCs and ABC interfaces. Just
|
||||
because ``isinstance(A(), AnABC)`` and ``isinstance(B(), AnABC)``
|
||||
are both true, that doesn't mean there's any class hierarchy
|
||||
relationship between ``A`` and ``B``, or between either of them
|
||||
and ``AnABC``. Thus, if ``AnABC`` implemented ``IAnABC``, it would
|
||||
not follow that either ``A`` or ``B`` implements ``IAnABC`` (nor
|
||||
their instances provide it)::
|
||||
|
||||
>>> class SizedClass(object):
|
||||
... def __len__(self): return 1
|
||||
...
|
||||
>>> from collections.abc import Sized
|
||||
>>> isinstance(SizedClass(), Sized)
|
||||
True
|
||||
>>> from zope.interface import classImplements
|
||||
>>> classImplements(Sized, I1)
|
||||
None
|
||||
>>> list(providedBy(SizedClass()))
|
||||
[]
|
||||
|
||||
Thus, to avoid conflicting assumptions, ABCs should not be
|
||||
declared to implement their parallel ABC interface. Only concrete
|
||||
classes specifically registered with the ABC should be declared to
|
||||
do so.
|
||||
|
||||
.. versionadded:: 5.0.0
|
||||
"""
|
||||
|
||||
# If we could figure out invalidation, and used some special
|
||||
# Specification/Declaration instances, and override the method
|
||||
# ``providedBy`` here, perhaps we could more closely integrate with ABC
|
||||
# virtual inheritance?
|
||||
|
||||
def __init__(self, name, bases, attrs):
|
||||
# go ahead and give us a name to ease debugging.
|
||||
self.__name__ = name
|
||||
extra_classes = attrs.pop('extra_classes', ())
|
||||
ignored_classes = attrs.pop('ignored_classes', ())
|
||||
|
||||
if 'abc' not in attrs:
|
||||
# Something like ``IList(ISequence)``: We're extending
|
||||
# abc interfaces but not an ABC interface ourself.
|
||||
InterfaceClass.__init__(self, name, bases, attrs)
|
||||
ABCInterfaceClass.__register_classes(
|
||||
self, extra_classes, ignored_classes,
|
||||
)
|
||||
self.__class__ = InterfaceClass
|
||||
return
|
||||
|
||||
based_on = attrs.pop('abc')
|
||||
self.__abc = based_on
|
||||
self.__extra_classes = tuple(extra_classes)
|
||||
self.__ignored_classes = tuple(ignored_classes)
|
||||
|
||||
assert name[1:] == based_on.__name__, (name, based_on)
|
||||
methods = {
|
||||
# Passing the name is important in case of aliases,
|
||||
# e.g., ``__ror__ = __or__``.
|
||||
k: self.__method_from_function(v, k)
|
||||
for k, v in vars(based_on).items()
|
||||
if isinstance(v, FunctionType) and
|
||||
not self.__is_private_name(k) and
|
||||
not self.__is_reverse_protocol_name(k)
|
||||
}
|
||||
|
||||
methods['__doc__'] = self.__create_class_doc(attrs)
|
||||
# Anything specified in the body takes precedence.
|
||||
methods.update(attrs)
|
||||
InterfaceClass.__init__(self, name, bases, methods)
|
||||
self.__register_classes()
|
||||
|
||||
@staticmethod
|
||||
def __optional_methods_to_docs(attrs):
|
||||
optionals = {k: v for k, v in attrs.items() if isinstance(v, optional)}
|
||||
for k in optionals:
|
||||
attrs[k] = _decorator_non_return
|
||||
|
||||
if not optionals:
|
||||
return ''
|
||||
|
||||
docs = "\n\nThe following methods are optional:\n - " + "\n-".join(
|
||||
f"{k}\n{v.__doc__}" for k, v in optionals.items()
|
||||
)
|
||||
return docs
|
||||
|
||||
def __create_class_doc(self, attrs):
|
||||
based_on = self.__abc
|
||||
|
||||
def ref(c):
|
||||
mod = c.__module__
|
||||
name = c.__name__
|
||||
if mod == str.__module__:
|
||||
return "`%s`" % name
|
||||
if mod == '_io':
|
||||
mod = 'io'
|
||||
return f"`{mod}.{name}`"
|
||||
|
||||
implementations_doc = "\n - ".join(
|
||||
ref(c)
|
||||
for c in sorted(self.getRegisteredConformers(), key=ref)
|
||||
)
|
||||
if implementations_doc:
|
||||
implementations_doc = (
|
||||
"\n\nKnown implementations are:\n\n - " + implementations_doc
|
||||
)
|
||||
|
||||
based_on_doc = (based_on.__doc__ or '')
|
||||
based_on_doc = based_on_doc.splitlines()
|
||||
based_on_doc = based_on_doc[0] if based_on_doc else ''
|
||||
|
||||
doc = """Interface for the ABC `{}.{}`.\n\n{}{}{}""".format(
|
||||
based_on.__module__, based_on.__name__,
|
||||
attrs.get('__doc__', based_on_doc),
|
||||
self.__optional_methods_to_docs(attrs),
|
||||
implementations_doc
|
||||
)
|
||||
return doc
|
||||
|
||||
@staticmethod
|
||||
def __is_private_name(name):
|
||||
if name.startswith('__') and name.endswith('__'):
|
||||
return False
|
||||
return name.startswith('_')
|
||||
|
||||
@staticmethod
|
||||
def __is_reverse_protocol_name(name):
|
||||
# The reverse names, like __rand__,
|
||||
# aren't really part of the protocol. The interpreter has
|
||||
# very complex behaviour around invoking those. PyPy
|
||||
# doesn't always even expose them as attributes.
|
||||
return name.startswith('__r') and name.endswith('__')
|
||||
|
||||
def __method_from_function(self, function, name):
|
||||
method = fromFunction(function, self, name=name)
|
||||
# Eliminate the leading *self*, which is implied in
|
||||
# an interface, but explicit in an ABC.
|
||||
method.positional = method.positional[1:]
|
||||
return method
|
||||
|
||||
def __register_classes(self, conformers=None, ignored_classes=None):
|
||||
# Make the concrete classes already present in our ABC's registry
|
||||
# declare that they implement this interface.
|
||||
conformers = (
|
||||
conformers if conformers is not None
|
||||
else self.getRegisteredConformers()
|
||||
)
|
||||
ignored = (
|
||||
ignored_classes if ignored_classes is not None
|
||||
else self.__ignored_classes
|
||||
)
|
||||
for cls in conformers:
|
||||
if cls in ignored:
|
||||
continue
|
||||
classImplements(cls, self)
|
||||
|
||||
def getABC(self):
|
||||
"""
|
||||
Return the ABC this interface represents.
|
||||
"""
|
||||
return self.__abc
|
||||
|
||||
def getRegisteredConformers(self):
|
||||
"""
|
||||
Return an iterable of the classes that are known to conform to
|
||||
the ABC this interface parallels.
|
||||
"""
|
||||
based_on = self.__abc
|
||||
|
||||
# The registry only contains things that aren't already
|
||||
# known to be subclasses of the ABC. But the ABC is in charge
|
||||
# of checking that, so its quite possible that registrations
|
||||
# are in fact ignored, winding up just in the _abc_cache.
|
||||
try:
|
||||
registered = (
|
||||
list(based_on._abc_registry) + list(based_on._abc_cache)
|
||||
)
|
||||
except AttributeError:
|
||||
# Rewritten in C in CPython 3.7.
|
||||
# These expose the underlying weakref.
|
||||
from abc import _get_dump
|
||||
data = _get_dump(based_on)
|
||||
registry = data[0]
|
||||
cache = data[1]
|
||||
registered = [x() for x in itertools.chain(registry, cache)]
|
||||
registered = [x for x in registered if x is not None]
|
||||
|
||||
return set(itertools.chain(registered, self.__extra_classes))
|
||||
|
||||
|
||||
def _create_ABCInterface():
|
||||
# It's a two-step process to create the root ABCInterface, because without
|
||||
# specifying a corresponding ABC, using the normal constructor gets us a
|
||||
# plain InterfaceClass object, and there is no ABC to associate with the
|
||||
# root.
|
||||
abc_name_bases_attrs = ('ABCInterface', (Interface,), {})
|
||||
instance = ABCInterfaceClass.__new__(
|
||||
ABCInterfaceClass, *abc_name_bases_attrs,
|
||||
)
|
||||
InterfaceClass.__init__(instance, *abc_name_bases_attrs)
|
||||
return instance
|
||||
|
||||
|
||||
ABCInterface = _create_ABCInterface()
|
||||
@@ -0,0 +1,122 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
"""
|
||||
Interface definitions for builtin types.
|
||||
|
||||
After this module is imported, the standard library types will declare
|
||||
that they implement the appropriate interface.
|
||||
|
||||
.. versionadded:: 5.0.0
|
||||
"""
|
||||
|
||||
from zope.interface import classImplements
|
||||
from zope.interface.common import collections
|
||||
from zope.interface.common import io
|
||||
from zope.interface.common import numbers
|
||||
|
||||
|
||||
__all__ = [
|
||||
'IList',
|
||||
'ITuple',
|
||||
'ITextString',
|
||||
'IByteString',
|
||||
'INativeString',
|
||||
'IBool',
|
||||
'IDict',
|
||||
'IFile',
|
||||
]
|
||||
|
||||
|
||||
# pylint:disable=no-self-argument
|
||||
class IList(collections.IMutableSequence):
|
||||
"""
|
||||
Interface for :class:`list`
|
||||
"""
|
||||
extra_classes = (list,)
|
||||
|
||||
def sort(key=None, reverse=False):
|
||||
"""
|
||||
Sort the list in place and return None.
|
||||
|
||||
*key* and *reverse* must be passed by name only.
|
||||
"""
|
||||
|
||||
|
||||
class ITuple(collections.ISequence):
|
||||
"""
|
||||
Interface for :class:`tuple`
|
||||
"""
|
||||
extra_classes = (tuple,)
|
||||
|
||||
|
||||
class ITextString(collections.ISequence):
|
||||
"""
|
||||
Interface for text ("unicode") strings.
|
||||
|
||||
This is :class:`str`
|
||||
"""
|
||||
extra_classes = (str,)
|
||||
|
||||
|
||||
class IByteString(collections.IByteString):
|
||||
"""
|
||||
Interface for immutable byte strings.
|
||||
|
||||
On all Python versions this is :class:`bytes`.
|
||||
|
||||
Unlike :class:`zope.interface.common.collections.IByteString`
|
||||
(the parent of this interface) this does *not* include
|
||||
:class:`bytearray`.
|
||||
"""
|
||||
extra_classes = (bytes,)
|
||||
|
||||
|
||||
class INativeString(ITextString):
|
||||
"""
|
||||
Interface for native strings.
|
||||
|
||||
On all Python versions, this is :class:`str`. Tt extends
|
||||
:class:`ITextString`.
|
||||
"""
|
||||
|
||||
|
||||
# We're not extending ABCInterface so extra_classes won't work
|
||||
classImplements(str, INativeString)
|
||||
|
||||
|
||||
class IBool(numbers.IIntegral):
|
||||
"""
|
||||
Interface for :class:`bool`
|
||||
"""
|
||||
extra_classes = (bool,)
|
||||
|
||||
|
||||
class IDict(collections.IMutableMapping):
|
||||
"""
|
||||
Interface for :class:`dict`
|
||||
"""
|
||||
extra_classes = (dict,)
|
||||
|
||||
|
||||
class IFile(io.IIOBase):
|
||||
"""
|
||||
Interface for :class:`file`.
|
||||
|
||||
It is recommended to use the interfaces from
|
||||
:mod:`zope.interface.common.io` instead of this interface.
|
||||
|
||||
On Python 3, there is no single implementation of this interface;
|
||||
depending on the arguments, the :func:`open` builtin can return
|
||||
many different classes that implement different interfaces from
|
||||
:mod:`zope.interface.common.io`.
|
||||
"""
|
||||
extra_classes = ()
|
||||
@@ -0,0 +1,267 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
"""
|
||||
Interface definitions paralleling the abstract base classes defined in
|
||||
:mod:`collections.abc`.
|
||||
|
||||
After this module is imported, the standard library types will declare that
|
||||
they implement the appropriate interface. While most standard library types
|
||||
will properly implement that interface (that is, ``verifyObject(ISequence,
|
||||
list()))`` will pass, for example), a few might not:
|
||||
|
||||
- `memoryview` doesn't feature all the defined methods of
|
||||
``ISequence`` such as ``count``; it is still declared to provide
|
||||
``ISequence`` though.
|
||||
|
||||
- `collections.deque.pop` doesn't accept the ``index`` argument of
|
||||
`collections.abc.MutableSequence.pop`
|
||||
|
||||
- `range.index` does not accept the ``start`` and ``stop`` arguments.
|
||||
|
||||
.. versionadded:: 5.0.0
|
||||
"""
|
||||
|
||||
import sys
|
||||
from abc import ABCMeta
|
||||
from collections import OrderedDict
|
||||
from collections import UserDict
|
||||
from collections import UserList
|
||||
from collections import UserString
|
||||
from collections import abc
|
||||
|
||||
from zope.interface.common import ABCInterface
|
||||
from zope.interface.common import optional
|
||||
|
||||
|
||||
# pylint:disable=inherit-non-class,
|
||||
# pylint:disable=no-self-argument,no-method-argument
|
||||
# pylint:disable=unexpected-special-method-signature
|
||||
# pylint:disable=no-value-for-parameter
|
||||
|
||||
|
||||
def _new_in_ver(name, ver,
|
||||
bases_if_missing=(ABCMeta,),
|
||||
register_if_missing=()):
|
||||
if ver:
|
||||
return getattr(abc, name)
|
||||
|
||||
# TODO: It's a shame to have to repeat the bases when
|
||||
# the ABC is missing. Can we DRY that?
|
||||
missing = ABCMeta(name, bases_if_missing, {
|
||||
'__doc__': "The ABC %s is not defined in this version of Python." % (
|
||||
name
|
||||
),
|
||||
})
|
||||
|
||||
for c in register_if_missing:
|
||||
missing.register(c)
|
||||
|
||||
return missing
|
||||
|
||||
|
||||
__all__ = [
|
||||
'IAsyncGenerator',
|
||||
'IAsyncIterable',
|
||||
'IAsyncIterator',
|
||||
'IAwaitable',
|
||||
'ICollection',
|
||||
'IContainer',
|
||||
'ICoroutine',
|
||||
'IGenerator',
|
||||
'IHashable',
|
||||
'IItemsView',
|
||||
'IIterable',
|
||||
'IIterator',
|
||||
'IKeysView',
|
||||
'IMapping',
|
||||
'IMappingView',
|
||||
'IMutableMapping',
|
||||
'IMutableSequence',
|
||||
'IMutableSet',
|
||||
'IReversible',
|
||||
'ISequence',
|
||||
'ISet',
|
||||
'ISized',
|
||||
'IValuesView',
|
||||
]
|
||||
|
||||
|
||||
class IContainer(ABCInterface):
|
||||
abc = abc.Container
|
||||
|
||||
@optional
|
||||
def __contains__(other):
|
||||
"""
|
||||
Optional method. If not provided, the interpreter will use
|
||||
``__iter__`` or the old ``__getitem__`` protocol
|
||||
to implement ``in``.
|
||||
"""
|
||||
|
||||
|
||||
class IHashable(ABCInterface):
|
||||
abc = abc.Hashable
|
||||
|
||||
|
||||
class IIterable(ABCInterface):
|
||||
abc = abc.Iterable
|
||||
|
||||
@optional
|
||||
def __iter__():
|
||||
"""
|
||||
Optional method. If not provided, the interpreter will
|
||||
implement `iter` using the old ``__getitem__`` protocol.
|
||||
"""
|
||||
|
||||
|
||||
class IIterator(IIterable):
|
||||
abc = abc.Iterator
|
||||
|
||||
|
||||
class IReversible(IIterable):
|
||||
abc = _new_in_ver('Reversible', True, (IIterable.getABC(),))
|
||||
|
||||
@optional
|
||||
def __reversed__():
|
||||
"""
|
||||
Optional method. If this isn't present, the interpreter
|
||||
will use ``__len__`` and ``__getitem__`` to implement the
|
||||
`reversed` builtin.
|
||||
"""
|
||||
|
||||
|
||||
class IGenerator(IIterator):
|
||||
# New in Python 3.5
|
||||
abc = _new_in_ver('Generator', True, (IIterator.getABC(),))
|
||||
|
||||
|
||||
class ISized(ABCInterface):
|
||||
abc = abc.Sized
|
||||
|
||||
|
||||
# ICallable is not defined because there's no standard signature.
|
||||
|
||||
|
||||
class ICollection(ISized,
|
||||
IIterable,
|
||||
IContainer):
|
||||
abc = _new_in_ver(
|
||||
'Collection',
|
||||
True,
|
||||
(ISized.getABC(), IIterable.getABC(), IContainer.getABC())
|
||||
)
|
||||
|
||||
|
||||
class ISequence(IReversible,
|
||||
ICollection):
|
||||
abc = abc.Sequence
|
||||
extra_classes = (UserString,)
|
||||
# On Python 2, basestring was registered as an ISequence, and
|
||||
# its subclass str is an IByteString. If we also register str as
|
||||
# an ISequence, that tends to lead to inconsistent resolution order.
|
||||
ignored_classes = ()
|
||||
|
||||
@optional
|
||||
def __reversed__():
|
||||
"""
|
||||
Optional method. If this isn't present, the interpreter
|
||||
will use ``__len__`` and ``__getitem__`` to implement the
|
||||
`reversed` builtin.
|
||||
"""
|
||||
|
||||
@optional
|
||||
def __iter__():
|
||||
"""
|
||||
Optional method. If not provided, the interpreter will
|
||||
implement `iter` using the old ``__getitem__`` protocol.
|
||||
"""
|
||||
|
||||
|
||||
class IMutableSequence(ISequence):
|
||||
abc = abc.MutableSequence
|
||||
extra_classes = (UserList,)
|
||||
|
||||
|
||||
class IByteString(ISequence):
|
||||
"""
|
||||
This unifies `bytes` and `bytearray`.
|
||||
"""
|
||||
abc = _new_in_ver(
|
||||
'ByteString', True, (ISequence.getABC(),), (bytes, bytearray),
|
||||
)
|
||||
|
||||
|
||||
class ISet(ICollection):
|
||||
abc = abc.Set
|
||||
|
||||
|
||||
class IMutableSet(ISet):
|
||||
abc = abc.MutableSet
|
||||
|
||||
|
||||
class IMapping(ICollection):
|
||||
abc = abc.Mapping
|
||||
extra_classes = (dict,)
|
||||
# OrderedDict is a subclass of dict. On CPython 2,
|
||||
# it winds up registered as a IMutableMapping, which
|
||||
# produces an inconsistent IRO if we also try to register it
|
||||
# here.
|
||||
ignored_classes = (OrderedDict,)
|
||||
|
||||
|
||||
class IMutableMapping(IMapping):
|
||||
abc = abc.MutableMapping
|
||||
extra_classes = (dict, UserDict,)
|
||||
ignored_classes = (OrderedDict,)
|
||||
|
||||
|
||||
class IMappingView(ISized):
|
||||
abc = abc.MappingView
|
||||
|
||||
|
||||
class IItemsView(IMappingView, ISet):
|
||||
abc = abc.ItemsView
|
||||
|
||||
|
||||
class IKeysView(IMappingView, ISet):
|
||||
abc = abc.KeysView
|
||||
|
||||
|
||||
class IValuesView(IMappingView, ICollection):
|
||||
abc = abc.ValuesView
|
||||
|
||||
@optional
|
||||
def __contains__(other):
|
||||
"""
|
||||
Optional method. If not provided, the interpreter will use
|
||||
``__iter__`` or the old ``__len__`` and ``__getitem__`` protocol
|
||||
to implement ``in``.
|
||||
"""
|
||||
|
||||
|
||||
class IAwaitable(ABCInterface):
|
||||
abc = _new_in_ver('Awaitable', True)
|
||||
|
||||
|
||||
class ICoroutine(IAwaitable):
|
||||
abc = _new_in_ver('Coroutine', True)
|
||||
|
||||
|
||||
class IAsyncIterable(ABCInterface):
|
||||
abc = _new_in_ver('AsyncIterable', True)
|
||||
|
||||
|
||||
class IAsyncIterator(IAsyncIterable):
|
||||
abc = _new_in_ver('AsyncIterator', True)
|
||||
|
||||
|
||||
class IAsyncGenerator(IAsyncIterator):
|
||||
abc = _new_in_ver('AsyncGenerator', True)
|
||||
@@ -0,0 +1,622 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
"""Datetime interfaces.
|
||||
|
||||
This module is called idatetime because if it were called datetime the import
|
||||
of the real datetime would fail.
|
||||
"""
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
from datetime import time
|
||||
from datetime import timedelta
|
||||
from datetime import tzinfo
|
||||
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
from zope.interface import classImplements
|
||||
|
||||
|
||||
class ITimeDeltaClass(Interface):
|
||||
"""This is the timedelta class interface.
|
||||
|
||||
This is symbolic; this module does **not** make
|
||||
`datetime.timedelta` provide this interface.
|
||||
"""
|
||||
|
||||
min = Attribute("The most negative timedelta object")
|
||||
|
||||
max = Attribute("The most positive timedelta object")
|
||||
|
||||
resolution = Attribute(
|
||||
"The smallest difference between non-equal timedelta objects")
|
||||
|
||||
|
||||
class ITimeDelta(ITimeDeltaClass):
|
||||
"""Represent the difference between two datetime objects.
|
||||
|
||||
Implemented by `datetime.timedelta`.
|
||||
|
||||
Supported operators:
|
||||
|
||||
- add, subtract timedelta
|
||||
- unary plus, minus, abs
|
||||
- compare to timedelta
|
||||
- multiply, divide by int/long
|
||||
|
||||
In addition, `.datetime` supports subtraction of two `.datetime` objects
|
||||
returning a `.timedelta`, and addition or subtraction of a `.datetime`
|
||||
and a `.timedelta` giving a `.datetime`.
|
||||
|
||||
Representation: (days, seconds, microseconds).
|
||||
"""
|
||||
|
||||
days = Attribute("Days between -999999999 and 999999999 inclusive")
|
||||
|
||||
seconds = Attribute("Seconds between 0 and 86399 inclusive")
|
||||
|
||||
microseconds = Attribute("Microseconds between 0 and 999999 inclusive")
|
||||
|
||||
|
||||
class IDateClass(Interface):
|
||||
"""This is the date class interface.
|
||||
|
||||
This is symbolic; this module does **not** make
|
||||
`datetime.date` provide this interface.
|
||||
"""
|
||||
|
||||
min = Attribute("The earliest representable date")
|
||||
|
||||
max = Attribute("The latest representable date")
|
||||
|
||||
resolution = Attribute(
|
||||
"The smallest difference between non-equal date objects")
|
||||
|
||||
def today():
|
||||
"""Return the current local time.
|
||||
|
||||
This is equivalent to ``date.fromtimestamp(time.time())``"""
|
||||
|
||||
def fromtimestamp(timestamp):
|
||||
"""Return the local date from a POSIX timestamp (like time.time())
|
||||
|
||||
This may raise `ValueError`, if the timestamp is out of the range of
|
||||
values supported by the platform C ``localtime()`` function. It's
|
||||
common for this to be restricted to years from 1970 through 2038. Note
|
||||
that on non-POSIX systems that include leap seconds in their notion of
|
||||
a timestamp, leap seconds are ignored by `fromtimestamp`.
|
||||
"""
|
||||
|
||||
def fromordinal(ordinal):
|
||||
"""Return the date corresponding to the proleptic Gregorian ordinal.
|
||||
|
||||
January 1 of year 1 has ordinal 1. `ValueError` is raised unless
|
||||
1 <= ordinal <= date.max.toordinal().
|
||||
|
||||
For any date *d*, ``date.fromordinal(d.toordinal()) == d``.
|
||||
"""
|
||||
|
||||
|
||||
class IDate(IDateClass):
|
||||
"""Represents a date (year, month and day) in an idealized calendar.
|
||||
|
||||
Implemented by `datetime.date`.
|
||||
|
||||
Operators:
|
||||
|
||||
__repr__, __str__
|
||||
__cmp__, __hash__
|
||||
__add__, __radd__, __sub__ (add/radd only with timedelta arg)
|
||||
"""
|
||||
|
||||
year = Attribute("Between MINYEAR and MAXYEAR inclusive.")
|
||||
|
||||
month = Attribute("Between 1 and 12 inclusive")
|
||||
|
||||
day = Attribute(
|
||||
"Between 1 and the number of days "
|
||||
"in the given month of the given year."
|
||||
)
|
||||
|
||||
def replace(year, month, day):
|
||||
"""Return a date with the same value.
|
||||
|
||||
Except for those members given new values by whichever keyword
|
||||
arguments are specified.
|
||||
|
||||
For example, if ``d == date(2002, 12, 31)``, then
|
||||
``d.replace(day=26) == date(2000, 12, 26)``.
|
||||
"""
|
||||
|
||||
def timetuple():
|
||||
"""Return a 9-element tuple of the form returned by `time.localtime`.
|
||||
|
||||
The hours, minutes and seconds are 0, and the DST flag is -1.
|
||||
``d.timetuple()`` is equivalent to
|
||||
``(d.year, d.month, d.day, 0, 0, 0, d.weekday(), d.toordinal() -
|
||||
date(d.year, 1, 1).toordinal() + 1, -1)``
|
||||
"""
|
||||
|
||||
def toordinal():
|
||||
"""Return the proleptic Gregorian ordinal of the date
|
||||
|
||||
January 1 of year 1 has ordinal 1. For any date object *d*,
|
||||
``date.fromordinal(d.toordinal()) == d``.
|
||||
"""
|
||||
|
||||
def weekday():
|
||||
"""Return the day of the week as an integer.
|
||||
|
||||
Monday is 0 and Sunday is 6. For example,
|
||||
``date(2002, 12, 4).weekday() == 2``, a Wednesday.
|
||||
|
||||
.. seealso:: `isoweekday`.
|
||||
"""
|
||||
|
||||
def isoweekday():
|
||||
"""Return the day of the week as an integer.
|
||||
|
||||
Monday is 1 and Sunday is 7. For example,
|
||||
date(2002, 12, 4).isoweekday() == 3, a Wednesday.
|
||||
|
||||
.. seealso:: `weekday`, `isocalendar`.
|
||||
"""
|
||||
|
||||
def isocalendar():
|
||||
"""Return a 3-tuple, (ISO year, ISO week number, ISO weekday).
|
||||
|
||||
The ISO calendar is a widely used variant of the Gregorian calendar.
|
||||
See http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm for a good
|
||||
explanation.
|
||||
|
||||
The ISO year consists of 52 or 53 full weeks, and where a week starts
|
||||
on a Monday and ends on a Sunday. The first week of an ISO year is the
|
||||
first (Gregorian) calendar week of a year containing a Thursday. This
|
||||
is called week number 1, and the ISO year of that Thursday is the same
|
||||
as its Gregorian year.
|
||||
|
||||
For example, 2004 begins on a Thursday, so the first week of ISO year
|
||||
2004 begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan 2004, so
|
||||
that ``date(2003, 12, 29).isocalendar() == (2004, 1, 1)`` and
|
||||
``date(2004, 1, 4).isocalendar() == (2004, 1, 7)``.
|
||||
"""
|
||||
|
||||
def isoformat():
|
||||
"""Return a string representing the date in ISO 8601 format.
|
||||
|
||||
This is 'YYYY-MM-DD'.
|
||||
For example, ``date(2002, 12, 4).isoformat() == '2002-12-04'``.
|
||||
"""
|
||||
|
||||
def __str__():
|
||||
"""For a date *d*, ``str(d)`` is equivalent to ``d.isoformat()``."""
|
||||
|
||||
def ctime():
|
||||
"""Return a string representing the date.
|
||||
|
||||
For example date(2002, 12, 4).ctime() == 'Wed Dec 4 00:00:00 2002'.
|
||||
d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple()))
|
||||
on platforms where the native C ctime() function
|
||||
(which `time.ctime` invokes, but which date.ctime() does not invoke)
|
||||
conforms to the C standard.
|
||||
"""
|
||||
|
||||
def strftime(format):
|
||||
"""Return a string representing the date.
|
||||
|
||||
Controlled by an explicit format string. Format codes referring to
|
||||
hours, minutes or seconds will see 0 values.
|
||||
"""
|
||||
|
||||
|
||||
class IDateTimeClass(Interface):
|
||||
"""This is the datetime class interface.
|
||||
|
||||
This is symbolic; this module does **not** make
|
||||
`datetime.datetime` provide this interface.
|
||||
"""
|
||||
|
||||
min = Attribute("The earliest representable datetime")
|
||||
|
||||
max = Attribute("The latest representable datetime")
|
||||
|
||||
resolution = Attribute(
|
||||
"The smallest possible difference between non-equal datetime objects")
|
||||
|
||||
def today():
|
||||
"""Return the current local datetime, with tzinfo None.
|
||||
|
||||
This is equivalent to ``datetime.fromtimestamp(time.time())``.
|
||||
|
||||
.. seealso:: `now`, `fromtimestamp`.
|
||||
"""
|
||||
|
||||
def now(tz=None):
|
||||
"""Return the current local date and time.
|
||||
|
||||
If optional argument *tz* is None or not specified, this is like
|
||||
`today`, but, if possible, supplies more precision than can be gotten
|
||||
from going through a `time.time` timestamp (for example, this may be
|
||||
possible on platforms supplying the C ``gettimeofday()`` function).
|
||||
|
||||
Else tz must be an instance of a class tzinfo subclass, and the current
|
||||
date and time are converted to tz's time zone. In this case the result
|
||||
is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz)).
|
||||
|
||||
.. seealso:: `today`, `utcnow`.
|
||||
"""
|
||||
|
||||
def utcnow():
|
||||
"""Return the current UTC date and time, with tzinfo None.
|
||||
|
||||
This is like `now`, but returns the current UTC date and time, as a
|
||||
naive datetime object.
|
||||
|
||||
.. seealso:: `now`.
|
||||
"""
|
||||
|
||||
def fromtimestamp(timestamp, tz=None):
|
||||
"""Return the local date and time corresponding to the POSIX timestamp.
|
||||
|
||||
Same as is returned by time.time(). If optional argument tz is None or
|
||||
not specified, the timestamp is converted to the platform's local date
|
||||
and time, and the returned datetime object is naive.
|
||||
|
||||
Else tz must be an instance of a class tzinfo subclass, and the
|
||||
timestamp is converted to tz's time zone. In this case the result is
|
||||
equivalent to
|
||||
``tz.fromutc(datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz))``
|
||||
|
||||
fromtimestamp() may raise `ValueError`, if the timestamp is out of the
|
||||
range of values supported by the platform C localtime() or gmtime()
|
||||
functions. It's common for this to be restricted to years in 1970
|
||||
through 2038. Note that on non-POSIX systems that include leap seconds
|
||||
in their notion of a timestamp, leap seconds are ignored by
|
||||
fromtimestamp(), and then it's possible to have two timestamps
|
||||
differing by a second that yield identical datetime objects.
|
||||
|
||||
.. seealso:: `utcfromtimestamp`.
|
||||
"""
|
||||
|
||||
def utcfromtimestamp(timestamp):
|
||||
"""Return the UTC datetime from the POSIX timestamp with tzinfo None.
|
||||
|
||||
This may raise `ValueError`, if the timestamp is out of the range of
|
||||
values supported by the platform C ``gmtime()`` function. It's common
|
||||
for this to be restricted to years in 1970 through 2038.
|
||||
|
||||
.. seealso:: `fromtimestamp`.
|
||||
"""
|
||||
|
||||
def fromordinal(ordinal):
|
||||
"""Return the datetime from the proleptic Gregorian ordinal.
|
||||
|
||||
January 1 of year 1 has ordinal 1. `ValueError` is raised unless
|
||||
1 <= ordinal <= datetime.max.toordinal().
|
||||
The hour, minute, second and microsecond of the result are all 0, and
|
||||
tzinfo is None.
|
||||
"""
|
||||
|
||||
def combine(date, time):
|
||||
"""Return a new datetime object.
|
||||
|
||||
Its date members are equal to the given date object's, and whose time
|
||||
and tzinfo members are equal to the given time object's. For any
|
||||
datetime object *d*, ``d == datetime.combine(d.date(), d.timetz())``.
|
||||
If date is a datetime object, its time and tzinfo members are ignored.
|
||||
"""
|
||||
|
||||
|
||||
class IDateTime(IDate, IDateTimeClass):
|
||||
"""Contains all the information from a date object and a time object.
|
||||
|
||||
Implemented by `datetime.datetime`.
|
||||
"""
|
||||
|
||||
year = Attribute("Year between MINYEAR and MAXYEAR inclusive")
|
||||
|
||||
month = Attribute("Month between 1 and 12 inclusive")
|
||||
|
||||
day = Attribute(
|
||||
"Day between 1 and the number of days in the given month of the year")
|
||||
|
||||
hour = Attribute("Hour in range(24)")
|
||||
|
||||
minute = Attribute("Minute in range(60)")
|
||||
|
||||
second = Attribute("Second in range(60)")
|
||||
|
||||
microsecond = Attribute("Microsecond in range(1000000)")
|
||||
|
||||
tzinfo = Attribute(
|
||||
"""The object passed as the tzinfo argument to the datetime constructor
|
||||
or None if none was passed""")
|
||||
|
||||
def date():
|
||||
"""Return date object with same year, month and day."""
|
||||
|
||||
def time():
|
||||
"""Return time object with same hour, minute, second, microsecond.
|
||||
|
||||
tzinfo is None.
|
||||
|
||||
.. seealso:: Method :meth:`timetz`.
|
||||
"""
|
||||
|
||||
def timetz():
|
||||
"""Return time object with same hour, minute, second, microsecond,
|
||||
and tzinfo.
|
||||
|
||||
.. seealso:: Method :meth:`time`.
|
||||
"""
|
||||
|
||||
def replace(year, month, day, hour, minute, second, microsecond, tzinfo):
|
||||
"""Return a datetime with the same members, except for those members
|
||||
given new values by whichever keyword arguments are specified.
|
||||
|
||||
Note that ``tzinfo=None`` can be specified to create a naive datetime
|
||||
from an aware datetime with no conversion of date and time members.
|
||||
"""
|
||||
|
||||
def astimezone(tz):
|
||||
"""Return a datetime object with new tzinfo member tz, adjusting the
|
||||
date and time members so the result is the same UTC time as self, but
|
||||
in tz's local time.
|
||||
|
||||
tz must be an instance of a tzinfo subclass, and its utcoffset() and
|
||||
dst() methods must not return None. self must be aware (self.tzinfo
|
||||
must not be None, and self.utcoffset() must not return None).
|
||||
|
||||
If self.tzinfo is tz, self.astimezone(tz) is equal to self: no
|
||||
adjustment of date or time members is performed. Else the result is
|
||||
local time in time zone tz, representing the same UTC time as self:
|
||||
|
||||
after astz = dt.astimezone(tz), astz - astz.utcoffset()
|
||||
|
||||
will usually have the same date and time members as dt -
|
||||
dt.utcoffset(). The discussion of class `datetime.tzinfo` explains
|
||||
the cases at Daylight Saving Time transition boundaries where this
|
||||
cannot be achieved (an issue only if tz models both standard and
|
||||
daylight time).
|
||||
|
||||
If you merely want to attach a time zone object *tz* to a datetime
|
||||
*dt* without adjustment of date and time members, use
|
||||
``dt.replace(tzinfo=tz)``.
|
||||
|
||||
If you merely want to remove the time zone object from an aware
|
||||
datetime dt without conversion of date and time members, use
|
||||
``dt.replace(tzinfo=None)``.
|
||||
|
||||
Note that the default `tzinfo.fromutc` method can be overridden in a
|
||||
tzinfo subclass to effect the result returned by `astimezone`.
|
||||
"""
|
||||
|
||||
def utcoffset():
|
||||
"""Return the timezone offset in minutes east of UTC (negative west of
|
||||
UTC)."""
|
||||
|
||||
def dst():
|
||||
"""Return 0 if DST is not in effect, or the DST offset (in minutes
|
||||
eastward) if DST is in effect.
|
||||
"""
|
||||
|
||||
def tzname():
|
||||
"""Return the timezone name."""
|
||||
|
||||
def timetuple():
|
||||
"""Return a 9-tuple of the form returned by `time.localtime`."""
|
||||
|
||||
def utctimetuple():
|
||||
"""Return UTC time tuple compatilble with `time.gmtime`."""
|
||||
|
||||
def toordinal():
|
||||
"""Return the proleptic Gregorian ordinal of the date.
|
||||
|
||||
The same as self.date().toordinal().
|
||||
"""
|
||||
|
||||
def weekday():
|
||||
"""Return the day of the week as an integer.
|
||||
|
||||
Monday is 0 and Sunday is 6. The same as self.date().weekday().
|
||||
See also isoweekday().
|
||||
"""
|
||||
|
||||
def isoweekday():
|
||||
"""Return the day of the week as an integer.
|
||||
|
||||
Monday is 1 and Sunday is 7. The same as self.date().isoweekday.
|
||||
|
||||
.. seealso:: `weekday`, `isocalendar`.
|
||||
"""
|
||||
|
||||
def isocalendar():
|
||||
"""Return a 3-tuple, (ISO year, ISO week number, ISO weekday).
|
||||
|
||||
The same as self.date().isocalendar().
|
||||
"""
|
||||
|
||||
def isoformat(sep='T'):
|
||||
"""Return a string representing the date and time in ISO 8601 format.
|
||||
|
||||
YYYY-MM-DDTHH:MM:SS.mmmmmm or YYYY-MM-DDTHH:MM:SS if microsecond is 0
|
||||
|
||||
If `utcoffset` does not return None, a 6-character string is appended,
|
||||
giving the UTC offset in (signed) hours and minutes:
|
||||
|
||||
YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or YYYY-MM-DDTHH:MM:SS+HH:MM
|
||||
if microsecond is 0.
|
||||
|
||||
The optional argument sep (default 'T') is a one-character separator,
|
||||
placed between the date and time portions of the result.
|
||||
"""
|
||||
|
||||
def __str__():
|
||||
"""Convert to a stirng
|
||||
|
||||
For a datetime instance *d*, ``str(d)`` is equivalent to
|
||||
``d.isoformat(' ')``.
|
||||
"""
|
||||
|
||||
def ctime():
|
||||
"""Return a string representing the date and time.
|
||||
|
||||
``datetime(2002, 12, 4, 20, 30, 40).ctime()`` yields
|
||||
``'Wed Dec 4 20:30:40 2002'``.
|
||||
``d.ctime()`` is equivalent to
|
||||
``time.ctime(time.mktime(d.timetuple()))`` on platforms where the
|
||||
native C ``ctime()`` function (which `time.ctime` invokes, but which
|
||||
`datetime.ctime` does not invoke) conforms to the C standard.
|
||||
"""
|
||||
|
||||
def strftime(format):
|
||||
"""Return a string representing the date and time.
|
||||
|
||||
This is controlled by an explicit format string.
|
||||
"""
|
||||
|
||||
|
||||
class ITimeClass(Interface):
|
||||
"""This is the time class interface.
|
||||
|
||||
This is symbolic; this module does **not** make
|
||||
`datetime.time` provide this interface.
|
||||
|
||||
"""
|
||||
|
||||
min = Attribute("The earliest representable time")
|
||||
|
||||
max = Attribute("The latest representable time")
|
||||
|
||||
resolution = Attribute(
|
||||
"The smallest possible difference between non-equal time objects")
|
||||
|
||||
|
||||
class ITime(ITimeClass):
|
||||
"""Represent time with time zone.
|
||||
|
||||
Implemented by `datetime.time`.
|
||||
|
||||
Operators:
|
||||
|
||||
__repr__, __str__
|
||||
__cmp__, __hash__
|
||||
"""
|
||||
|
||||
hour = Attribute("Hour in range(24)")
|
||||
|
||||
minute = Attribute("Minute in range(60)")
|
||||
|
||||
second = Attribute("Second in range(60)")
|
||||
|
||||
microsecond = Attribute("Microsecond in range(1000000)")
|
||||
|
||||
tzinfo = Attribute(
|
||||
"""The object passed as the tzinfo argument to the time constructor
|
||||
or None if none was passed.""")
|
||||
|
||||
def replace(hour, minute, second, microsecond, tzinfo):
|
||||
"""Return a time with the same value.
|
||||
|
||||
Except for those members given new values by whichever keyword
|
||||
arguments are specified. Note that tzinfo=None can be specified
|
||||
to create a naive time from an aware time, without conversion of the
|
||||
time members.
|
||||
"""
|
||||
|
||||
def isoformat():
|
||||
"""Return a string representing the time in ISO 8601 format.
|
||||
|
||||
That is HH:MM:SS.mmmmmm or, if self.microsecond is 0, HH:MM:SS
|
||||
If utcoffset() does not return None, a 6-character string is appended,
|
||||
giving the UTC offset in (signed) hours and minutes:
|
||||
HH:MM:SS.mmmmmm+HH:MM or, if self.microsecond is 0, HH:MM:SS+HH:MM
|
||||
"""
|
||||
|
||||
def __str__():
|
||||
"""For a time t, str(t) is equivalent to t.isoformat()."""
|
||||
|
||||
def strftime(format):
|
||||
"""Return a string representing the time.
|
||||
|
||||
This is controlled by an explicit format string.
|
||||
"""
|
||||
|
||||
def utcoffset():
|
||||
"""Return the timezone offset in minutes east of UTC (negative west of
|
||||
UTC).
|
||||
|
||||
If tzinfo is None, returns None, else returns
|
||||
self.tzinfo.utcoffset(None), and raises an exception if the latter
|
||||
doesn't return None or a timedelta object representing a whole number
|
||||
of minutes with magnitude less than one day.
|
||||
"""
|
||||
|
||||
def dst():
|
||||
"""Return 0 if DST is not in effect, or the DST offset (in minutes
|
||||
eastward) if DST is in effect.
|
||||
|
||||
If tzinfo is None, returns None, else returns self.tzinfo.dst(None),
|
||||
and raises an exception if the latter doesn't return None, or a
|
||||
timedelta object representing a whole number of minutes with
|
||||
magnitude less than one day.
|
||||
"""
|
||||
|
||||
def tzname():
|
||||
"""Return the timezone name.
|
||||
|
||||
If tzinfo is None, returns None, else returns self.tzinfo.tzname(None),
|
||||
or raises an exception if the latter doesn't return None or a string
|
||||
object.
|
||||
"""
|
||||
|
||||
|
||||
class ITZInfo(Interface):
|
||||
"""Time zone info class.
|
||||
"""
|
||||
|
||||
def utcoffset(dt):
|
||||
"""Return offset of local time from UTC, in minutes east of UTC.
|
||||
|
||||
If local time is west of UTC, this should be negative.
|
||||
Note that this is intended to be the total offset from UTC;
|
||||
for example, if a tzinfo object represents both time zone and DST
|
||||
adjustments, utcoffset() should return their sum. If the UTC offset
|
||||
isn't known, return None. Else the value returned must be a timedelta
|
||||
object specifying a whole number of minutes in the range -1439 to 1439
|
||||
inclusive (1440 = 24*60; the magnitude of the offset must be less
|
||||
than one day).
|
||||
"""
|
||||
|
||||
def dst(dt):
|
||||
"""Return the daylight saving time (DST) adjustment, in minutes east
|
||||
of UTC, or None if DST information isn't known.
|
||||
"""
|
||||
|
||||
def tzname(dt):
|
||||
"""Return the time zone name corresponding to the datetime object as
|
||||
a string.
|
||||
"""
|
||||
|
||||
def fromutc(dt):
|
||||
"""Return an equivalent datetime in self's local time."""
|
||||
|
||||
|
||||
classImplements(timedelta, ITimeDelta)
|
||||
classImplements(date, IDate)
|
||||
classImplements(datetime, IDateTime)
|
||||
classImplements(time, ITime)
|
||||
classImplements(tzinfo, ITZInfo)
|
||||
|
||||
# directlyProvides(timedelta, ITimeDeltaClass)
|
||||
# directlyProvides(date, IDateClass)
|
||||
# directlyProvides(datetime, IDateTimeClass)
|
||||
# directlyProvides(time, ITimeClass)
|
||||
@@ -0,0 +1,281 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Interfaces for standard python exceptions
|
||||
"""
|
||||
from zope.interface import Interface
|
||||
from zope.interface import classImplements
|
||||
|
||||
|
||||
class IException(Interface):
|
||||
"Interface for `Exception`"
|
||||
|
||||
|
||||
classImplements(Exception, IException) # noqa E305
|
||||
|
||||
|
||||
class IStandardError(IException):
|
||||
"Interface for `StandardError` (no longer existing.)"
|
||||
|
||||
|
||||
class IWarning(IException):
|
||||
"Interface for `Warning`"
|
||||
|
||||
|
||||
classImplements(Warning, IWarning) # noqa E305
|
||||
|
||||
|
||||
class ISyntaxError(IStandardError):
|
||||
"Interface for `SyntaxError`"
|
||||
|
||||
|
||||
classImplements(SyntaxError, ISyntaxError) # noqa E305
|
||||
|
||||
|
||||
class ILookupError(IStandardError):
|
||||
"Interface for `LookupError`"
|
||||
|
||||
|
||||
classImplements(LookupError, ILookupError) # noqa E305
|
||||
|
||||
|
||||
class IValueError(IStandardError):
|
||||
"Interface for `ValueError`"
|
||||
|
||||
|
||||
classImplements(ValueError, IValueError) # noqa E305
|
||||
|
||||
|
||||
class IRuntimeError(IStandardError):
|
||||
"Interface for `RuntimeError`"
|
||||
|
||||
|
||||
classImplements(RuntimeError, IRuntimeError) # noqa E305
|
||||
|
||||
|
||||
class IArithmeticError(IStandardError):
|
||||
"Interface for `ArithmeticError`"
|
||||
|
||||
|
||||
classImplements(ArithmeticError, IArithmeticError) # noqa E305
|
||||
|
||||
|
||||
class IAssertionError(IStandardError):
|
||||
"Interface for `AssertionError`"
|
||||
|
||||
|
||||
classImplements(AssertionError, IAssertionError) # noqa E305
|
||||
|
||||
|
||||
class IAttributeError(IStandardError):
|
||||
"Interface for `AttributeError`"
|
||||
|
||||
|
||||
classImplements(AttributeError, IAttributeError) # noqa E305
|
||||
|
||||
|
||||
class IDeprecationWarning(IWarning):
|
||||
"Interface for `DeprecationWarning`"
|
||||
|
||||
|
||||
classImplements(DeprecationWarning, IDeprecationWarning) # noqa E305
|
||||
|
||||
|
||||
class IEOFError(IStandardError):
|
||||
"Interface for `EOFError`"
|
||||
|
||||
|
||||
classImplements(EOFError, IEOFError) # noqa E305
|
||||
|
||||
|
||||
class IEnvironmentError(IStandardError):
|
||||
"Interface for `EnvironmentError`"
|
||||
|
||||
|
||||
classImplements(EnvironmentError, IEnvironmentError) # noqa E305
|
||||
|
||||
|
||||
class IFloatingPointError(IArithmeticError):
|
||||
"Interface for `FloatingPointError`"
|
||||
|
||||
|
||||
classImplements(FloatingPointError, IFloatingPointError) # noqa E305
|
||||
|
||||
|
||||
class IIOError(IEnvironmentError):
|
||||
"Interface for `IOError`"
|
||||
|
||||
|
||||
classImplements(IOError, IIOError) # noqa E305
|
||||
|
||||
|
||||
class IImportError(IStandardError):
|
||||
"Interface for `ImportError`"
|
||||
|
||||
|
||||
classImplements(ImportError, IImportError) # noqa E305
|
||||
|
||||
|
||||
class IIndentationError(ISyntaxError):
|
||||
"Interface for `IndentationError`"
|
||||
|
||||
|
||||
classImplements(IndentationError, IIndentationError) # noqa E305
|
||||
|
||||
|
||||
class IIndexError(ILookupError):
|
||||
"Interface for `IndexError`"
|
||||
|
||||
|
||||
classImplements(IndexError, IIndexError) # noqa E305
|
||||
|
||||
|
||||
class IKeyError(ILookupError):
|
||||
"Interface for `KeyError`"
|
||||
|
||||
|
||||
classImplements(KeyError, IKeyError) # noqa E305
|
||||
|
||||
|
||||
class IKeyboardInterrupt(IStandardError):
|
||||
"Interface for `KeyboardInterrupt`"
|
||||
|
||||
|
||||
classImplements(KeyboardInterrupt, IKeyboardInterrupt) # noqa E305
|
||||
|
||||
|
||||
class IMemoryError(IStandardError):
|
||||
"Interface for `MemoryError`"
|
||||
|
||||
|
||||
classImplements(MemoryError, IMemoryError) # noqa E305
|
||||
|
||||
|
||||
class INameError(IStandardError):
|
||||
"Interface for `NameError`"
|
||||
|
||||
|
||||
classImplements(NameError, INameError) # noqa E305
|
||||
|
||||
|
||||
class INotImplementedError(IRuntimeError):
|
||||
"Interface for `NotImplementedError`"
|
||||
|
||||
|
||||
classImplements(NotImplementedError, INotImplementedError) # noqa E305
|
||||
|
||||
|
||||
class IOSError(IEnvironmentError):
|
||||
"Interface for `OSError`"
|
||||
|
||||
|
||||
classImplements(OSError, IOSError) # noqa E305
|
||||
|
||||
|
||||
class IOverflowError(IArithmeticError):
|
||||
"Interface for `ArithmeticError`"
|
||||
|
||||
|
||||
classImplements(OverflowError, IOverflowError) # noqa E305
|
||||
|
||||
|
||||
class IOverflowWarning(IWarning):
|
||||
"""Deprecated, no standard class implements this.
|
||||
|
||||
This was the interface for ``OverflowWarning`` prior to Python 2.5,
|
||||
but that class was removed for all versions after that.
|
||||
"""
|
||||
|
||||
|
||||
class IReferenceError(IStandardError):
|
||||
"Interface for `ReferenceError`"
|
||||
|
||||
|
||||
classImplements(ReferenceError, IReferenceError) # noqa E305
|
||||
|
||||
|
||||
class IRuntimeWarning(IWarning):
|
||||
"Interface for `RuntimeWarning`"
|
||||
|
||||
|
||||
classImplements(RuntimeWarning, IRuntimeWarning) # noqa E305
|
||||
|
||||
|
||||
class IStopIteration(IException):
|
||||
"Interface for `StopIteration`"
|
||||
|
||||
|
||||
classImplements(StopIteration, IStopIteration) # noqa E305
|
||||
|
||||
|
||||
class ISyntaxWarning(IWarning):
|
||||
"Interface for `SyntaxWarning`"
|
||||
|
||||
|
||||
classImplements(SyntaxWarning, ISyntaxWarning) # noqa E305
|
||||
|
||||
|
||||
class ISystemError(IStandardError):
|
||||
"Interface for `SystemError`"
|
||||
|
||||
|
||||
classImplements(SystemError, ISystemError) # noqa E305
|
||||
|
||||
|
||||
class ISystemExit(IException):
|
||||
"Interface for `SystemExit`"
|
||||
|
||||
|
||||
classImplements(SystemExit, ISystemExit) # noqa E305
|
||||
|
||||
|
||||
class ITabError(IIndentationError):
|
||||
"Interface for `TabError`"
|
||||
|
||||
|
||||
classImplements(TabError, ITabError) # noqa E305
|
||||
|
||||
|
||||
class ITypeError(IStandardError):
|
||||
"Interface for `TypeError`"
|
||||
|
||||
|
||||
classImplements(TypeError, ITypeError) # noqa E305
|
||||
|
||||
|
||||
class IUnboundLocalError(INameError):
|
||||
"Interface for `UnboundLocalError`"
|
||||
|
||||
|
||||
classImplements(UnboundLocalError, IUnboundLocalError) # noqa E305
|
||||
|
||||
|
||||
class IUnicodeError(IValueError):
|
||||
"Interface for `UnicodeError`"
|
||||
|
||||
|
||||
classImplements(UnicodeError, IUnicodeError) # noqa E305
|
||||
|
||||
|
||||
class IUserWarning(IWarning):
|
||||
"Interface for `UserWarning`"
|
||||
|
||||
|
||||
classImplements(UserWarning, IUserWarning) # noqa E305
|
||||
|
||||
|
||||
class IZeroDivisionError(IArithmeticError):
|
||||
"Interface for `ZeroDivisionError`"
|
||||
|
||||
|
||||
classImplements(ZeroDivisionError, IZeroDivisionError) # noqa E305
|
||||
@@ -0,0 +1,44 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
"""
|
||||
Interface definitions paralleling the abstract base classes defined in
|
||||
:mod:`io`.
|
||||
|
||||
After this module is imported, the standard library types will declare
|
||||
that they implement the appropriate interface.
|
||||
|
||||
.. versionadded:: 5.0.0
|
||||
"""
|
||||
|
||||
import io as abc
|
||||
|
||||
from zope.interface.common import ABCInterface
|
||||
|
||||
|
||||
# pylint:disable=inherit-non-class,
|
||||
# pylint:disable=no-member
|
||||
|
||||
class IIOBase(ABCInterface):
|
||||
abc = abc.IOBase
|
||||
|
||||
|
||||
class IRawIOBase(IIOBase):
|
||||
abc = abc.RawIOBase
|
||||
|
||||
|
||||
class IBufferedIOBase(IIOBase):
|
||||
abc = abc.BufferedIOBase
|
||||
extra_classes = ()
|
||||
|
||||
|
||||
class ITextIOBase(IIOBase):
|
||||
abc = abc.TextIOBase
|
||||
@@ -0,0 +1,177 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""
|
||||
Mapping Interfaces.
|
||||
|
||||
Importing this module does *not* mark any standard classes as
|
||||
implementing any of these interfaces.
|
||||
|
||||
While this module is not deprecated, new code should generally use
|
||||
:mod:`zope.interface.common.collections`, specifically
|
||||
:class:`~zope.interface.common.collections.IMapping` and
|
||||
:class:`~zope.interface.common.collections.IMutableMapping`. This
|
||||
module is occasionally useful for its extremely fine grained breakdown
|
||||
of interfaces.
|
||||
|
||||
The standard library :class:`dict` and :class:`collections.UserDict`
|
||||
implement ``IMutableMapping``, but *do not* implement any of the
|
||||
interfaces in this module.
|
||||
"""
|
||||
from zope.interface import Interface
|
||||
from zope.interface.common import collections
|
||||
|
||||
|
||||
class IItemMapping(Interface):
|
||||
"""Simplest readable mapping object
|
||||
"""
|
||||
|
||||
def __getitem__(key):
|
||||
"""Get a value for a key
|
||||
|
||||
A `KeyError` is raised if there is no value for the key.
|
||||
"""
|
||||
|
||||
|
||||
class IReadMapping(collections.IContainer, IItemMapping):
|
||||
"""
|
||||
Basic mapping interface.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``IContainer``
|
||||
"""
|
||||
|
||||
def get(key, default=None):
|
||||
"""Get a value for a key
|
||||
|
||||
The default is returned if there is no value for the key.
|
||||
"""
|
||||
|
||||
def __contains__(key):
|
||||
"""Tell if a key exists in the mapping."""
|
||||
# Optional in IContainer, required by this interface.
|
||||
|
||||
|
||||
class IWriteMapping(Interface):
|
||||
"""Mapping methods for changing data"""
|
||||
|
||||
def __delitem__(key):
|
||||
"""Delete a value from the mapping using the key."""
|
||||
|
||||
def __setitem__(key, value):
|
||||
"""Set a new item in the mapping."""
|
||||
|
||||
|
||||
class IEnumerableMapping(collections.ISized, IReadMapping):
|
||||
"""
|
||||
Mapping objects whose items can be enumerated.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``ISized``
|
||||
"""
|
||||
|
||||
def keys():
|
||||
"""Return the keys of the mapping object.
|
||||
"""
|
||||
|
||||
def __iter__():
|
||||
"""Return an iterator for the keys of the mapping object.
|
||||
"""
|
||||
|
||||
def values():
|
||||
"""Return the values of the mapping object.
|
||||
"""
|
||||
|
||||
def items():
|
||||
"""Return the items of the mapping object.
|
||||
"""
|
||||
|
||||
|
||||
class IMapping(IWriteMapping, IEnumerableMapping):
|
||||
''' Simple mapping interface '''
|
||||
|
||||
|
||||
class IIterableMapping(IEnumerableMapping):
|
||||
"""A mapping that has distinct methods for iterating
|
||||
without copying.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class IClonableMapping(Interface):
|
||||
"""Something that can produce a copy of itself.
|
||||
|
||||
This is available in `dict`.
|
||||
"""
|
||||
|
||||
def copy():
|
||||
"return copy of dict"
|
||||
|
||||
|
||||
class IExtendedReadMapping(IIterableMapping):
|
||||
"""
|
||||
Something with a particular method equivalent to ``__contains__``.
|
||||
|
||||
On Python 2, `dict` provided the ``has_key`` method, but it was removed
|
||||
in Python 3.
|
||||
"""
|
||||
|
||||
|
||||
class IExtendedWriteMapping(IWriteMapping):
|
||||
"""Additional mutation methods.
|
||||
|
||||
These are all provided by `dict`.
|
||||
"""
|
||||
|
||||
def clear():
|
||||
"delete all items"
|
||||
|
||||
def update(d):
|
||||
" Update D from E: for k in E.keys(): D[k] = E[k]"
|
||||
|
||||
def setdefault(key, default=None):
|
||||
"D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D"
|
||||
|
||||
def pop(k, default=None):
|
||||
"""
|
||||
pop(k[,default]) -> value
|
||||
|
||||
Remove specified key and return the corresponding value.
|
||||
|
||||
If key is not found, *default* is returned if given, otherwise
|
||||
`KeyError` is raised. Note that *default* must not be passed by
|
||||
name.
|
||||
"""
|
||||
|
||||
def popitem():
|
||||
"""remove and return some (key, value) pair as a
|
||||
2-tuple; but raise KeyError if mapping is empty"""
|
||||
|
||||
|
||||
class IFullMapping(
|
||||
collections.IMutableMapping,
|
||||
IExtendedReadMapping,
|
||||
IExtendedWriteMapping,
|
||||
IClonableMapping,
|
||||
IMapping,
|
||||
):
|
||||
"""
|
||||
Full mapping interface.
|
||||
|
||||
Most uses of this interface should instead use
|
||||
:class:`~zope.interface.commons.collections.IMutableMapping` (one of the
|
||||
bases of this interface). The required methods are the same.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``IMutableMapping``
|
||||
"""
|
||||
@@ -0,0 +1,65 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
"""
|
||||
Interface definitions paralleling the abstract base classes defined in
|
||||
:mod:`numbers`.
|
||||
|
||||
After this module is imported, the standard library types will declare
|
||||
that they implement the appropriate interface.
|
||||
|
||||
.. versionadded:: 5.0.0
|
||||
"""
|
||||
|
||||
import numbers as abc
|
||||
|
||||
from zope.interface.common import ABCInterface
|
||||
from zope.interface.common import optional
|
||||
|
||||
|
||||
# pylint:disable=inherit-non-class,
|
||||
# pylint:disable=no-self-argument,no-method-argument
|
||||
# pylint:disable=unexpected-special-method-signature
|
||||
# pylint:disable=no-value-for-parameter
|
||||
|
||||
|
||||
class INumber(ABCInterface):
|
||||
abc = abc.Number
|
||||
|
||||
|
||||
class IComplex(INumber):
|
||||
abc = abc.Complex
|
||||
|
||||
@optional
|
||||
def __complex__():
|
||||
"""
|
||||
Rarely implemented, even in builtin types.
|
||||
"""
|
||||
|
||||
|
||||
class IReal(IComplex):
|
||||
abc = abc.Real
|
||||
|
||||
@optional
|
||||
def __complex__():
|
||||
"""
|
||||
Rarely implemented, even in builtin types.
|
||||
"""
|
||||
|
||||
__floor__ = __ceil__ = __complex__
|
||||
|
||||
|
||||
class IRational(IReal):
|
||||
abc = abc.Rational
|
||||
|
||||
|
||||
class IIntegral(IRational):
|
||||
abc = abc.Integral
|
||||
@@ -0,0 +1,195 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""
|
||||
Sequence Interfaces
|
||||
|
||||
Importing this module does *not* mark any standard classes as
|
||||
implementing any of these interfaces.
|
||||
|
||||
While this module is not deprecated, new code should generally use
|
||||
:mod:`zope.interface.common.collections`, specifically
|
||||
:class:`~zope.interface.common.collections.ISequence` and
|
||||
:class:`~zope.interface.common.collections.IMutableSequence`. This
|
||||
module is occasionally useful for its fine-grained breakdown of interfaces.
|
||||
|
||||
The standard library :class:`list`, :class:`tuple` and
|
||||
:class:`collections.UserList`, among others, implement ``ISequence``
|
||||
or ``IMutableSequence`` but *do not* implement any of the interfaces
|
||||
in this module.
|
||||
"""
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
from zope.interface import Interface
|
||||
from zope.interface.common import collections
|
||||
|
||||
|
||||
class IMinimalSequence(collections.IIterable):
|
||||
"""Most basic sequence interface.
|
||||
|
||||
All sequences are iterable. This requires at least one of the
|
||||
following:
|
||||
|
||||
- a `__getitem__()` method that takes a single argument; integer
|
||||
values starting at 0 must be supported, and `IndexError` should
|
||||
be raised for the first index for which there is no value, or
|
||||
|
||||
- an `__iter__()` method that returns an iterator as defined in
|
||||
the Python documentation (http://docs.python.org/lib/typeiter.html).
|
||||
|
||||
"""
|
||||
|
||||
def __getitem__(index):
|
||||
"""``x.__getitem__(index) <==> x[index]``
|
||||
|
||||
Declaring this interface does not specify whether `__getitem__`
|
||||
supports slice objects."""
|
||||
|
||||
|
||||
class IFiniteSequence(collections.ISized, IMinimalSequence):
|
||||
"""
|
||||
A sequence of bound size.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``ISized``
|
||||
"""
|
||||
|
||||
|
||||
class IReadSequence(collections.IContainer, IFiniteSequence):
|
||||
"""
|
||||
read interface shared by tuple and list
|
||||
|
||||
This interface is similar to
|
||||
:class:`~zope.interface.common.collections.ISequence`, but
|
||||
requires that all instances be totally ordered. Most users
|
||||
should prefer ``ISequence``.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``IContainer``
|
||||
"""
|
||||
|
||||
def __contains__(item):
|
||||
"""``x.__contains__(item) <==> item in x``"""
|
||||
# Optional in IContainer, required here.
|
||||
|
||||
def __lt__(other):
|
||||
"""``x.__lt__(other) <==> x < other``"""
|
||||
|
||||
def __le__(other):
|
||||
"""``x.__le__(other) <==> x <= other``"""
|
||||
|
||||
def __eq__(other):
|
||||
"""``x.__eq__(other) <==> x == other``"""
|
||||
|
||||
def __ne__(other):
|
||||
"""``x.__ne__(other) <==> x != other``"""
|
||||
|
||||
def __gt__(other):
|
||||
"""``x.__gt__(other) <==> x > other``"""
|
||||
|
||||
def __ge__(other):
|
||||
"""``x.__ge__(other) <==> x >= other``"""
|
||||
|
||||
def __add__(other):
|
||||
"""``x.__add__(other) <==> x + other``"""
|
||||
|
||||
def __mul__(n):
|
||||
"""``x.__mul__(n) <==> x * n``"""
|
||||
|
||||
def __rmul__(n):
|
||||
"""``x.__rmul__(n) <==> n * x``"""
|
||||
|
||||
|
||||
class IExtendedReadSequence(IReadSequence):
|
||||
"""Full read interface for lists"""
|
||||
|
||||
def count(item):
|
||||
"""Return number of occurrences of value"""
|
||||
|
||||
def index(item, *args):
|
||||
"""index(value, [start, [stop]]) -> int
|
||||
|
||||
Return first index of *value*
|
||||
"""
|
||||
|
||||
|
||||
class IUniqueMemberWriteSequence(Interface):
|
||||
"""The write contract for a sequence that may enforce unique members"""
|
||||
|
||||
def __setitem__(index, item):
|
||||
"""``x.__setitem__(index, item) <==> x[index] = item``
|
||||
|
||||
Declaring this interface does not specify whether `__setitem__`
|
||||
supports slice objects.
|
||||
"""
|
||||
|
||||
def __delitem__(index):
|
||||
"""``x.__delitem__(index) <==> del x[index]``
|
||||
|
||||
Declaring this interface does not specify whether `__delitem__`
|
||||
supports slice objects.
|
||||
"""
|
||||
|
||||
def __iadd__(y):
|
||||
"""``x.__iadd__(y) <==> x += y``"""
|
||||
|
||||
def append(item):
|
||||
"""Append item to end"""
|
||||
|
||||
def insert(index, item):
|
||||
"""Insert item before index"""
|
||||
|
||||
def pop(index=-1):
|
||||
"""Remove and return item at index (default last)"""
|
||||
|
||||
def remove(item):
|
||||
"""Remove first occurrence of value"""
|
||||
|
||||
def reverse():
|
||||
"""Reverse *IN PLACE*"""
|
||||
|
||||
def sort(cmpfunc=None):
|
||||
"""Stable sort *IN PLACE*; `cmpfunc(x, y)` -> -1, 0, 1"""
|
||||
|
||||
def extend(iterable):
|
||||
"""Extend list by appending elements from the iterable"""
|
||||
|
||||
|
||||
class IWriteSequence(IUniqueMemberWriteSequence):
|
||||
"""Full write contract for sequences"""
|
||||
|
||||
def __imul__(n):
|
||||
"""``x.__imul__(n) <==> x *= n``"""
|
||||
|
||||
|
||||
class ISequence(IReadSequence, IWriteSequence):
|
||||
"""
|
||||
Full sequence contract.
|
||||
|
||||
New code should prefer
|
||||
:class:`~zope.interface.common.collections.IMutableSequence`.
|
||||
|
||||
Compared to that interface, which is implemented by :class:`list`
|
||||
(:class:`~zope.interface.common.builtins.IList`), among others,
|
||||
this interface is missing the following methods:
|
||||
|
||||
- clear
|
||||
|
||||
- count
|
||||
|
||||
- index
|
||||
|
||||
This interface adds the following methods:
|
||||
|
||||
- sort
|
||||
"""
|
||||
@@ -0,0 +1,146 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
|
||||
import unittest
|
||||
|
||||
from zope.interface.common import ABCInterface
|
||||
from zope.interface.common import ABCInterfaceClass
|
||||
from zope.interface.verify import verifyClass
|
||||
from zope.interface.verify import verifyObject
|
||||
|
||||
|
||||
def iter_abc_interfaces(predicate=lambda iface: True):
|
||||
# Iterate ``(iface, classes)``, where ``iface`` is a descendent of
|
||||
# the ABCInterfaceClass passing the *predicate* and ``classes`` is
|
||||
# an iterable of classes registered to conform to that interface.
|
||||
#
|
||||
# Note that some builtin classes are registered for two distinct parts of
|
||||
# the ABC/interface tree. For example, bytearray is both ByteString and
|
||||
# MutableSequence.
|
||||
seen = set()
|
||||
stack = list(
|
||||
ABCInterface.dependents
|
||||
) # subclasses, but also implementedBy objects
|
||||
|
||||
while stack:
|
||||
iface = stack.pop(0)
|
||||
if iface in seen or not isinstance(iface, ABCInterfaceClass):
|
||||
continue
|
||||
seen.add(iface)
|
||||
stack.extend(list(iface.dependents))
|
||||
if not predicate(iface):
|
||||
continue
|
||||
|
||||
registered = set(iface.getRegisteredConformers())
|
||||
registered -= set(iface._ABCInterfaceClass__ignored_classes)
|
||||
if registered:
|
||||
yield iface, registered
|
||||
|
||||
|
||||
def add_abc_interface_tests(cls, module):
|
||||
def predicate(iface):
|
||||
return iface.__module__ == module
|
||||
add_verify_tests(cls, iter_abc_interfaces(predicate))
|
||||
|
||||
|
||||
def add_verify_tests(cls, iface_classes_iter):
|
||||
cls.maxDiff = None
|
||||
for iface, registered_classes in iface_classes_iter:
|
||||
for stdlib_class in registered_classes:
|
||||
def test(self, stdlib_class=stdlib_class, iface=iface):
|
||||
if (
|
||||
stdlib_class in self.UNVERIFIABLE or
|
||||
stdlib_class.__name__ in self.UNVERIFIABLE
|
||||
):
|
||||
self.skipTest("Unable to verify %s" % stdlib_class)
|
||||
|
||||
self.assertTrue(self.verify(iface, stdlib_class))
|
||||
|
||||
suffix = "{}_{}_{}_{}".format(
|
||||
stdlib_class.__module__.replace('.', '_'),
|
||||
stdlib_class.__name__,
|
||||
iface.__module__.replace('.', '_'),
|
||||
iface.__name__
|
||||
)
|
||||
name = 'test_auto_' + suffix
|
||||
test.__name__ = name
|
||||
assert not hasattr(cls, name), (name, list(cls.__dict__))
|
||||
setattr(cls, name, test)
|
||||
|
||||
def test_ro(self, stdlib_class=stdlib_class, iface=iface):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementedBy
|
||||
from zope.interface import ro
|
||||
self.assertEqual(
|
||||
tuple(ro.ro(iface, strict=True)),
|
||||
iface.__sro__)
|
||||
implements = implementedBy(stdlib_class)
|
||||
sro = implements.__sro__
|
||||
self.assertIs(sro[-1], Interface)
|
||||
|
||||
if stdlib_class not in self.UNVERIFIABLE_RO:
|
||||
# Check that we got the strict C3 resolution order, unless
|
||||
# we know we cannot. Note that 'Interface' is virtual base
|
||||
# that doesn't necessarily appear at the same place in the
|
||||
# calculated SRO as in the final SRO.
|
||||
strict = stdlib_class not in self.NON_STRICT_RO
|
||||
isro = ro.ro(implements, strict=strict)
|
||||
isro.remove(Interface)
|
||||
isro.append(Interface)
|
||||
|
||||
self.assertEqual(tuple(isro), sro)
|
||||
|
||||
name = 'test_auto_ro_' + suffix
|
||||
test_ro.__name__ = name
|
||||
assert not hasattr(cls, name)
|
||||
setattr(cls, name, test_ro)
|
||||
|
||||
|
||||
class VerifyClassMixin(unittest.TestCase):
|
||||
verifier = staticmethod(verifyClass)
|
||||
UNVERIFIABLE = ()
|
||||
NON_STRICT_RO = ()
|
||||
UNVERIFIABLE_RO = ()
|
||||
|
||||
def _adjust_object_before_verify(self, iface, x):
|
||||
return x
|
||||
|
||||
def verify(self, iface, klass, **kwargs):
|
||||
return self.verifier(iface,
|
||||
self._adjust_object_before_verify(iface, klass),
|
||||
**kwargs)
|
||||
|
||||
|
||||
class VerifyObjectMixin(VerifyClassMixin):
|
||||
verifier = staticmethod(verifyObject)
|
||||
CONSTRUCTORS = {
|
||||
}
|
||||
|
||||
def _adjust_object_before_verify(self, iface, x):
|
||||
constructor = self.CONSTRUCTORS.get(x)
|
||||
if not constructor:
|
||||
constructor = self.CONSTRUCTORS.get(iface)
|
||||
if not constructor:
|
||||
constructor = self.CONSTRUCTORS.get(x.__name__)
|
||||
if not constructor:
|
||||
constructor = x
|
||||
if constructor is unittest.SkipTest:
|
||||
self.skipTest("Cannot create " + str(x))
|
||||
|
||||
try:
|
||||
result = constructor()
|
||||
except Exception as e: # pragma: no cover
|
||||
raise TypeError(
|
||||
f'Failed to create instance of {constructor}') from e
|
||||
if hasattr(result, 'close'):
|
||||
self.addCleanup(result.close)
|
||||
return result
|
||||
@@ -0,0 +1,115 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Base Mapping tests
|
||||
"""
|
||||
from operator import __getitem__
|
||||
|
||||
|
||||
def testIReadMapping(self, inst, state, absent):
|
||||
for key in state:
|
||||
self.assertEqual(inst[key], state[key])
|
||||
self.assertEqual(inst.get(key, None), state[key])
|
||||
self.assertIn(key, inst)
|
||||
|
||||
for key in absent:
|
||||
self.assertEqual(inst.get(key, None), None)
|
||||
self.assertEqual(inst.get(key), None)
|
||||
self.assertEqual(inst.get(key, self), self)
|
||||
self.assertRaises(KeyError, __getitem__, inst, key)
|
||||
|
||||
|
||||
def test_keys(self, inst, state):
|
||||
# Return the keys of the mapping object
|
||||
inst_keys = sorted(inst.keys())
|
||||
state_keys = sorted(state.keys())
|
||||
self.assertEqual(inst_keys, state_keys)
|
||||
|
||||
|
||||
def test_iter(self, inst, state):
|
||||
# Return the keys of the mapping object
|
||||
inst_keys = sorted(inst)
|
||||
state_keys = sorted(state.keys())
|
||||
self.assertEqual(inst_keys, state_keys)
|
||||
|
||||
|
||||
def test_values(self, inst, state):
|
||||
# Return the values of the mapping object
|
||||
inst_values = sorted(inst.values())
|
||||
state_values = sorted(state.values())
|
||||
self.assertEqual(inst_values, state_values)
|
||||
|
||||
|
||||
def test_items(self, inst, state):
|
||||
# Return the items of the mapping object
|
||||
inst_items = sorted(inst.items())
|
||||
state_items = sorted(state.items())
|
||||
self.assertEqual(inst_items, state_items)
|
||||
|
||||
|
||||
def test___len__(self, inst, state):
|
||||
# Return the number of items
|
||||
self.assertEqual(len(inst), len(state))
|
||||
|
||||
|
||||
def testIEnumerableMapping(self, inst, state):
|
||||
test_keys(self, inst, state)
|
||||
test_items(self, inst, state)
|
||||
test_values(self, inst, state)
|
||||
test___len__(self, inst, state)
|
||||
|
||||
|
||||
class BaseTestIReadMapping:
|
||||
|
||||
def testIReadMapping(self):
|
||||
inst = self._IReadMapping__sample()
|
||||
state = self._IReadMapping__stateDict()
|
||||
absent = self._IReadMapping__absentKeys()
|
||||
testIReadMapping(self, inst, state, absent)
|
||||
|
||||
|
||||
class BaseTestIEnumerableMapping(BaseTestIReadMapping):
|
||||
# Mapping objects whose items can be enumerated
|
||||
|
||||
def test_keys(self):
|
||||
# Return the keys of the mapping object
|
||||
inst = self._IEnumerableMapping__sample()
|
||||
state = self._IEnumerableMapping__stateDict()
|
||||
test_keys(self, inst, state)
|
||||
|
||||
def test_values(self):
|
||||
# Return the values of the mapping object
|
||||
inst = self._IEnumerableMapping__sample()
|
||||
state = self._IEnumerableMapping__stateDict()
|
||||
test_values(self, inst, state)
|
||||
|
||||
def test_items(self):
|
||||
# Return the items of the mapping object
|
||||
inst = self._IEnumerableMapping__sample()
|
||||
state = self._IEnumerableMapping__stateDict()
|
||||
test_items(self, inst, state)
|
||||
|
||||
def test___len__(self):
|
||||
# Return the number of items
|
||||
inst = self._IEnumerableMapping__sample()
|
||||
state = self._IEnumerableMapping__stateDict()
|
||||
test___len__(self, inst, state)
|
||||
|
||||
def _IReadMapping__stateDict(self):
|
||||
return self._IEnumerableMapping__stateDict()
|
||||
|
||||
def _IReadMapping__sample(self):
|
||||
return self._IEnumerableMapping__sample()
|
||||
|
||||
def _IReadMapping__absentKeys(self):
|
||||
return self._IEnumerableMapping__absentKeys()
|
||||
@@ -0,0 +1,43 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
|
||||
import unittest
|
||||
|
||||
from zope.interface.common import builtins
|
||||
|
||||
from . import VerifyClassMixin
|
||||
from . import VerifyObjectMixin
|
||||
from . import add_verify_tests
|
||||
|
||||
|
||||
class TestVerifyClass(VerifyClassMixin,
|
||||
unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
add_verify_tests(TestVerifyClass, (
|
||||
(builtins.IList, (list,)),
|
||||
(builtins.ITuple, (tuple,)),
|
||||
(builtins.ITextString, (str,)),
|
||||
(builtins.IByteString, (bytes,)),
|
||||
(builtins.INativeString, (str,)),
|
||||
(builtins.IBool, (bool,)),
|
||||
(builtins.IDict, (dict,)),
|
||||
(builtins.IFile, ()),
|
||||
))
|
||||
|
||||
|
||||
class TestVerifyObject(VerifyObjectMixin,
|
||||
TestVerifyClass):
|
||||
CONSTRUCTORS = {
|
||||
builtins.IFile: lambda: open(__file__)
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
|
||||
|
||||
import array
|
||||
import sys
|
||||
import unittest
|
||||
from collections import OrderedDict
|
||||
from collections import abc
|
||||
from collections import deque
|
||||
from types import MappingProxyType
|
||||
|
||||
from zope.interface import Invalid
|
||||
from zope.interface._compat import PYPY
|
||||
# Note that importing z.i.c.collections does work on import.
|
||||
from zope.interface.common import collections
|
||||
|
||||
from . import VerifyClassMixin
|
||||
from . import VerifyObjectMixin
|
||||
from . import add_abc_interface_tests
|
||||
|
||||
|
||||
class TestVerifyClass(VerifyClassMixin, unittest.TestCase):
|
||||
|
||||
# Here we test some known builtin classes that are defined to implement
|
||||
# various collection interfaces as a quick sanity test.
|
||||
def test_frozenset(self):
|
||||
self.assertIsInstance(frozenset(), abc.Set)
|
||||
self.assertTrue(self.verify(collections.ISet, frozenset))
|
||||
|
||||
def test_list(self):
|
||||
self.assertIsInstance(list(), abc.MutableSequence)
|
||||
self.assertTrue(self.verify(collections.IMutableSequence, list))
|
||||
|
||||
# Here we test some derived classes.
|
||||
def test_UserList(self):
|
||||
self.assertTrue(self.verify(collections.IMutableSequence,
|
||||
collections.UserList))
|
||||
|
||||
def test_UserDict(self):
|
||||
self.assertTrue(self.verify(collections.IMutableMapping,
|
||||
collections.UserDict))
|
||||
|
||||
def test_UserString(self):
|
||||
self.assertTrue(self.verify(collections.ISequence,
|
||||
collections.UserString))
|
||||
|
||||
# Now we go through the registry, which should have several things, mostly
|
||||
# builtins, but if we've imported other libraries already, it could
|
||||
# contain things from outside of there too. We aren't concerned about
|
||||
# third-party code here, just standard library types. We start with a
|
||||
# blacklist of things to exclude, but if that gets out of hand we can
|
||||
# figure out a better whitelisting.
|
||||
UNVERIFIABLE = {
|
||||
# This is declared to be an ISequence, but is missing lots of methods,
|
||||
# including some that aren't part of a language protocol, such as
|
||||
# ``index`` and ``count``.
|
||||
memoryview,
|
||||
# 'pkg_resources._vendor.pyparsing.ParseResults' is registered as a
|
||||
# MutableMapping but is missing methods like ``popitem`` and
|
||||
# ``setdefault``. It's imported due to namespace packages.
|
||||
'ParseResults',
|
||||
# sqlite3.Row claims ISequence but also misses ``index`` and
|
||||
# ``count``. It's imported because...? Coverage imports it, but why
|
||||
# do we have it without coverage?
|
||||
'Row',
|
||||
# In Python 3.10 ``array.array`` appears as ``IMutableSequence`` but it
|
||||
# does not provide a ``clear()`` method and it cannot be instantiated
|
||||
# using ``array.array()``.
|
||||
array.array,
|
||||
}
|
||||
|
||||
if PYPY:
|
||||
UNVERIFIABLE.update({
|
||||
# collections.deque.pop() doesn't support the index= argument to
|
||||
# MutableSequence.pop(). We can't verify this on CPython because
|
||||
# we can't get the signature, but on PyPy we /can/ get the
|
||||
# signature, and of course it doesn't match.
|
||||
deque,
|
||||
# Likewise for index
|
||||
range,
|
||||
})
|
||||
UNVERIFIABLE_RO = {
|
||||
# ``array.array`` fails the ``test_auto_ro_*`` tests with and
|
||||
# without strict RO but only on Windows (AppVeyor) on Python 3.10.0
|
||||
# (in older versions ``array.array`` does not appear as
|
||||
# ``IMutableSequence``).
|
||||
array.array,
|
||||
}
|
||||
|
||||
|
||||
add_abc_interface_tests(TestVerifyClass, collections.ISet.__module__)
|
||||
|
||||
|
||||
def _get_FrameLocalsProxy():
|
||||
return type(sys._getframe().f_locals)
|
||||
|
||||
|
||||
class TestVerifyObject(VerifyObjectMixin,
|
||||
TestVerifyClass):
|
||||
CONSTRUCTORS = {
|
||||
collections.IValuesView: {}.values,
|
||||
collections.IItemsView: {}.items,
|
||||
collections.IKeysView: {}.keys,
|
||||
memoryview: lambda: memoryview(b'abc'),
|
||||
range: lambda: range(10),
|
||||
MappingProxyType: lambda: MappingProxyType({}),
|
||||
collections.UserString: lambda: collections.UserString('abc'),
|
||||
type(iter(bytearray())): lambda: iter(bytearray()),
|
||||
type(iter(b'abc')): lambda: iter(b'abc'),
|
||||
'coroutine': unittest.SkipTest,
|
||||
type(iter({}.keys())): lambda: iter({}.keys()),
|
||||
type(iter({}.items())): lambda: iter({}.items()),
|
||||
type(iter({}.values())): lambda: iter({}.values()),
|
||||
type(i for i in range(1)): lambda: (i for i in range(3)),
|
||||
type(iter([])): lambda: iter([]),
|
||||
type(reversed([])): lambda: reversed([]),
|
||||
'longrange_iterator': unittest.SkipTest,
|
||||
'range_iterator': lambda: iter(range(3)),
|
||||
'rangeiterator': lambda: iter(range(3)),
|
||||
type(iter(set())): lambda: iter(set()),
|
||||
type(iter('')): lambda: iter(''),
|
||||
'async_generator': unittest.SkipTest,
|
||||
type(iter(tuple())): lambda: iter(tuple()),
|
||||
}
|
||||
if sys.version_info >= (3, 13):
|
||||
def FrameLocalsProxy_constructor():
|
||||
return _get_FrameLocalsProxy()(sys._getframe())
|
||||
FrameLocalsProxy = _get_FrameLocalsProxy()
|
||||
CONSTRUCTORS[FrameLocalsProxy] = FrameLocalsProxy_constructor
|
||||
|
||||
UNVERIFIABLE_RO = {
|
||||
# ``array.array`` fails the ``test_auto_ro_*`` tests with and
|
||||
# without strict RO but only on Windows (AppVeyor) on Python 3.10.0
|
||||
# (in older versions ``array.array`` does not appear as
|
||||
# ``IMutableSequence``).
|
||||
array.array,
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Test for datetime interfaces
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
from datetime import time
|
||||
from datetime import timedelta
|
||||
from datetime import tzinfo
|
||||
|
||||
from zope.interface.common.idatetime import IDate
|
||||
from zope.interface.common.idatetime import IDateClass
|
||||
from zope.interface.common.idatetime import IDateTime
|
||||
from zope.interface.common.idatetime import IDateTimeClass
|
||||
from zope.interface.common.idatetime import ITime
|
||||
from zope.interface.common.idatetime import ITimeClass
|
||||
from zope.interface.common.idatetime import ITimeDelta
|
||||
from zope.interface.common.idatetime import ITimeDeltaClass
|
||||
from zope.interface.common.idatetime import ITZInfo
|
||||
from zope.interface.verify import verifyClass
|
||||
from zope.interface.verify import verifyObject
|
||||
|
||||
|
||||
class TestDateTimeInterfaces(unittest.TestCase):
|
||||
|
||||
def test_interfaces(self):
|
||||
verifyObject(ITimeDelta, timedelta(minutes=20))
|
||||
verifyObject(IDate, date(2000, 1, 2))
|
||||
verifyObject(IDateTime, datetime(2000, 1, 2, 10, 20))
|
||||
verifyObject(ITime, time(20, 30, 15, 1234))
|
||||
verifyObject(ITZInfo, tzinfo())
|
||||
verifyClass(ITimeDeltaClass, timedelta)
|
||||
verifyClass(IDateClass, date)
|
||||
verifyClass(IDateTimeClass, datetime)
|
||||
verifyClass(ITimeClass, time)
|
||||
@@ -0,0 +1,21 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2006 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
|
||||
class TestInterfaceImport(unittest.TestCase):
|
||||
|
||||
def test_import(self):
|
||||
import zope.interface.common.interfaces as x
|
||||
self.assertIsNotNone(x)
|
||||
@@ -0,0 +1,46 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
|
||||
|
||||
import io as abc
|
||||
import unittest
|
||||
|
||||
# Note that importing z.i.c.io does work on import.
|
||||
from zope.interface.common import io
|
||||
|
||||
from . import VerifyClassMixin
|
||||
from . import VerifyObjectMixin
|
||||
from . import add_abc_interface_tests
|
||||
|
||||
|
||||
class TestVerifyClass(VerifyClassMixin,
|
||||
unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
add_abc_interface_tests(TestVerifyClass, io.IIOBase.__module__)
|
||||
|
||||
|
||||
class TestVerifyObject(VerifyObjectMixin,
|
||||
TestVerifyClass):
|
||||
CONSTRUCTORS = {
|
||||
abc.BufferedWriter: lambda: abc.BufferedWriter(abc.StringIO()),
|
||||
abc.BufferedReader: lambda: abc.BufferedReader(abc.StringIO()),
|
||||
abc.TextIOWrapper: lambda: abc.TextIOWrapper(abc.BytesIO()),
|
||||
abc.BufferedRandom: lambda: abc.BufferedRandom(abc.BytesIO()),
|
||||
abc.BufferedRWPair: lambda: abc.BufferedRWPair(
|
||||
abc.BytesIO(), abc.BytesIO()
|
||||
),
|
||||
abc.FileIO: lambda: abc.FileIO(__file__),
|
||||
'_WindowsConsoleIO': unittest.SkipTest,
|
||||
'WinConsoleIO': unittest.SkipTest, # breaks on PyPy-3.10
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2020 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
##############################################################################
|
||||
|
||||
|
||||
import numbers as abc
|
||||
import unittest
|
||||
|
||||
# Note that importing z.i.c.numbers does work on import.
|
||||
from zope.interface.common import numbers
|
||||
|
||||
from . import VerifyClassMixin
|
||||
from . import VerifyObjectMixin
|
||||
from . import add_abc_interface_tests
|
||||
|
||||
|
||||
class TestVerifyClass(VerifyClassMixin,
|
||||
unittest.TestCase):
|
||||
|
||||
def test_int(self):
|
||||
self.assertIsInstance(int(), abc.Integral)
|
||||
self.assertTrue(self.verify(numbers.IIntegral, int))
|
||||
|
||||
def test_float(self):
|
||||
self.assertIsInstance(float(), abc.Real)
|
||||
self.assertTrue(self.verify(numbers.IReal, float))
|
||||
|
||||
|
||||
add_abc_interface_tests(TestVerifyClass, numbers.INumber.__module__)
|
||||
|
||||
|
||||
class TestVerifyObject(VerifyObjectMixin,
|
||||
TestVerifyClass):
|
||||
pass
|
||||
1219
.venv/lib/python3.12/site-packages/zope/interface/declarations.py
Normal file
1219
.venv/lib/python3.12/site-packages/zope/interface/declarations.py
Normal file
File diff suppressed because it is too large
Load Diff
133
.venv/lib/python3.12/site-packages/zope/interface/document.py
Normal file
133
.venv/lib/python3.12/site-packages/zope/interface/document.py
Normal file
@@ -0,0 +1,133 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
""" Pretty-Print an Interface object as structured text (Yum)
|
||||
|
||||
This module provides a function, asStructuredText, for rendering an
|
||||
interface as structured text.
|
||||
"""
|
||||
import zope.interface
|
||||
|
||||
|
||||
__all__ = [
|
||||
'asReStructuredText',
|
||||
'asStructuredText',
|
||||
]
|
||||
|
||||
|
||||
def asStructuredText(iface, munge=0, rst=False):
|
||||
""" Output structured text format. Note, this will whack any existing
|
||||
'structured' format of the text.
|
||||
|
||||
If `rst=True`, then the output will quote all code as inline literals in
|
||||
accordance with 'reStructuredText' markup principles.
|
||||
"""
|
||||
|
||||
if rst:
|
||||
def inline_literal(s):
|
||||
return f"``{s}``"
|
||||
else:
|
||||
def inline_literal(s):
|
||||
return s
|
||||
|
||||
r = [inline_literal(iface.getName())]
|
||||
outp = r.append
|
||||
level = 1
|
||||
|
||||
if iface.getDoc():
|
||||
outp(_justify_and_indent(_trim_doc_string(iface.getDoc()), level))
|
||||
|
||||
bases = [base
|
||||
for base in iface.__bases__
|
||||
if base is not zope.interface.Interface
|
||||
]
|
||||
if bases:
|
||||
outp(_justify_and_indent("This interface extends:", level, munge))
|
||||
level += 1
|
||||
for b in bases:
|
||||
item = "o %s" % inline_literal(b.getName())
|
||||
outp(_justify_and_indent(_trim_doc_string(item), level, munge))
|
||||
level -= 1
|
||||
|
||||
namesAndDescriptions = sorted(iface.namesAndDescriptions())
|
||||
|
||||
outp(_justify_and_indent("Attributes:", level, munge))
|
||||
level += 1
|
||||
for name, desc in namesAndDescriptions:
|
||||
if not hasattr(desc, 'getSignatureString'): # ugh...
|
||||
item = "{} -- {}".format(
|
||||
inline_literal(desc.getName()),
|
||||
desc.getDoc() or 'no documentation'
|
||||
)
|
||||
outp(_justify_and_indent(_trim_doc_string(item), level, munge))
|
||||
level -= 1
|
||||
|
||||
outp(_justify_and_indent("Methods:", level, munge))
|
||||
level += 1
|
||||
for name, desc in namesAndDescriptions:
|
||||
if hasattr(desc, 'getSignatureString'): # ugh...
|
||||
_call = f"{desc.getName()}{desc.getSignatureString()}"
|
||||
item = "{} -- {}".format(
|
||||
inline_literal(_call),
|
||||
desc.getDoc() or 'no documentation'
|
||||
)
|
||||
outp(_justify_and_indent(_trim_doc_string(item), level, munge))
|
||||
|
||||
return "\n\n".join(r) + "\n\n"
|
||||
|
||||
|
||||
def asReStructuredText(iface, munge=0):
|
||||
""" Output reStructuredText format.
|
||||
|
||||
Note, this will whack any existing 'structured' format of the text."""
|
||||
return asStructuredText(iface, munge=munge, rst=True)
|
||||
|
||||
|
||||
def _trim_doc_string(text):
|
||||
""" Trims a doc string to make it format
|
||||
correctly with structured text. """
|
||||
|
||||
lines = text.replace('\r\n', '\n').split('\n')
|
||||
nlines = [lines.pop(0)]
|
||||
if lines:
|
||||
min_indent = min([len(line) - len(line.lstrip())
|
||||
for line in lines])
|
||||
for line in lines:
|
||||
nlines.append(line[min_indent:])
|
||||
|
||||
return '\n'.join(nlines)
|
||||
|
||||
|
||||
def _justify_and_indent(text, level, munge=0, width=72):
|
||||
""" indent and justify text, rejustify (munge) if specified """
|
||||
|
||||
indent = " " * level
|
||||
|
||||
if munge:
|
||||
lines = []
|
||||
line = indent
|
||||
text = text.split()
|
||||
|
||||
for word in text:
|
||||
line = ' '.join([line, word])
|
||||
if len(line) > width:
|
||||
lines.append(line)
|
||||
line = indent
|
||||
else:
|
||||
lines.append(line)
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
else:
|
||||
return indent + \
|
||||
text.strip().replace("\r\n", "\n") .replace("\n", "\n" + indent)
|
||||
278
.venv/lib/python3.12/site-packages/zope/interface/exceptions.py
Normal file
278
.venv/lib/python3.12/site-packages/zope/interface/exceptions.py
Normal file
@@ -0,0 +1,278 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Interface-specific exceptions
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
# Invalid tree
|
||||
'Invalid',
|
||||
'DoesNotImplement',
|
||||
'BrokenImplementation',
|
||||
'BrokenMethodImplementation',
|
||||
'MultipleInvalid',
|
||||
# Other
|
||||
'BadImplements',
|
||||
'InvalidInterface',
|
||||
]
|
||||
|
||||
|
||||
class Invalid(Exception):
|
||||
"""A specification is violated
|
||||
"""
|
||||
|
||||
|
||||
class _TargetInvalid(Invalid):
|
||||
# Internal use. Subclass this when you're describing
|
||||
# a particular target object that's invalid according
|
||||
# to a specific interface.
|
||||
#
|
||||
# For backwards compatibility, the *target* and *interface* are
|
||||
# optional, and the signatures are inconsistent in their ordering.
|
||||
#
|
||||
# We deal with the inconsistency in ordering by defining the index
|
||||
# of the two values in ``self.args``. *target* uses a marker object to
|
||||
# distinguish "not given" from "given, but None", because the latter
|
||||
# can be a value that gets passed to validation. For this reason, it must
|
||||
# always be the last argument (we detect absence by the ``IndexError``).
|
||||
|
||||
_IX_INTERFACE = 0
|
||||
_IX_TARGET = 1
|
||||
# The exception to catch when indexing self.args indicating that
|
||||
# an argument was not given. If all arguments are expected,
|
||||
# a subclass should set this to ().
|
||||
_NOT_GIVEN_CATCH = IndexError
|
||||
_NOT_GIVEN = '<Not Given>'
|
||||
|
||||
def _get_arg_or_default(self, ix, default=None):
|
||||
try:
|
||||
return self.args[ix] # pylint:disable=unsubscriptable-object
|
||||
except self._NOT_GIVEN_CATCH:
|
||||
return default
|
||||
|
||||
@property
|
||||
def interface(self):
|
||||
return self._get_arg_or_default(self._IX_INTERFACE)
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
return self._get_arg_or_default(self._IX_TARGET, self._NOT_GIVEN)
|
||||
|
||||
###
|
||||
# str
|
||||
#
|
||||
# The ``__str__`` of self is implemented by concatenating (%s), in order,
|
||||
# these properties (none of which should have leading or trailing
|
||||
# whitespace):
|
||||
#
|
||||
# - self._str_subject
|
||||
# Begin the message, including a description of the target.
|
||||
# - self._str_description
|
||||
# Provide a general description of the type of error, including
|
||||
# the interface name if possible and relevant.
|
||||
# - self._str_conjunction
|
||||
# Join the description to the details. Defaults to ": ".
|
||||
# - self._str_details
|
||||
# Provide details about how this particular instance of the error.
|
||||
# - self._str_trailer
|
||||
# End the message. Usually just a period.
|
||||
###
|
||||
|
||||
@property
|
||||
def _str_subject(self):
|
||||
target = self.target
|
||||
if target is self._NOT_GIVEN:
|
||||
return "An object"
|
||||
return f"The object {target!r}"
|
||||
|
||||
@property
|
||||
def _str_description(self):
|
||||
return "has failed to implement interface %s" % (
|
||||
self.interface or '<Unknown>'
|
||||
)
|
||||
|
||||
_str_conjunction = ": "
|
||||
_str_details = "<unknown>"
|
||||
_str_trailer = '.'
|
||||
|
||||
def __str__(self):
|
||||
return "{} {}{}{}{}".format(
|
||||
self._str_subject,
|
||||
self._str_description,
|
||||
self._str_conjunction,
|
||||
self._str_details,
|
||||
self._str_trailer
|
||||
)
|
||||
|
||||
|
||||
class DoesNotImplement(_TargetInvalid):
|
||||
"""
|
||||
DoesNotImplement(interface[, target])
|
||||
|
||||
The *target* (optional) does not implement the *interface*.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Add the *target* argument and attribute, and change the resulting
|
||||
string value of this object accordingly.
|
||||
"""
|
||||
|
||||
_str_details = "Does not declaratively implement the interface"
|
||||
|
||||
|
||||
class BrokenImplementation(_TargetInvalid):
|
||||
"""
|
||||
BrokenImplementation(interface, name[, target])
|
||||
|
||||
The *target* (optional) is missing the attribute *name*.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Add the *target* argument and attribute, and change the resulting
|
||||
string value of this object accordingly.
|
||||
|
||||
The *name* can either be a simple string or a ``Attribute`` object.
|
||||
"""
|
||||
|
||||
_IX_NAME = _TargetInvalid._IX_INTERFACE + 1
|
||||
_IX_TARGET = _IX_NAME + 1
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.args[1] # pylint:disable=unsubscriptable-object
|
||||
|
||||
@property
|
||||
def _str_details(self):
|
||||
return "The %s attribute was not provided" % (
|
||||
repr(self.name) if isinstance(self.name, str) else self.name
|
||||
)
|
||||
|
||||
|
||||
class BrokenMethodImplementation(_TargetInvalid):
|
||||
"""
|
||||
BrokenMethodImplementation(
|
||||
method, message[, implementation, interface, target]
|
||||
)
|
||||
|
||||
The *target* (optional) has a *method* in *implementation* that violates
|
||||
its contract in a way described by *mess*.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Add the *interface* and *target* argument and attribute,
|
||||
and change the resulting string value of this object accordingly.
|
||||
|
||||
The *method* can either be a simple string or a ``Method`` object.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
If *implementation* is given, then the *message* will have the
|
||||
string "implementation" replaced with an short but informative
|
||||
representation of *implementation*.
|
||||
|
||||
"""
|
||||
|
||||
_IX_IMPL = 2
|
||||
_IX_INTERFACE = _IX_IMPL + 1
|
||||
_IX_TARGET = _IX_INTERFACE + 1
|
||||
|
||||
@property
|
||||
def method(self):
|
||||
return self.args[0] # pylint:disable=unsubscriptable-object
|
||||
|
||||
@property
|
||||
def mess(self):
|
||||
return self.args[1] # pylint:disable=unsubscriptable-object
|
||||
|
||||
@staticmethod
|
||||
def __implementation_str(impl):
|
||||
# It could be a callable or some arbitrary object, we don't
|
||||
# know yet.
|
||||
import inspect # Inspect is a heavy-weight dependency, lots of imports
|
||||
try:
|
||||
sig = inspect.signature
|
||||
formatsig = str
|
||||
except AttributeError:
|
||||
sig = inspect.getargspec
|
||||
formatsig = inspect.formatargspec
|
||||
|
||||
try:
|
||||
sig = sig(impl)
|
||||
except (ValueError, TypeError):
|
||||
# Unable to introspect. Darn.
|
||||
# This could be a non-callable, or a particular builtin,
|
||||
# or a bound method that doesn't even accept 'self', e.g.,
|
||||
# ``Class.method = lambda: None; Class().method``
|
||||
return repr(impl)
|
||||
|
||||
try:
|
||||
name = impl.__qualname__
|
||||
except AttributeError:
|
||||
name = impl.__name__
|
||||
|
||||
return name + formatsig(sig)
|
||||
|
||||
@property
|
||||
def _str_details(self):
|
||||
impl = self._get_arg_or_default(self._IX_IMPL, self._NOT_GIVEN)
|
||||
message = self.mess
|
||||
if impl is not self._NOT_GIVEN and 'implementation' in message:
|
||||
message = message.replace("implementation", '%r')
|
||||
message = message % (self.__implementation_str(impl),)
|
||||
|
||||
return 'The contract of {} is violated because {}'.format(
|
||||
repr(self.method) if isinstance(self.method, str) else self.method,
|
||||
message,
|
||||
)
|
||||
|
||||
|
||||
class MultipleInvalid(_TargetInvalid):
|
||||
"""
|
||||
The *target* has failed to implement the *interface* in
|
||||
multiple ways.
|
||||
|
||||
The failures are described by *exceptions*, a collection of
|
||||
other `Invalid` instances.
|
||||
|
||||
.. versionadded:: 5.0
|
||||
"""
|
||||
|
||||
_NOT_GIVEN_CATCH = ()
|
||||
|
||||
def __init__(self, interface, target, exceptions):
|
||||
super().__init__(interface, target, tuple(exceptions))
|
||||
|
||||
@property
|
||||
def exceptions(self):
|
||||
return self.args[2] # pylint:disable=unsubscriptable-object
|
||||
|
||||
@property
|
||||
def _str_details(self):
|
||||
# It would be nice to use tabs here, but that
|
||||
# is hard to represent in doctests.
|
||||
return '\n ' + '\n '.join(
|
||||
x._str_details.strip() if isinstance(x, _TargetInvalid) else str(x)
|
||||
for x in self.exceptions
|
||||
)
|
||||
|
||||
_str_conjunction = ':' # no trailing space, messes up doctests
|
||||
_str_trailer = ''
|
||||
|
||||
|
||||
class InvalidInterface(Exception):
|
||||
"""The interface has invalid contents
|
||||
"""
|
||||
|
||||
|
||||
class BadImplements(TypeError):
|
||||
"""An implementation assertion is invalid
|
||||
|
||||
because it doesn't contain an interface or a sequence of valid
|
||||
implementation assertions.
|
||||
"""
|
||||
1183
.venv/lib/python3.12/site-packages/zope/interface/interface.py
Normal file
1183
.venv/lib/python3.12/site-packages/zope/interface/interface.py
Normal file
File diff suppressed because it is too large
Load Diff
1516
.venv/lib/python3.12/site-packages/zope/interface/interfaces.py
Normal file
1516
.venv/lib/python3.12/site-packages/zope/interface/interfaces.py
Normal file
File diff suppressed because it is too large
Load Diff
750
.venv/lib/python3.12/site-packages/zope/interface/registry.py
Normal file
750
.venv/lib/python3.12/site-packages/zope/interface/registry.py
Normal file
@@ -0,0 +1,750 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2006 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Basic components support
|
||||
"""
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
try:
|
||||
from zope.event import notify
|
||||
except ImportError: # pragma: no cover
|
||||
def notify(*arg, **kw):
|
||||
pass
|
||||
|
||||
from zope.interface.adapter import AdapterRegistry
|
||||
from zope.interface.declarations import implementedBy
|
||||
from zope.interface.declarations import implementer
|
||||
from zope.interface.declarations import implementer_only
|
||||
from zope.interface.declarations import providedBy
|
||||
from zope.interface.interface import Interface
|
||||
from zope.interface.interfaces import ComponentLookupError
|
||||
from zope.interface.interfaces import IAdapterRegistration
|
||||
from zope.interface.interfaces import IComponents
|
||||
from zope.interface.interfaces import IHandlerRegistration
|
||||
from zope.interface.interfaces import ISpecification
|
||||
from zope.interface.interfaces import ISubscriptionAdapterRegistration
|
||||
from zope.interface.interfaces import IUtilityRegistration
|
||||
from zope.interface.interfaces import Registered
|
||||
from zope.interface.interfaces import Unregistered
|
||||
|
||||
|
||||
__all__ = [
|
||||
# Components is public API, but
|
||||
# the *Registration classes are just implementations
|
||||
# of public interfaces.
|
||||
'Components',
|
||||
]
|
||||
|
||||
|
||||
class _UnhashableComponentCounter:
|
||||
# defaultdict(int)-like object for unhashable components
|
||||
|
||||
def __init__(self, otherdict):
|
||||
# [(component, count)]
|
||||
self._data = [item for item in otherdict.items()]
|
||||
|
||||
def __getitem__(self, key):
|
||||
for component, count in self._data:
|
||||
if component == key:
|
||||
return count
|
||||
return 0
|
||||
|
||||
def __setitem__(self, component, count):
|
||||
for i, data in enumerate(self._data):
|
||||
if data[0] == component:
|
||||
self._data[i] = component, count
|
||||
return
|
||||
self._data.append((component, count))
|
||||
|
||||
def __delitem__(self, component):
|
||||
for i, data in enumerate(self._data):
|
||||
if data[0] == component:
|
||||
del self._data[i]
|
||||
return
|
||||
raise KeyError(component) # pragma: no cover
|
||||
|
||||
|
||||
def _defaultdict_int():
|
||||
return defaultdict(int)
|
||||
|
||||
|
||||
class _UtilityRegistrations:
|
||||
|
||||
def __init__(self, utilities, utility_registrations):
|
||||
# {provided -> {component: count}}
|
||||
self._cache = defaultdict(_defaultdict_int)
|
||||
self._utilities = utilities
|
||||
self._utility_registrations = utility_registrations
|
||||
|
||||
self.__populate_cache()
|
||||
|
||||
def __populate_cache(self):
|
||||
for ((p, _), data) in iter(self._utility_registrations.items()):
|
||||
component = data[0]
|
||||
self.__cache_utility(p, component)
|
||||
|
||||
def __cache_utility(self, provided, component):
|
||||
try:
|
||||
self._cache[provided][component] += 1
|
||||
except TypeError:
|
||||
# The component is not hashable, and we have a dict. Switch to a
|
||||
# strategy that doesn't use hashing.
|
||||
prov = self._cache[provided] = _UnhashableComponentCounter(
|
||||
self._cache[provided]
|
||||
)
|
||||
prov[component] += 1
|
||||
|
||||
def __uncache_utility(self, provided, component):
|
||||
provided = self._cache[provided]
|
||||
# It seems like this line could raise a TypeError if component isn't
|
||||
# hashable and we haven't yet switched to _UnhashableComponentCounter.
|
||||
# However, we can't actually get in that situation. In order to get
|
||||
# here, we would have had to cache the utility already which would
|
||||
# have switched the datastructure if needed.
|
||||
count = provided[component]
|
||||
count -= 1
|
||||
if count == 0:
|
||||
del provided[component]
|
||||
else:
|
||||
provided[component] = count
|
||||
return count > 0
|
||||
|
||||
def _is_utility_subscribed(self, provided, component):
|
||||
try:
|
||||
return self._cache[provided][component] > 0
|
||||
except TypeError:
|
||||
# Not hashable and we're still using a dict
|
||||
return False
|
||||
|
||||
def registerUtility(self, provided, name, component, info, factory):
|
||||
subscribed = self._is_utility_subscribed(provided, component)
|
||||
|
||||
self._utility_registrations[
|
||||
(provided, name)
|
||||
] = component, info, factory
|
||||
self._utilities.register((), provided, name, component)
|
||||
|
||||
if not subscribed:
|
||||
self._utilities.subscribe((), provided, component)
|
||||
|
||||
self.__cache_utility(provided, component)
|
||||
|
||||
def unregisterUtility(self, provided, name, component):
|
||||
del self._utility_registrations[(provided, name)]
|
||||
self._utilities.unregister((), provided, name)
|
||||
|
||||
subscribed = self.__uncache_utility(provided, component)
|
||||
|
||||
if not subscribed:
|
||||
self._utilities.unsubscribe((), provided, component)
|
||||
|
||||
|
||||
@implementer(IComponents)
|
||||
class Components:
|
||||
|
||||
_v_utility_registrations_cache = None
|
||||
|
||||
def __init__(self, name='', bases=()):
|
||||
# __init__ is used for test cleanup as well as initialization.
|
||||
# XXX add a separate API for test cleanup.
|
||||
assert isinstance(name, str)
|
||||
self.__name__ = name
|
||||
self._init_registries()
|
||||
self._init_registrations()
|
||||
self.__bases__ = tuple(bases)
|
||||
self._v_utility_registrations_cache = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__} {self.__name__}>"
|
||||
|
||||
def __reduce__(self):
|
||||
# Mimic what a persistent.Persistent object does and elide
|
||||
# _v_ attributes so that they don't get saved in ZODB.
|
||||
# This allows us to store things that cannot be pickled in such
|
||||
# attributes.
|
||||
reduction = super().__reduce__()
|
||||
# (callable, args, state, listiter, dictiter)
|
||||
# We assume the state is always a dict; the last three items
|
||||
# are technically optional and can be missing or None.
|
||||
filtered_state = {k: v for k, v in reduction[2].items()
|
||||
if not k.startswith('_v_')}
|
||||
reduction = list(reduction)
|
||||
reduction[2] = filtered_state
|
||||
return tuple(reduction)
|
||||
|
||||
def _init_registries(self):
|
||||
# Subclasses have never been required to call this method
|
||||
# if they override it, merely to fill in these two attributes.
|
||||
self.adapters = AdapterRegistry()
|
||||
self.utilities = AdapterRegistry()
|
||||
|
||||
def _init_registrations(self):
|
||||
self._utility_registrations = {}
|
||||
self._adapter_registrations = {}
|
||||
self._subscription_registrations = []
|
||||
self._handler_registrations = []
|
||||
|
||||
@property
|
||||
def _utility_registrations_cache(self):
|
||||
# We use a _v_ attribute internally so that data aren't saved in ZODB,
|
||||
# because this object cannot be pickled.
|
||||
cache = self._v_utility_registrations_cache
|
||||
if (
|
||||
cache is None or
|
||||
cache._utilities is not self.utilities or
|
||||
cache._utility_registrations is not self._utility_registrations
|
||||
):
|
||||
cache = self._v_utility_registrations_cache = (
|
||||
_UtilityRegistrations(
|
||||
self.utilities, self._utility_registrations,
|
||||
)
|
||||
)
|
||||
return cache
|
||||
|
||||
def _getBases(self):
|
||||
# Subclasses might override
|
||||
return self.__dict__.get('__bases__', ())
|
||||
|
||||
def _setBases(self, bases):
|
||||
# Subclasses might override
|
||||
self.adapters.__bases__ = tuple([
|
||||
base.adapters for base in bases])
|
||||
self.utilities.__bases__ = tuple([
|
||||
base.utilities for base in bases])
|
||||
self.__dict__['__bases__'] = tuple(bases)
|
||||
|
||||
__bases__ = property(
|
||||
lambda self: self._getBases(),
|
||||
lambda self, bases: self._setBases(bases),
|
||||
)
|
||||
|
||||
def registerUtility(self, component=None, provided=None, name='',
|
||||
info='', event=True, factory=None):
|
||||
if factory:
|
||||
if component:
|
||||
raise TypeError("Can't specify factory and component.")
|
||||
component = factory()
|
||||
|
||||
if provided is None:
|
||||
provided = _getUtilityProvided(component)
|
||||
|
||||
if name == '':
|
||||
name = _getName(component)
|
||||
|
||||
reg = self._utility_registrations.get((provided, name))
|
||||
if reg is not None:
|
||||
if reg[:2] == (component, info):
|
||||
# already registered
|
||||
return
|
||||
self.unregisterUtility(reg[0], provided, name)
|
||||
|
||||
self._utility_registrations_cache.registerUtility(
|
||||
provided, name, component, info, factory)
|
||||
|
||||
if event:
|
||||
notify(Registered(
|
||||
UtilityRegistration(
|
||||
self, provided, name, component, info, factory)
|
||||
))
|
||||
|
||||
def unregisterUtility(self, component=None, provided=None, name='',
|
||||
factory=None):
|
||||
if factory:
|
||||
if component:
|
||||
raise TypeError("Can't specify factory and component.")
|
||||
component = factory()
|
||||
|
||||
if provided is None:
|
||||
if component is None:
|
||||
raise TypeError("Must specify one of component, factory and "
|
||||
"provided")
|
||||
provided = _getUtilityProvided(component)
|
||||
|
||||
old = self._utility_registrations.get((provided, name))
|
||||
if (old is None) or ((component is not None) and
|
||||
(component != old[0])):
|
||||
return False
|
||||
|
||||
if component is None:
|
||||
component = old[0]
|
||||
|
||||
# Note that component is now the old thing registered
|
||||
self._utility_registrations_cache.unregisterUtility(
|
||||
provided, name, component)
|
||||
|
||||
notify(Unregistered(
|
||||
UtilityRegistration(self, provided, name, component, *old[1:])
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def registeredUtilities(self):
|
||||
for ((provided, name), data
|
||||
) in iter(self._utility_registrations.items()):
|
||||
yield UtilityRegistration(self, provided, name, *data)
|
||||
|
||||
def queryUtility(self, provided, name='', default=None):
|
||||
return self.utilities.lookup((), provided, name, default)
|
||||
|
||||
def getUtility(self, provided, name=''):
|
||||
utility = self.utilities.lookup((), provided, name)
|
||||
if utility is None:
|
||||
raise ComponentLookupError(provided, name)
|
||||
return utility
|
||||
|
||||
def getUtilitiesFor(self, interface):
|
||||
yield from self.utilities.lookupAll((), interface)
|
||||
|
||||
def getAllUtilitiesRegisteredFor(self, interface):
|
||||
return self.utilities.subscriptions((), interface)
|
||||
|
||||
def registerAdapter(self, factory, required=None, provided=None,
|
||||
name='', info='', event=True):
|
||||
if provided is None:
|
||||
provided = _getAdapterProvided(factory)
|
||||
required = _getAdapterRequired(factory, required)
|
||||
if name == '':
|
||||
name = _getName(factory)
|
||||
self._adapter_registrations[(required, provided, name)
|
||||
] = factory, info
|
||||
self.adapters.register(required, provided, name, factory)
|
||||
|
||||
if event:
|
||||
notify(Registered(
|
||||
AdapterRegistration(
|
||||
self, required, provided, name, factory, info
|
||||
)
|
||||
))
|
||||
|
||||
def unregisterAdapter(self, factory=None,
|
||||
required=None, provided=None, name='',
|
||||
):
|
||||
if provided is None:
|
||||
if factory is None:
|
||||
raise TypeError("Must specify one of factory and provided")
|
||||
provided = _getAdapterProvided(factory)
|
||||
|
||||
if (required is None) and (factory is None):
|
||||
raise TypeError("Must specify one of factory and required")
|
||||
|
||||
required = _getAdapterRequired(factory, required)
|
||||
old = self._adapter_registrations.get((required, provided, name))
|
||||
if (old is None) or ((factory is not None) and
|
||||
(factory != old[0])):
|
||||
return False
|
||||
|
||||
del self._adapter_registrations[(required, provided, name)]
|
||||
self.adapters.unregister(required, provided, name)
|
||||
|
||||
notify(Unregistered(
|
||||
AdapterRegistration(self, required, provided, name, *old)
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def registeredAdapters(self):
|
||||
for ((required, provided, name), (component, info)
|
||||
) in iter(self._adapter_registrations.items()):
|
||||
yield AdapterRegistration(self, required, provided, name,
|
||||
component, info)
|
||||
|
||||
def queryAdapter(self, object, interface, name='', default=None):
|
||||
return self.adapters.queryAdapter(object, interface, name, default)
|
||||
|
||||
def getAdapter(self, object, interface, name=''):
|
||||
adapter = self.adapters.queryAdapter(object, interface, name)
|
||||
if adapter is None:
|
||||
raise ComponentLookupError(object, interface, name)
|
||||
return adapter
|
||||
|
||||
def queryMultiAdapter(self, objects, interface, name='',
|
||||
default=None):
|
||||
return self.adapters.queryMultiAdapter(
|
||||
objects, interface, name, default)
|
||||
|
||||
def getMultiAdapter(self, objects, interface, name=''):
|
||||
adapter = self.adapters.queryMultiAdapter(objects, interface, name)
|
||||
if adapter is None:
|
||||
raise ComponentLookupError(objects, interface, name)
|
||||
return adapter
|
||||
|
||||
def getAdapters(self, objects, provided):
|
||||
for name, factory in self.adapters.lookupAll(
|
||||
list(map(providedBy, objects)), provided,
|
||||
):
|
||||
adapter = factory(*objects)
|
||||
if adapter is not None:
|
||||
yield name, adapter
|
||||
|
||||
def registerSubscriptionAdapter(self,
|
||||
factory, required=None, provided=None,
|
||||
name='', info='',
|
||||
event=True):
|
||||
if name:
|
||||
raise TypeError("Named subscribers are not yet supported")
|
||||
if provided is None:
|
||||
provided = _getAdapterProvided(factory)
|
||||
required = _getAdapterRequired(factory, required)
|
||||
self._subscription_registrations.append(
|
||||
(required, provided, name, factory, info)
|
||||
)
|
||||
self.adapters.subscribe(required, provided, factory)
|
||||
|
||||
if event:
|
||||
notify(Registered(
|
||||
SubscriptionRegistration(
|
||||
self, required, provided, name, factory, info,
|
||||
)
|
||||
))
|
||||
|
||||
def registeredSubscriptionAdapters(self):
|
||||
for data in self._subscription_registrations:
|
||||
yield SubscriptionRegistration(self, *data)
|
||||
|
||||
def unregisterSubscriptionAdapter(
|
||||
self, factory=None, required=None, provided=None, name='',
|
||||
):
|
||||
if name:
|
||||
raise TypeError("Named subscribers are not yet supported")
|
||||
if provided is None:
|
||||
if factory is None:
|
||||
raise TypeError("Must specify one of factory and provided")
|
||||
provided = _getAdapterProvided(factory)
|
||||
|
||||
if (required is None) and (factory is None):
|
||||
raise TypeError("Must specify one of factory and required")
|
||||
|
||||
required = _getAdapterRequired(factory, required)
|
||||
|
||||
if factory is None:
|
||||
new = [(r, p, n, f, i)
|
||||
for (r, p, n, f, i)
|
||||
in self._subscription_registrations
|
||||
if not (r == required and p == provided)
|
||||
]
|
||||
else:
|
||||
new = [(r, p, n, f, i)
|
||||
for (r, p, n, f, i)
|
||||
in self._subscription_registrations
|
||||
if not (r == required and p == provided and f == factory)
|
||||
]
|
||||
|
||||
if len(new) == len(self._subscription_registrations):
|
||||
return False
|
||||
|
||||
self._subscription_registrations[:] = new
|
||||
self.adapters.unsubscribe(required, provided, factory)
|
||||
|
||||
notify(Unregistered(
|
||||
SubscriptionRegistration(
|
||||
self, required, provided, name, factory, '',
|
||||
)
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def subscribers(self, objects, provided):
|
||||
return self.adapters.subscribers(objects, provided)
|
||||
|
||||
def registerHandler(self,
|
||||
factory, required=None,
|
||||
name='', info='',
|
||||
event=True):
|
||||
if name:
|
||||
raise TypeError("Named handlers are not yet supported")
|
||||
required = _getAdapterRequired(factory, required)
|
||||
self._handler_registrations.append(
|
||||
(required, name, factory, info)
|
||||
)
|
||||
self.adapters.subscribe(required, None, factory)
|
||||
|
||||
if event:
|
||||
notify(Registered(
|
||||
HandlerRegistration(self, required, name, factory, info)
|
||||
))
|
||||
|
||||
def registeredHandlers(self):
|
||||
for data in self._handler_registrations:
|
||||
yield HandlerRegistration(self, *data)
|
||||
|
||||
def unregisterHandler(self, factory=None, required=None, name=''):
|
||||
if name:
|
||||
raise TypeError("Named subscribers are not yet supported")
|
||||
|
||||
if (required is None) and (factory is None):
|
||||
raise TypeError("Must specify one of factory and required")
|
||||
|
||||
required = _getAdapterRequired(factory, required)
|
||||
|
||||
if factory is None:
|
||||
new = [(r, n, f, i)
|
||||
for (r, n, f, i)
|
||||
in self._handler_registrations
|
||||
if r != required
|
||||
]
|
||||
else:
|
||||
new = [(r, n, f, i)
|
||||
for (r, n, f, i)
|
||||
in self._handler_registrations
|
||||
if not (r == required and f == factory)
|
||||
]
|
||||
|
||||
if len(new) == len(self._handler_registrations):
|
||||
return False
|
||||
|
||||
self._handler_registrations[:] = new
|
||||
self.adapters.unsubscribe(required, None, factory)
|
||||
|
||||
notify(Unregistered(
|
||||
HandlerRegistration(self, required, name, factory, '')
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def handle(self, *objects):
|
||||
self.adapters.subscribers(objects, None)
|
||||
|
||||
def rebuildUtilityRegistryFromLocalCache(self, rebuild=False):
|
||||
"""
|
||||
Emergency maintenance method to rebuild the ``.utilities``
|
||||
registry from the local copy maintained in this object, or
|
||||
detect the need to do so.
|
||||
|
||||
Most users will never need to call this, but it can be helpful
|
||||
in the event of suspected corruption.
|
||||
|
||||
By default, this method only checks for corruption. To make it
|
||||
actually rebuild the registry, pass `True` for *rebuild*.
|
||||
|
||||
:param bool rebuild: If set to `True` (not the default),
|
||||
this method will actually register and subscribe utilities
|
||||
in the registry as needed to synchronize with the local cache.
|
||||
|
||||
:return: A dictionary that's meant as diagnostic data. The keys
|
||||
and values may change over time. When called with a false
|
||||
*rebuild*, the keys ``"needed_registered"`` and
|
||||
``"needed_subscribed"`` will be non-zero if any corruption was
|
||||
detected, but that will not be corrected.
|
||||
|
||||
.. versionadded:: 5.3.0
|
||||
"""
|
||||
regs = dict(self._utility_registrations)
|
||||
utils = self.utilities
|
||||
needed_registered = 0
|
||||
did_not_register = 0
|
||||
needed_subscribed = 0
|
||||
did_not_subscribe = 0
|
||||
|
||||
# Avoid the expensive change process during this; we'll call
|
||||
# it once at the end if needed.
|
||||
assert 'changed' not in utils.__dict__
|
||||
utils.changed = lambda _: None
|
||||
|
||||
if rebuild:
|
||||
register = utils.register
|
||||
subscribe = utils.subscribe
|
||||
else:
|
||||
register = subscribe = lambda *args: None
|
||||
|
||||
try:
|
||||
for (provided, name), (value, _info, _factory) in regs.items():
|
||||
if utils.registered((), provided, name) != value:
|
||||
register((), provided, name, value)
|
||||
needed_registered += 1
|
||||
else:
|
||||
did_not_register += 1
|
||||
|
||||
if utils.subscribed((), provided, value) is None:
|
||||
needed_subscribed += 1
|
||||
subscribe((), provided, value)
|
||||
else:
|
||||
did_not_subscribe += 1
|
||||
finally:
|
||||
del utils.changed
|
||||
if rebuild and (needed_subscribed or needed_registered):
|
||||
utils.changed(utils)
|
||||
|
||||
return {
|
||||
'needed_registered': needed_registered,
|
||||
'did_not_register': did_not_register,
|
||||
'needed_subscribed': needed_subscribed,
|
||||
'did_not_subscribe': did_not_subscribe
|
||||
}
|
||||
|
||||
|
||||
def _getName(component):
|
||||
try:
|
||||
return component.__component_name__
|
||||
except AttributeError:
|
||||
return ''
|
||||
|
||||
|
||||
def _getUtilityProvided(component):
|
||||
provided = list(providedBy(component))
|
||||
if len(provided) == 1:
|
||||
return provided[0]
|
||||
raise TypeError(
|
||||
"The utility doesn't provide a single interface "
|
||||
"and no provided interface was specified."
|
||||
)
|
||||
|
||||
|
||||
def _getAdapterProvided(factory):
|
||||
provided = list(implementedBy(factory))
|
||||
if len(provided) == 1:
|
||||
return provided[0]
|
||||
raise TypeError(
|
||||
"The adapter factory doesn't implement a single interface "
|
||||
"and no provided interface was specified."
|
||||
)
|
||||
|
||||
|
||||
def _getAdapterRequired(factory, required):
|
||||
if required is None:
|
||||
try:
|
||||
required = factory.__component_adapts__
|
||||
except AttributeError:
|
||||
raise TypeError(
|
||||
"The adapter factory doesn't have a __component_adapts__ "
|
||||
"attribute and no required specifications were specified"
|
||||
)
|
||||
elif ISpecification.providedBy(required):
|
||||
raise TypeError(
|
||||
"the required argument should be a list of "
|
||||
"interfaces, not a single interface"
|
||||
)
|
||||
|
||||
result = []
|
||||
for r in required:
|
||||
if r is None:
|
||||
r = Interface
|
||||
elif not ISpecification.providedBy(r):
|
||||
if isinstance(r, type):
|
||||
r = implementedBy(r)
|
||||
else:
|
||||
raise TypeError(
|
||||
"Required specification must be a "
|
||||
"specification or class, not %r" % type(r)
|
||||
)
|
||||
result.append(r)
|
||||
return tuple(result)
|
||||
|
||||
|
||||
@implementer(IUtilityRegistration)
|
||||
class UtilityRegistration:
|
||||
|
||||
def __init__(self, registry, provided, name, component, doc, factory=None):
|
||||
self.registry = registry
|
||||
self.provided = provided
|
||||
self.name = name
|
||||
self.component = component
|
||||
self.info = doc
|
||||
self.factory = factory
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({!r}, {}, {!r}, {}, {!r}, {!r})'.format(
|
||||
self.__class__.__name__,
|
||||
self.registry,
|
||||
getattr(self.provided, '__name__', None), self.name,
|
||||
getattr(self.component, '__name__', repr(self.component)),
|
||||
self.factory, self.info,
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return id(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return repr(self) == repr(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return repr(self) != repr(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return repr(self) < repr(other)
|
||||
|
||||
def __le__(self, other):
|
||||
return repr(self) <= repr(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
return repr(self) > repr(other)
|
||||
|
||||
def __ge__(self, other):
|
||||
return repr(self) >= repr(other)
|
||||
|
||||
|
||||
@implementer(IAdapterRegistration)
|
||||
class AdapterRegistration:
|
||||
|
||||
def __init__(self, registry, required, provided, name, component, doc):
|
||||
(self.registry, self.required, self.provided, self.name,
|
||||
self.factory, self.info
|
||||
) = registry, required, provided, name, component, doc
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({!r}, {}, {}, {!r}, {}, {!r})'.format(
|
||||
self.__class__.__name__,
|
||||
self.registry,
|
||||
'[' + ", ".join([r.__name__ for r in self.required]) + ']',
|
||||
getattr(self.provided, '__name__', None), self.name,
|
||||
getattr(self.factory, '__name__', repr(self.factory)), self.info,
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return id(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return repr(self) == repr(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return repr(self) != repr(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return repr(self) < repr(other)
|
||||
|
||||
def __le__(self, other):
|
||||
return repr(self) <= repr(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
return repr(self) > repr(other)
|
||||
|
||||
def __ge__(self, other):
|
||||
return repr(self) >= repr(other)
|
||||
|
||||
|
||||
@implementer_only(ISubscriptionAdapterRegistration)
|
||||
class SubscriptionRegistration(AdapterRegistration):
|
||||
pass
|
||||
|
||||
|
||||
@implementer_only(IHandlerRegistration)
|
||||
class HandlerRegistration(AdapterRegistration):
|
||||
|
||||
def __init__(self, registry, required, name, handler, doc):
|
||||
(self.registry, self.required, self.name, self.handler, self.info
|
||||
) = registry, required, name, handler, doc
|
||||
|
||||
@property
|
||||
def factory(self):
|
||||
return self.handler
|
||||
|
||||
provided = None
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({!r}, {}, {!r}, {}, {!r})'.format(
|
||||
self.__class__.__name__,
|
||||
self.registry,
|
||||
'[' + ", ".join([r.__name__ for r in self.required]) + ']',
|
||||
self.name,
|
||||
getattr(self.factory, '__name__', repr(self.factory)), self.info,
|
||||
)
|
||||
722
.venv/lib/python3.12/site-packages/zope/interface/ro.py
Normal file
722
.venv/lib/python3.12/site-packages/zope/interface/ro.py
Normal file
@@ -0,0 +1,722 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""
|
||||
Compute a resolution order for an object and its bases.
|
||||
|
||||
.. versionchanged:: 5.0
|
||||
The resolution order is now based on the same C3 order that Python
|
||||
uses for classes. In complex instances of multiple inheritance, this
|
||||
may result in a different ordering.
|
||||
|
||||
In older versions, the ordering wasn't required to be C3 compliant,
|
||||
and for backwards compatibility, it still isn't. If the ordering
|
||||
isn't C3 compliant (if it is *inconsistent*), zope.interface will
|
||||
make a best guess to try to produce a reasonable resolution order.
|
||||
Still (just as before), the results in such cases may be
|
||||
surprising.
|
||||
|
||||
.. rubric:: Environment Variables
|
||||
|
||||
Due to the change in 5.0, certain environment variables can be used to control
|
||||
errors and warnings about inconsistent resolution orders. They are listed in
|
||||
priority order, with variables at the bottom generally overriding variables
|
||||
above them.
|
||||
|
||||
ZOPE_INTERFACE_WARN_BAD_IRO
|
||||
If this is set to "1", then if there is at least one inconsistent
|
||||
resolution order discovered, a warning
|
||||
(:class:`InconsistentResolutionOrderWarning`) will be issued. Use the
|
||||
usual warning mechanisms to control this behaviour. The warning text will
|
||||
contain additional information on debugging.
|
||||
|
||||
ZOPE_INTERFACE_TRACK_BAD_IRO
|
||||
If this is set to "1", then zope.interface will log information about each
|
||||
inconsistent resolution order discovered, and keep those details in memory
|
||||
in this module for later inspection.
|
||||
|
||||
ZOPE_INTERFACE_STRICT_IRO
|
||||
If this is set to "1", any attempt to use :func:`ro` that would produce a
|
||||
non-C3 ordering will fail by raising
|
||||
:class:`InconsistentResolutionOrderError`.
|
||||
|
||||
.. important::
|
||||
|
||||
``ZOPE_INTERFACE_STRICT_IRO`` is intended to become the default in the
|
||||
future.
|
||||
|
||||
There are two environment variables that are independent.
|
||||
|
||||
ZOPE_INTERFACE_LOG_CHANGED_IRO
|
||||
If this is set to "1", then if the C3 resolution order is different from
|
||||
the legacy resolution order for any given object, a message explaining the
|
||||
differences will be logged. This is intended to be used for debugging
|
||||
complicated IROs.
|
||||
|
||||
ZOPE_INTERFACE_USE_LEGACY_IRO
|
||||
If this is set to "1", then the C3 resolution order will *not* be used.
|
||||
The legacy IRO will be used instead. This is a temporary measure and will
|
||||
be removed in the future. It is intended to help during the transition.
|
||||
It implies ``ZOPE_INTERFACE_LOG_CHANGED_IRO``.
|
||||
|
||||
.. rubric:: Debugging Behaviour Changes in zope.interface 5
|
||||
|
||||
Most behaviour changes from zope.interface 4 to 5 are related to
|
||||
inconsistent resolution orders. ``ZOPE_INTERFACE_STRICT_IRO`` is the
|
||||
most effective tool to find such inconsistent resolution orders, and
|
||||
we recommend running your code with this variable set if at all
|
||||
possible. Doing so will ensure that all interface resolution orders
|
||||
are consistent, and if they're not, will immediately point the way to
|
||||
where this is violated.
|
||||
|
||||
Occasionally, however, this may not be enough. This is because in some
|
||||
cases, a C3 ordering can be found (the resolution order is fully
|
||||
consistent) that is substantially different from the ad-hoc legacy
|
||||
ordering. In such cases, you may find that you get an unexpected value
|
||||
returned when adapting one or more objects to an interface. To debug
|
||||
this, *also* enable ``ZOPE_INTERFACE_LOG_CHANGED_IRO`` and examine the
|
||||
output. The main thing to look for is changes in the relative
|
||||
positions of interfaces for which there are registered adapters.
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
import warnings
|
||||
|
||||
|
||||
__all__ = [
|
||||
'ro',
|
||||
'InconsistentResolutionOrderError',
|
||||
'InconsistentResolutionOrderWarning',
|
||||
]
|
||||
|
||||
__logger = None
|
||||
|
||||
|
||||
def _logger():
|
||||
global __logger # pylint:disable=global-statement
|
||||
if __logger is None:
|
||||
import logging
|
||||
__logger = logging.getLogger(__name__)
|
||||
return __logger
|
||||
|
||||
|
||||
def _legacy_mergeOrderings(orderings):
|
||||
"""Merge multiple orderings so that within-ordering order is preserved
|
||||
|
||||
Orderings are constrained in such a way that if an object appears
|
||||
in two or more orderings, then the suffix that begins with the
|
||||
object must be in both orderings.
|
||||
|
||||
For example:
|
||||
|
||||
>>> _legacy_mergeOrderings([
|
||||
... ['x', 'y', 'z'],
|
||||
... ['q', 'z'],
|
||||
... [1, 3, 5],
|
||||
... ['z']
|
||||
... ])
|
||||
['x', 'y', 'q', 1, 3, 5, 'z']
|
||||
|
||||
"""
|
||||
|
||||
seen = set()
|
||||
result = []
|
||||
for ordering in reversed(orderings):
|
||||
for o in reversed(ordering):
|
||||
if o not in seen:
|
||||
seen.add(o)
|
||||
result.insert(0, o)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _legacy_flatten(begin):
|
||||
result = [begin]
|
||||
i = 0
|
||||
for ob in iter(result):
|
||||
i += 1
|
||||
# The recursive calls can be avoided by inserting the base classes
|
||||
# into the dynamically growing list directly after the currently
|
||||
# considered object; the iterator makes sure this will keep working
|
||||
# in the future, since it cannot rely on the length of the list
|
||||
# by definition.
|
||||
result[i:i] = ob.__bases__
|
||||
return result
|
||||
|
||||
|
||||
def _legacy_ro(ob):
|
||||
return _legacy_mergeOrderings([_legacy_flatten(ob)])
|
||||
|
||||
###
|
||||
# Compare base objects using identity, not equality. This matches what
|
||||
# the CPython MRO algorithm does, and is *much* faster to boot: that,
|
||||
# plus some other small tweaks makes the difference between 25s and 6s
|
||||
# in loading 446 plone/zope interface.py modules (1925 InterfaceClass,
|
||||
# 1200 Implements, 1100 ClassProvides objects)
|
||||
###
|
||||
|
||||
|
||||
class InconsistentResolutionOrderWarning(PendingDeprecationWarning):
|
||||
"""
|
||||
The warning issued when an invalid IRO is requested.
|
||||
"""
|
||||
|
||||
|
||||
class InconsistentResolutionOrderError(TypeError):
|
||||
"""
|
||||
The error raised when an invalid IRO is requested in strict mode.
|
||||
"""
|
||||
|
||||
def __init__(self, c3, base_tree_remaining):
|
||||
self.C = c3.leaf
|
||||
base_tree = c3.base_tree
|
||||
self.base_ros = {
|
||||
base: base_tree[i + 1]
|
||||
for i, base in enumerate(self.C.__bases__)
|
||||
}
|
||||
# Unfortunately, this doesn't necessarily directly match
|
||||
# up to any transformation on C.__bases__, because
|
||||
# if any were fully used up, they were removed already.
|
||||
self.base_tree_remaining = base_tree_remaining
|
||||
|
||||
TypeError.__init__(self)
|
||||
|
||||
def __str__(self):
|
||||
import pprint
|
||||
return (
|
||||
"{}: For object {!r}.\nBase ROs:\n{}\nConflict Location:\n{}"
|
||||
).format(
|
||||
self.__class__.__name__,
|
||||
self.C,
|
||||
pprint.pformat(self.base_ros),
|
||||
pprint.pformat(self.base_tree_remaining),
|
||||
)
|
||||
|
||||
|
||||
class _NamedBool(int): # cannot actually inherit bool
|
||||
|
||||
def __new__(cls, val, name):
|
||||
inst = super(cls, _NamedBool).__new__(cls, val)
|
||||
inst.__name__ = name
|
||||
return inst
|
||||
|
||||
|
||||
class _ClassBoolFromEnv:
|
||||
"""
|
||||
Non-data descriptor that reads a transformed environment variable
|
||||
as a boolean, and caches the result in the class.
|
||||
"""
|
||||
|
||||
def __get__(self, inst, klass):
|
||||
import os
|
||||
for cls in klass.__mro__:
|
||||
my_name = None
|
||||
for k in dir(klass):
|
||||
if k in cls.__dict__ and cls.__dict__[k] is self:
|
||||
my_name = k
|
||||
break
|
||||
if my_name is not None:
|
||||
break
|
||||
else: # pragma: no cover
|
||||
raise RuntimeError("Unable to find self")
|
||||
|
||||
env_name = 'ZOPE_INTERFACE_' + my_name
|
||||
val = os***REMOVED***iron.get(env_name, '') == '1'
|
||||
val = _NamedBool(val, my_name)
|
||||
setattr(klass, my_name, val)
|
||||
setattr(klass, 'ORIG_' + my_name, self)
|
||||
return val
|
||||
|
||||
|
||||
class _StaticMRO:
|
||||
# A previously resolved MRO, supplied by the caller.
|
||||
# Used in place of calculating it.
|
||||
|
||||
had_inconsistency = None # We don't know...
|
||||
|
||||
def __init__(self, C, mro):
|
||||
self.leaf = C
|
||||
self.__mro = tuple(mro)
|
||||
|
||||
def mro(self):
|
||||
return list(self.__mro)
|
||||
|
||||
|
||||
_INCONSISTENT_RESOLUTION_ORDER = """\
|
||||
An inconsistent resolution order is being requested. (Interfaces should
|
||||
follow the Python class rules known as C3.) For backwards compatibility,
|
||||
zope.interface will allow this, making the best guess it can to produce as
|
||||
meaningful an order as possible. In the future this might be an error. Set
|
||||
the warning filter to error, or set the environment variable
|
||||
'ZOPE_INTERFACE_TRACK_BAD_IRO' to '1' and examine ro.C3.BAD_IROS to debug, or
|
||||
set 'ZOPE_INTERFACE_STRICT_IRO' to raise exceptions."""
|
||||
|
||||
|
||||
class C3:
|
||||
# Holds the shared state during computation of an MRO.
|
||||
|
||||
@staticmethod
|
||||
def resolver(C, strict, base_mros):
|
||||
strict = strict if strict is not None else C3.STRICT_IRO
|
||||
factory = C3
|
||||
if strict:
|
||||
factory = _StrictC3
|
||||
elif C3.TRACK_BAD_IRO:
|
||||
factory = _TrackingC3
|
||||
|
||||
memo = {}
|
||||
base_mros = base_mros or {}
|
||||
for base, mro in base_mros.items():
|
||||
assert base in C.__bases__
|
||||
memo[base] = _StaticMRO(base, mro)
|
||||
|
||||
return factory(C, memo)
|
||||
|
||||
__mro = None
|
||||
__legacy_ro = None
|
||||
direct_inconsistency = False
|
||||
|
||||
def __init__(self, C, memo):
|
||||
self.leaf = C
|
||||
self.memo = memo
|
||||
kind = self.__class__
|
||||
|
||||
base_resolvers = []
|
||||
for base in C.__bases__:
|
||||
if base not in memo:
|
||||
resolver = kind(base, memo)
|
||||
memo[base] = resolver
|
||||
base_resolvers.append(memo[base])
|
||||
|
||||
self.base_tree = [
|
||||
[C]
|
||||
] + [
|
||||
memo[base].mro() for base in C.__bases__
|
||||
] + [
|
||||
list(C.__bases__)
|
||||
]
|
||||
|
||||
self.bases_had_inconsistency = any(
|
||||
base.had_inconsistency for base in base_resolvers
|
||||
)
|
||||
|
||||
if len(C.__bases__) == 1:
|
||||
self.__mro = [C] + memo[C.__bases__[0]].mro()
|
||||
|
||||
@property
|
||||
def had_inconsistency(self):
|
||||
return self.direct_inconsistency or self.bases_had_inconsistency
|
||||
|
||||
@property
|
||||
def legacy_ro(self):
|
||||
if self.__legacy_ro is None:
|
||||
self.__legacy_ro = tuple(_legacy_ro(self.leaf))
|
||||
return list(self.__legacy_ro)
|
||||
|
||||
TRACK_BAD_IRO = _ClassBoolFromEnv()
|
||||
STRICT_IRO = _ClassBoolFromEnv()
|
||||
WARN_BAD_IRO = _ClassBoolFromEnv()
|
||||
LOG_CHANGED_IRO = _ClassBoolFromEnv()
|
||||
USE_LEGACY_IRO = _ClassBoolFromEnv()
|
||||
BAD_IROS = ()
|
||||
|
||||
def _warn_iro(self):
|
||||
if not self.WARN_BAD_IRO:
|
||||
# For the initial release, one must opt-in to see the warning.
|
||||
# In the future (2021?) seeing at least the first warning will
|
||||
# be the default
|
||||
return
|
||||
warnings.warn(
|
||||
_INCONSISTENT_RESOLUTION_ORDER,
|
||||
InconsistentResolutionOrderWarning,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _can_choose_base(base, base_tree_remaining):
|
||||
# From C3:
|
||||
# nothead = [s for s in nonemptyseqs if cand in s[1:]]
|
||||
for bases in base_tree_remaining:
|
||||
if not bases or bases[0] is base:
|
||||
continue
|
||||
|
||||
for b in bases:
|
||||
if b is base:
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _nonempty_bases_ignoring(base_tree, ignoring):
|
||||
return list(filter(None, [
|
||||
[b for b in bases if b is not ignoring]
|
||||
for bases
|
||||
in base_tree
|
||||
]))
|
||||
|
||||
def _choose_next_base(self, base_tree_remaining):
|
||||
"""
|
||||
Return the next base.
|
||||
|
||||
The return value will either fit the C3 constraints or be our best
|
||||
guess about what to do. If we cannot guess, this may raise an
|
||||
exception.
|
||||
"""
|
||||
base = self._find_next_C3_base(base_tree_remaining)
|
||||
if base is not None:
|
||||
return base
|
||||
return self._guess_next_base(base_tree_remaining)
|
||||
|
||||
def _find_next_C3_base(self, base_tree_remaining):
|
||||
"""Return the next base that fits the constraints
|
||||
|
||||
Return ``None`` if there isn't one.
|
||||
"""
|
||||
for bases in base_tree_remaining:
|
||||
base = bases[0]
|
||||
if self._can_choose_base(base, base_tree_remaining):
|
||||
return base
|
||||
return None
|
||||
|
||||
class _UseLegacyRO(Exception):
|
||||
pass
|
||||
|
||||
def _guess_next_base(self, base_tree_remaining):
|
||||
# Narf. We may have an inconsistent order (we won't know for
|
||||
# sure until we check all the bases). Python cannot create
|
||||
# classes like this:
|
||||
#
|
||||
# class B1:
|
||||
# pass
|
||||
# class B2(B1):
|
||||
# pass
|
||||
# class C(B1, B2): # -> TypeError; this is like saying C(B1, B2, B1).
|
||||
# pass
|
||||
#
|
||||
# However, older versions of zope.interface were fine with this order.
|
||||
# A good example is ``providedBy(IOError())``. Because of the way
|
||||
# ``classImplements`` works, it winds up with ``__bases__`` ==
|
||||
# ``[IEnvironmentError, IIOError, IOSError, <implementedBy
|
||||
# Exception>]`` (on Python 3). But ``IEnvironmentError`` is a base of
|
||||
# both ``IIOError`` and ``IOSError``. Previously, we would get a
|
||||
# resolution order of ``[IIOError, IOSError, IEnvironmentError,
|
||||
# IStandardError, IException, Interface]`` but the standard Python
|
||||
# algorithm would forbid creating that order entirely.
|
||||
|
||||
# Unlike Python's MRO, we attempt to resolve the issue. A few
|
||||
# heuristics have been tried. One was:
|
||||
#
|
||||
# Strip off the first (highest priority) base of each direct
|
||||
# base one at a time and seeing if we can come to an agreement
|
||||
# with the other bases. (We're trying for a partial ordering
|
||||
# here.) This often resolves cases (such as the IOSError case
|
||||
# above), and frequently produces the same ordering as the
|
||||
# legacy MRO did. If we looked at all the highest priority
|
||||
# bases and couldn't find any partial ordering, then we strip
|
||||
# them *all* out and begin the C3 step again. We take care not
|
||||
# to promote a common root over all others.
|
||||
#
|
||||
# If we only did the first part, stripped off the first
|
||||
# element of the first item, we could resolve simple cases.
|
||||
# But it tended to fail badly. If we did the whole thing, it
|
||||
# could be extremely painful from a performance perspective
|
||||
# for deep/wide things like Zope's OFS.SimpleItem.Item. Plus,
|
||||
# anytime you get ExtensionClass.Base into the mix, you're
|
||||
# likely to wind up in trouble, because it messes with the MRO
|
||||
# of classes. Sigh.
|
||||
#
|
||||
# So now, we fall back to the old linearization (fast to compute).
|
||||
self._warn_iro()
|
||||
self.direct_inconsistency = InconsistentResolutionOrderError(
|
||||
self, base_tree_remaining,
|
||||
)
|
||||
raise self._UseLegacyRO
|
||||
|
||||
def _merge(self):
|
||||
# Returns a merged *list*.
|
||||
result = self.__mro = []
|
||||
base_tree_remaining = self.base_tree
|
||||
base = None
|
||||
while 1:
|
||||
# Take last picked base out of the base tree wherever it is.
|
||||
# This differs slightly from the standard Python MRO and is needed
|
||||
# because we have no other step that prevents duplicates
|
||||
# from coming in (e.g., in the inconsistent fallback path)
|
||||
base_tree_remaining = self._nonempty_bases_ignoring(
|
||||
base_tree_remaining, base
|
||||
)
|
||||
|
||||
if not base_tree_remaining:
|
||||
return result
|
||||
try:
|
||||
base = self._choose_next_base(base_tree_remaining)
|
||||
except self._UseLegacyRO:
|
||||
self.__mro = self.legacy_ro
|
||||
return self.legacy_ro
|
||||
|
||||
result.append(base)
|
||||
|
||||
def mro(self):
|
||||
if self.__mro is None:
|
||||
self.__mro = tuple(self._merge())
|
||||
return list(self.__mro)
|
||||
|
||||
|
||||
class _StrictC3(C3):
|
||||
__slots__ = ()
|
||||
|
||||
def _guess_next_base(self, base_tree_remaining):
|
||||
raise InconsistentResolutionOrderError(self, base_tree_remaining)
|
||||
|
||||
|
||||
class _TrackingC3(C3):
|
||||
__slots__ = ()
|
||||
|
||||
def _guess_next_base(self, base_tree_remaining):
|
||||
import traceback
|
||||
bad_iros = C3.BAD_IROS
|
||||
if self.leaf not in bad_iros:
|
||||
if bad_iros == ():
|
||||
import weakref
|
||||
|
||||
# This is a race condition, but it doesn't matter much.
|
||||
bad_iros = C3.BAD_IROS = weakref.WeakKeyDictionary()
|
||||
bad_iros[self.leaf] = t = (
|
||||
InconsistentResolutionOrderError(self, base_tree_remaining),
|
||||
traceback.format_stack()
|
||||
)
|
||||
_logger().warning("Tracking inconsistent IRO: %s", t[0])
|
||||
return C3._guess_next_base(self, base_tree_remaining)
|
||||
|
||||
|
||||
class _ROComparison:
|
||||
# Exists to compute and print a pretty string comparison
|
||||
# for differing ROs.
|
||||
# Since we're used in a logging context, and may actually never be printed,
|
||||
# this is a class so we can defer computing the diff until asked.
|
||||
|
||||
# Components we use to build up the comparison report
|
||||
class Item:
|
||||
prefix = ' '
|
||||
|
||||
def __init__(self, item):
|
||||
self.item = item
|
||||
|
||||
def __str__(self):
|
||||
return "{}{}".format(
|
||||
self.prefix,
|
||||
self.item,
|
||||
)
|
||||
|
||||
class Deleted(Item):
|
||||
prefix = '- '
|
||||
|
||||
class Inserted(Item):
|
||||
prefix = '+ '
|
||||
|
||||
Empty = str
|
||||
|
||||
class ReplacedBy: # pragma: no cover
|
||||
prefix = '- '
|
||||
suffix = ''
|
||||
|
||||
def __init__(self, chunk, total_count):
|
||||
self.chunk = chunk
|
||||
self.total_count = total_count
|
||||
|
||||
def __iter__(self):
|
||||
lines = [
|
||||
self.prefix + str(item) + self.suffix
|
||||
for item in self.chunk
|
||||
]
|
||||
while len(lines) < self.total_count:
|
||||
lines.append('')
|
||||
|
||||
return iter(lines)
|
||||
|
||||
class Replacing(ReplacedBy):
|
||||
prefix = "+ "
|
||||
suffix = ''
|
||||
|
||||
_c3_report = None
|
||||
_legacy_report = None
|
||||
|
||||
def __init__(self, c3, c3_ro, legacy_ro):
|
||||
self.c3 = c3
|
||||
self.c3_ro = c3_ro
|
||||
self.legacy_ro = legacy_ro
|
||||
|
||||
def __move(self, from_, to_, chunk, operation):
|
||||
for x in chunk:
|
||||
to_.append(operation(x))
|
||||
from_.append(self.Empty())
|
||||
|
||||
def _generate_report(self):
|
||||
if self._c3_report is None:
|
||||
import difflib
|
||||
|
||||
# The opcodes we get describe how to turn 'a' into 'b'. So
|
||||
# the old one (legacy) needs to be first ('a')
|
||||
matcher = difflib.SequenceMatcher(None, self.legacy_ro, self.c3_ro)
|
||||
# The reports are equal length sequences. We're going for a
|
||||
# side-by-side diff.
|
||||
self._c3_report = c3_report = []
|
||||
self._legacy_report = legacy_report = []
|
||||
for opcode, leg1, leg2, c31, c32 in matcher.get_opcodes():
|
||||
c3_chunk = self.c3_ro[c31:c32]
|
||||
legacy_chunk = self.legacy_ro[leg1:leg2]
|
||||
|
||||
if opcode == 'equal':
|
||||
# Guaranteed same length
|
||||
c3_report.extend(self.Item(x) for x in c3_chunk)
|
||||
legacy_report.extend(self.Item(x) for x in legacy_chunk)
|
||||
if opcode == 'delete':
|
||||
# Guaranteed same length
|
||||
assert not c3_chunk
|
||||
self.__move(
|
||||
c3_report, legacy_report, legacy_chunk, self.Deleted,
|
||||
)
|
||||
if opcode == 'insert':
|
||||
# Guaranteed same length
|
||||
assert not legacy_chunk
|
||||
self.__move(
|
||||
legacy_report, c3_report, c3_chunk, self.Inserted,
|
||||
)
|
||||
if opcode == 'replace': # pragma: no cover
|
||||
# (How do you make it output this?)
|
||||
# Either side could be longer.
|
||||
chunk_size = max(len(c3_chunk), len(legacy_chunk))
|
||||
c3_report.extend(self.Replacing(c3_chunk, chunk_size))
|
||||
legacy_report.extend(
|
||||
self.ReplacedBy(legacy_chunk, chunk_size),
|
||||
)
|
||||
|
||||
return self._c3_report, self._legacy_report
|
||||
|
||||
@property
|
||||
def _inconsistent_label(self):
|
||||
inconsistent = []
|
||||
if self.c3.direct_inconsistency:
|
||||
inconsistent.append('direct')
|
||||
if self.c3.bases_had_inconsistency:
|
||||
inconsistent.append('bases')
|
||||
return '+'.join(inconsistent) if inconsistent else 'no'
|
||||
|
||||
def __str__(self):
|
||||
c3_report, legacy_report = self._generate_report()
|
||||
assert len(c3_report) == len(legacy_report)
|
||||
|
||||
left_lines = [str(x) for x in legacy_report]
|
||||
right_lines = [str(x) for x in c3_report]
|
||||
|
||||
# We have the same number of lines in the report; this is not
|
||||
# necessarily the same as the number of items in either RO.
|
||||
assert len(left_lines) == len(right_lines)
|
||||
|
||||
padding = ' ' * 2
|
||||
max_left = max(len(x) for x in left_lines)
|
||||
max_right = max(len(x) for x in right_lines)
|
||||
|
||||
left_title = f'Legacy RO (len={len(self.legacy_ro)})'
|
||||
|
||||
right_title = 'C3 RO (len={}; inconsistent={})'.format(
|
||||
len(self.c3_ro),
|
||||
self._inconsistent_label,
|
||||
)
|
||||
lines = [
|
||||
(
|
||||
padding +
|
||||
left_title.ljust(max_left) +
|
||||
padding +
|
||||
right_title.ljust(max_right)
|
||||
),
|
||||
padding + '=' * (max_left + len(padding) + max_right)
|
||||
]
|
||||
lines += [
|
||||
padding + left.ljust(max_left) + padding + right
|
||||
for left, right in zip(left_lines, right_lines)
|
||||
]
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
# Set to `Interface` once it is defined. This is used to
|
||||
# avoid logging false positives about changed ROs.
|
||||
_ROOT = None
|
||||
|
||||
|
||||
def ro(
|
||||
C, strict=None, base_mros=None, log_changed_ro=None, use_legacy_ro=None,
|
||||
):
|
||||
"""
|
||||
ro(C) -> list
|
||||
|
||||
Compute the precedence list (mro) according to C3.
|
||||
|
||||
:return: A fresh `list` object.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Add the *strict*, *log_changed_ro* and *use_legacy_ro*
|
||||
keyword arguments. These are provisional and likely to be
|
||||
removed in the future. They are most useful for testing.
|
||||
"""
|
||||
# The ``base_mros`` argument is for internal optimization and
|
||||
# not documented.
|
||||
resolver = C3.resolver(C, strict, base_mros)
|
||||
mro = resolver.mro()
|
||||
|
||||
log_changed = (
|
||||
log_changed_ro if log_changed_ro is not None
|
||||
else resolver.LOG_CHANGED_IRO
|
||||
)
|
||||
use_legacy = (
|
||||
use_legacy_ro if use_legacy_ro is not None
|
||||
else resolver.USE_LEGACY_IRO
|
||||
)
|
||||
|
||||
if log_changed or use_legacy:
|
||||
legacy_ro = resolver.legacy_ro
|
||||
assert isinstance(legacy_ro, list)
|
||||
assert isinstance(mro, list)
|
||||
changed = legacy_ro != mro
|
||||
if changed:
|
||||
# Did only Interface move? The fix for issue #8 made that
|
||||
# somewhat common. It's almost certainly not a problem, though,
|
||||
# so allow ignoring it.
|
||||
legacy_without_root = [x for x in legacy_ro if x is not _ROOT]
|
||||
mro_without_root = [x for x in mro if x is not _ROOT]
|
||||
changed = legacy_without_root != mro_without_root
|
||||
|
||||
if changed:
|
||||
comparison = _ROComparison(resolver, mro, legacy_ro)
|
||||
_logger().warning(
|
||||
"Object %r has different legacy and C3 MROs:\n%s",
|
||||
C, comparison
|
||||
)
|
||||
if resolver.had_inconsistency and legacy_ro == mro:
|
||||
comparison = _ROComparison(resolver, mro, legacy_ro)
|
||||
_logger().warning(
|
||||
"Object %r had inconsistent IRO and used the legacy RO:\n%s"
|
||||
"\nInconsistency entered at:\n%s",
|
||||
C, comparison, resolver.direct_inconsistency
|
||||
)
|
||||
if use_legacy:
|
||||
return legacy_ro
|
||||
|
||||
return mro
|
||||
|
||||
|
||||
def is_consistent(C):
|
||||
"""Is the resolution order for *C*, consistent according to C3.
|
||||
|
||||
Order as computed by :func:`ro`.
|
||||
"""
|
||||
return not C3.resolver(C, False, None).had_inconsistency
|
||||
@@ -0,0 +1,130 @@
|
||||
from zope.interface._compat import _should_attempt_c_optimizations
|
||||
|
||||
|
||||
class OptimizationTestMixin:
|
||||
"""Mixin testing that C optimizations are used when appropriate.
|
||||
"""
|
||||
|
||||
def _getTargetClass(self):
|
||||
"""Return the implementation in use, without 'Py' or 'Fallback' suffix.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _getFallbackClass(self):
|
||||
"""Return the fallback Python implementation.
|
||||
"""
|
||||
# Is there an algorithmic way to do this? The C
|
||||
# objects all come from the same module so I don't see how we can
|
||||
# get the Python object from that.
|
||||
raise NotImplementedError
|
||||
|
||||
def test_optimizations(self):
|
||||
used = self._getTargetClass()
|
||||
fallback = self._getFallbackClass()
|
||||
|
||||
if _should_attempt_c_optimizations():
|
||||
self.assertIsNot(used, fallback)
|
||||
else:
|
||||
self.assertIs(used, fallback)
|
||||
|
||||
|
||||
class SubclassableMixin:
|
||||
|
||||
def _getTargetClass(self):
|
||||
"""Return the implementation in use without 'Py' or 'Fallback' suffix.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def test_can_subclass(self):
|
||||
klass = self._getTargetClass()
|
||||
|
||||
class Derived(klass): # no raise
|
||||
pass
|
||||
|
||||
|
||||
class MissingSomeAttrs:
|
||||
"""
|
||||
Helper for tests that raises a specific exception
|
||||
for attributes that are missing. This is usually not
|
||||
an AttributeError, and this object is used to test that
|
||||
those errors are not improperly caught and treated like
|
||||
an AttributeError.
|
||||
"""
|
||||
|
||||
def __init__(self, exc_kind, **other_attrs):
|
||||
self.__exc_kind = exc_kind
|
||||
d = object.__getattribute__(self, '__dict__')
|
||||
d.update(other_attrs)
|
||||
|
||||
def __getattribute__(self, name):
|
||||
# Note that we ignore objects found in the class dictionary.
|
||||
d = object.__getattribute__(self, '__dict__')
|
||||
try:
|
||||
return d[name]
|
||||
except KeyError:
|
||||
raise d['_MissingSomeAttrs__exc_kind'](name)
|
||||
|
||||
EXCEPTION_CLASSES = (
|
||||
TypeError,
|
||||
RuntimeError,
|
||||
BaseException,
|
||||
ValueError,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def test_raises(cls, unittest, test_func, expected_missing, **other_attrs):
|
||||
"""
|
||||
Loop through various exceptions, calling *test_func* inside a
|
||||
``assertRaises`` block.
|
||||
|
||||
:param test_func: A callable of one argument, the instance of this
|
||||
class.
|
||||
:param str expected_missing: The attribute that should fail with the
|
||||
exception. This is used to ensure that we're testing the path we
|
||||
think we are.
|
||||
:param other_attrs: Attributes that should be provided on the test
|
||||
object. Must not contain *expected_missing*.
|
||||
"""
|
||||
assert isinstance(expected_missing, str)
|
||||
assert expected_missing not in other_attrs
|
||||
for exc in cls.EXCEPTION_CLASSES:
|
||||
ob = cls(exc, **other_attrs)
|
||||
with unittest.assertRaises(exc) as ex:
|
||||
test_func(ob)
|
||||
|
||||
unittest.assertEqual(ex.exception.args[0], expected_missing)
|
||||
|
||||
# Now test that the AttributeError for that expected_missing is *not*
|
||||
# raised.
|
||||
ob = cls(AttributeError, **other_attrs)
|
||||
try:
|
||||
test_func(ob)
|
||||
except AttributeError as e:
|
||||
unittest.assertNotIn(expected_missing, str(e))
|
||||
except Exception: # pylint:disable=broad-except
|
||||
pass
|
||||
|
||||
# Be sure cleanup functionality is available; classes that use the adapter hook
|
||||
# need to be sure to subclass ``CleanUp``.
|
||||
#
|
||||
# If zope.component is installed and imported when we run our tests
|
||||
# (import chain:
|
||||
# zope.testrunner->zope.security->zope.location->zope.component.api)
|
||||
# it adds an adapter hook that uses its global site manager. That can cause
|
||||
# leakage from one test to another unless its cleanup hooks are run. The
|
||||
# symptoms can be odd, especially if one test used C objects and the next used
|
||||
# the Python implementation. (For example, you can get strange TypeErrors or
|
||||
# find inexplicable comparisons being done.)
|
||||
|
||||
|
||||
try:
|
||||
from zope.testing import cleanup
|
||||
except ImportError:
|
||||
|
||||
class CleanUp:
|
||||
def cleanUp(self):
|
||||
pass
|
||||
|
||||
setUp = tearDown = cleanUp
|
||||
else:
|
||||
CleanUp = cleanup.CleanUp
|
||||
@@ -0,0 +1,29 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
import sys
|
||||
|
||||
from zope.interface.advice import getFrameInfo
|
||||
|
||||
|
||||
my_globals = globals()
|
||||
|
||||
ClassicClass = None
|
||||
|
||||
|
||||
class NewStyleClass:
|
||||
__metaclass__ = type
|
||||
classLevelFrameInfo = getFrameInfo(sys._getframe())
|
||||
|
||||
|
||||
moduleLevelFrameInfo = getFrameInfo(sys._getframe())
|
||||
@@ -0,0 +1,25 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
""" Dummy Module
|
||||
"""
|
||||
from zope.interface import moduleProvides
|
||||
from zope.interface.tests.idummy import IDummyModule
|
||||
|
||||
|
||||
moduleProvides(IDummyModule)
|
||||
|
||||
|
||||
def bar(baz):
|
||||
# Note: no 'self', because the module provides the interface directly.
|
||||
raise NotImplementedError()
|
||||
@@ -0,0 +1,24 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
""" Interface describing API of zope.interface.tests.dummy test module
|
||||
"""
|
||||
from zope.interface import Interface
|
||||
|
||||
|
||||
class IDummyModule(Interface):
|
||||
""" Dummy interface for unit tests.
|
||||
"""
|
||||
def bar(baz):
|
||||
""" Just a note.
|
||||
"""
|
||||
@@ -0,0 +1,28 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Test module that declares an interface
|
||||
"""
|
||||
from zope.interface import Interface
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
|
||||
class I1(Interface):
|
||||
pass
|
||||
|
||||
|
||||
class I2(Interface):
|
||||
pass
|
||||
|
||||
|
||||
moduleProvides(I1, I2)
|
||||
130
.venv/lib/python3.12/site-packages/zope/interface/tests/odd.py
Normal file
130
.venv/lib/python3.12/site-packages/zope/interface/tests/odd.py
Normal file
@@ -0,0 +1,130 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Odd meta class that doesn't subclass type.
|
||||
|
||||
This is used for testing support for ExtensionClass in new interfaces.
|
||||
|
||||
>>> class A(object):
|
||||
... __metaclass__ = MetaClass
|
||||
... a = 1
|
||||
...
|
||||
>>> A.__name__
|
||||
'A'
|
||||
>>> A.__bases__ == (object,)
|
||||
True
|
||||
>>> class B(object):
|
||||
... __metaclass__ = MetaClass
|
||||
... b = 1
|
||||
...
|
||||
>>> class C(A, B): pass
|
||||
...
|
||||
>>> C.__name__
|
||||
'C'
|
||||
>>> int(C.__bases__ == (A, B))
|
||||
1
|
||||
>>> a = A()
|
||||
>>> aa = A()
|
||||
>>> a.a
|
||||
1
|
||||
>>> aa.a
|
||||
1
|
||||
>>> aa.a = 2
|
||||
>>> a.a
|
||||
1
|
||||
>>> aa.a
|
||||
2
|
||||
>>> c = C()
|
||||
>>> c.a
|
||||
1
|
||||
>>> c.b
|
||||
1
|
||||
>>> c.b = 2
|
||||
>>> c.b
|
||||
2
|
||||
>>> C.c = 1
|
||||
>>> c.c
|
||||
1
|
||||
|
||||
>>> int(C.__class__.__class__ is C.__class__)
|
||||
1
|
||||
"""
|
||||
|
||||
# class OddClass is an odd meta class
|
||||
|
||||
|
||||
class MetaMetaClass(type):
|
||||
|
||||
def __getattribute__(cls, name):
|
||||
if name == '__class__':
|
||||
return cls
|
||||
# Under Python 3.6, __prepare__ gets requested
|
||||
return type.__getattribute__(cls, name)
|
||||
|
||||
|
||||
class MetaClass:
|
||||
"""Odd classes
|
||||
"""
|
||||
|
||||
def __init__(self, name, bases, dict):
|
||||
self.__name__ = name
|
||||
self.__bases__ = bases
|
||||
self.__dict__.update(dict)
|
||||
|
||||
def __call__(self):
|
||||
return OddInstance(self)
|
||||
|
||||
def __getattr__(self, name):
|
||||
for b in self.__bases__:
|
||||
v = getattr(b, name, self)
|
||||
if v is not self:
|
||||
return v
|
||||
raise AttributeError(name)
|
||||
|
||||
def __repr__(self): # pragma: no cover
|
||||
return f"<odd class {self.__name__} at {hex(id(self))}>"
|
||||
|
||||
|
||||
MetaClass = MetaMetaClass(
|
||||
'MetaClass',
|
||||
MetaClass.__bases__,
|
||||
{
|
||||
k: v for k, v in MetaClass.__dict__.items()
|
||||
if k not in ('__dict__',)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class OddInstance:
|
||||
|
||||
def __init__(self, cls):
|
||||
self.__dict__['__class__'] = cls
|
||||
|
||||
def __getattribute__(self, name):
|
||||
dict = object.__getattribute__(self, '__dict__')
|
||||
if name == '__dict__':
|
||||
return dict
|
||||
v = dict.get(name, self)
|
||||
if v is not self:
|
||||
return v
|
||||
return getattr(dict['__class__'], name)
|
||||
|
||||
def __setattr__(self, name, v):
|
||||
self.__dict__[name] = v
|
||||
|
||||
def __delattr__(self, name):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __repr__(self): # pragma: no cover
|
||||
return "<odd {} instance at {}>".format(
|
||||
self.__class__.__name__, hex(id(self)))
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,217 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Tests for advice
|
||||
|
||||
This module was adapted from 'protocols.tests.advice', part of the Python
|
||||
Enterprise Application Kit (PEAK). Please notify the PEAK authors
|
||||
(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or
|
||||
Zope-specific changes are required, so that the PEAK version of this module
|
||||
can be kept in sync.
|
||||
|
||||
PEAK is a Python application framework that interoperates with (but does
|
||||
not require) Zope 3 and Twisted. It provides tools for manipulating UML
|
||||
models, object-relational persistence, aspect-oriented programming, and more.
|
||||
Visit the PEAK home page at http://peak.telecommunity.com for more information.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
|
||||
class FrameInfoTest(unittest.TestCase):
|
||||
|
||||
def test_w_module(self):
|
||||
from zope.interface.tests import advisory_testing
|
||||
(kind, module,
|
||||
f_locals, f_globals) = advisory_testing.moduleLevelFrameInfo
|
||||
self.assertEqual(kind, "module")
|
||||
for d in module.__dict__, f_locals, f_globals:
|
||||
self.assertIs(d, advisory_testing.my_globals)
|
||||
|
||||
def test_w_class(self):
|
||||
from zope.interface.tests import advisory_testing
|
||||
(kind,
|
||||
module,
|
||||
f_locals,
|
||||
f_globals) = advisory_testing.NewStyleClass.classLevelFrameInfo
|
||||
self.assertEqual(kind, "class")
|
||||
|
||||
for d in module.__dict__, f_globals:
|
||||
self.assertIs(d, advisory_testing.my_globals)
|
||||
|
||||
def test_inside_function_call(self):
|
||||
from zope.interface.advice import getFrameInfo
|
||||
kind, module, f_locals, f_globals = getFrameInfo(sys._getframe())
|
||||
self.assertEqual(kind, "function call")
|
||||
|
||||
frame = sys._getframe()
|
||||
self.assertEqual(f_locals, frame.f_locals)
|
||||
self.assertEqual(f_locals, locals())
|
||||
|
||||
for d in module.__dict__, f_globals:
|
||||
self.assertIs(d, globals())
|
||||
|
||||
def test_inside_exec(self):
|
||||
from zope.interface.advice import getFrameInfo
|
||||
_globals = {'getFrameInfo': getFrameInfo}
|
||||
_locals = {}
|
||||
exec(_FUNKY_EXEC, _globals, _locals)
|
||||
self.assertEqual(_locals['kind'], "exec")
|
||||
self.assertIs(_locals['f_locals'], _locals)
|
||||
self.assertIsNone(_locals['module'])
|
||||
self.assertIs(_locals['f_globals'], _globals)
|
||||
|
||||
|
||||
_FUNKY_EXEC = """\
|
||||
import sys
|
||||
kind, module, f_locals, f_globals = getFrameInfo(sys._getframe())
|
||||
"""
|
||||
|
||||
|
||||
class Test_isClassAdvisor(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, *args, **kw):
|
||||
from zope.interface.advice import isClassAdvisor
|
||||
return isClassAdvisor(*args, **kw)
|
||||
|
||||
def test_w_non_function(self):
|
||||
self.assertEqual(self._callFUT(self), False)
|
||||
|
||||
def test_w_normal_function(self):
|
||||
|
||||
def foo():
|
||||
raise NotImplementedError()
|
||||
|
||||
self.assertEqual(self._callFUT(foo), False)
|
||||
|
||||
def test_w_advisor_function(self):
|
||||
|
||||
def bar():
|
||||
raise NotImplementedError()
|
||||
|
||||
bar.previousMetaclass = object()
|
||||
self.assertEqual(self._callFUT(bar), True)
|
||||
|
||||
|
||||
class Test_determineMetaclass(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, *args, **kw):
|
||||
from zope.interface.advice import determineMetaclass
|
||||
return determineMetaclass(*args, **kw)
|
||||
|
||||
def test_empty_w_explicit_metatype(self):
|
||||
|
||||
class Meta(type):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT((), Meta), Meta)
|
||||
|
||||
def test_single(self):
|
||||
|
||||
class Meta(type):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT((Meta,)), type)
|
||||
|
||||
def test_meta_of_class(self):
|
||||
|
||||
class Metameta(type):
|
||||
pass
|
||||
|
||||
class Meta(type, metaclass=Metameta):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT((Meta, type)), Metameta)
|
||||
|
||||
def test_multiple_in_hierarchy_py3k(self):
|
||||
|
||||
class Meta_A(type):
|
||||
pass
|
||||
|
||||
class Meta_B(Meta_A):
|
||||
pass
|
||||
|
||||
class A(type, metaclass=Meta_A):
|
||||
pass
|
||||
|
||||
class B(type, metaclass=Meta_B):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT((A, B)), Meta_B)
|
||||
|
||||
def test_multiple_not_in_hierarchy_py3k(self):
|
||||
|
||||
class Meta_A(type):
|
||||
pass
|
||||
|
||||
class Meta_B(type):
|
||||
pass
|
||||
|
||||
class A(type, metaclass=Meta_A):
|
||||
pass
|
||||
|
||||
class B(type, metaclass=Meta_B):
|
||||
pass
|
||||
|
||||
self.assertRaises(TypeError, self._callFUT, (A, B))
|
||||
|
||||
|
||||
class Test_minimalBases(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, klasses):
|
||||
from zope.interface.advice import minimalBases
|
||||
return minimalBases(klasses)
|
||||
|
||||
def test_empty(self):
|
||||
self.assertEqual(self._callFUT([]), [])
|
||||
|
||||
def test_w_newstyle_meta(self):
|
||||
self.assertEqual(self._callFUT([type]), [type])
|
||||
|
||||
def test_w_newstyle_class(self):
|
||||
|
||||
class C:
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT([C]), [C])
|
||||
|
||||
def test_simple_hierarchy_skips_implied(self):
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
class C(B):
|
||||
pass
|
||||
|
||||
class D:
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT([A, B, C]), [C])
|
||||
self.assertEqual(self._callFUT([A, C]), [C])
|
||||
self.assertEqual(self._callFUT([B, C]), [C])
|
||||
self.assertEqual(self._callFUT([A, B]), [B])
|
||||
self.assertEqual(self._callFUT([D, B, D]), [B, D])
|
||||
|
||||
def test_repeats_kicked_to_end_of_queue(self):
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B:
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT([A, B, A]), [B, A])
|
||||
@@ -0,0 +1,29 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2022 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import struct
|
||||
import unittest
|
||||
|
||||
import zope.interface # noqa: try to load a C module for side effects
|
||||
|
||||
|
||||
class TestFloatingPoint(unittest.TestCase):
|
||||
|
||||
def test_no_fast_math_optimization(self):
|
||||
# Building with -Ofast enables -ffast-math, which sets certain FPU
|
||||
# flags that can cause breakage elsewhere. A library such as BTrees
|
||||
# has no business changing global FPU flags for the entire process.
|
||||
zero_bits = struct.unpack("!Q", struct.pack("!d", 0.0))[0]
|
||||
next_up = zero_bits + 1
|
||||
smallest_subnormal = struct.unpack("!d", struct.pack("!Q", next_up))[0]
|
||||
self.assertNotEqual(smallest_subnormal, 0.0)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,547 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Documentation tests.
|
||||
"""
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
|
||||
class Test_asStructuredText(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, iface):
|
||||
from zope.interface.document import asStructuredText
|
||||
return asStructuredText(iface)
|
||||
|
||||
def test_asStructuredText_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"INoDocstring",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
|
||||
class INoDocstring(Interface):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(INoDocstring), EXPECTED)
|
||||
|
||||
def test_asStructuredText_empty_with_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IEmpty",
|
||||
" This is an empty interface.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
|
||||
class IEmpty(Interface):
|
||||
""" This is an empty interface.
|
||||
"""
|
||||
|
||||
self.assertEqual(self._callFUT(IEmpty), EXPECTED)
|
||||
|
||||
def test_asStructuredText_empty_with_multiline_docstring(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
# In Python 3.13+, compiler strips indents from docstrings
|
||||
indent = " " * 12 if sys.version_info < (3, 13) else ""
|
||||
|
||||
EXPECTED = '\n'.join([
|
||||
"IEmpty",
|
||||
"",
|
||||
" This is an empty interface.",
|
||||
" ",
|
||||
(f"{indent} It can be used to annotate any class or object, "
|
||||
"because it promises"), # noqa E127
|
||||
f"{indent} nothing.",
|
||||
"",
|
||||
" Attributes:",
|
||||
"",
|
||||
" Methods:",
|
||||
"",
|
||||
""
|
||||
])
|
||||
|
||||
class IEmpty(Interface):
|
||||
""" This is an empty interface.
|
||||
|
||||
It can be used to annotate any class or object, because it promises
|
||||
nothing.
|
||||
"""
|
||||
|
||||
self.assertEqual(self._callFUT(IEmpty), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_attribute_no_docstring(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasAttribute",
|
||||
" This interface has an attribute.",
|
||||
" Attributes:",
|
||||
" an_attribute -- no documentation",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasAttribute(Interface):
|
||||
""" This interface has an attribute.
|
||||
"""
|
||||
an_attribute = Attribute('an_attribute')
|
||||
|
||||
self.assertEqual(self._callFUT(IHasAttribute), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_attribute_with_docstring(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasAttribute",
|
||||
" This interface has an attribute.",
|
||||
" Attributes:",
|
||||
" an_attribute -- This attribute is documented.",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasAttribute(Interface):
|
||||
""" This interface has an attribute.
|
||||
"""
|
||||
an_attribute = Attribute('an_attribute',
|
||||
'This attribute is documented.')
|
||||
|
||||
self.assertEqual(self._callFUT(IHasAttribute), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_no_args_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod() -- no documentation",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod():
|
||||
pass # pragma: no cover
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_positional_args_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod(first, second) -- no documentation",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod(first, second):
|
||||
pass # pragma: no cover
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_starargs_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod(first, second, *rest) -- no documentation",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod(first, second, *rest):
|
||||
pass # pragma: no cover
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_kwargs_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod(first, second, **kw) -- no documentation",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod(first, second, **kw):
|
||||
pass # pragma: no cover
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_with_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod() -- This method is documented.",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod():
|
||||
"""This method is documented.
|
||||
"""
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_derived_ignores_base(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IDerived",
|
||||
" IDerived doc",
|
||||
" This interface extends:",
|
||||
" o IBase",
|
||||
" Attributes:",
|
||||
" attr1 -- no documentation",
|
||||
" attr2 -- attr2 doc",
|
||||
" Methods:",
|
||||
" method3() -- method3 doc",
|
||||
" method4() -- no documentation",
|
||||
" method5() -- method5 doc",
|
||||
"",
|
||||
])
|
||||
|
||||
class IBase(Interface):
|
||||
def method1():
|
||||
"""docstring"""
|
||||
|
||||
def method2():
|
||||
"""docstring"""
|
||||
|
||||
class IDerived(IBase):
|
||||
"IDerived doc"
|
||||
attr1 = Attribute('attr1')
|
||||
attr2 = Attribute('attr2', 'attr2 doc')
|
||||
|
||||
def method3():
|
||||
"method3 doc"
|
||||
|
||||
def method4():
|
||||
pass # pragma: no cover
|
||||
|
||||
def method5():
|
||||
"method5 doc"
|
||||
|
||||
self.assertEqual(self._callFUT(IDerived), EXPECTED)
|
||||
|
||||
|
||||
class Test_asReStructuredText(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, iface):
|
||||
from zope.interface.document import asReStructuredText
|
||||
return asReStructuredText(iface)
|
||||
|
||||
def test_asReStructuredText_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``INoDocstring``",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
|
||||
class INoDocstring(Interface):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(INoDocstring), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_empty_with_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``IEmpty``",
|
||||
" This is an empty interface.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
|
||||
class IEmpty(Interface):
|
||||
""" This is an empty interface.
|
||||
"""
|
||||
|
||||
self.assertEqual(self._callFUT(IEmpty), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_empty_with_multiline_docstring(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
# In Python 3.13+, compiler strips indents from docstrings
|
||||
indent = " " * 12 if sys.version_info < (3, 13) else ""
|
||||
|
||||
EXPECTED = '\n'.join([
|
||||
"``IEmpty``",
|
||||
"",
|
||||
" This is an empty interface.",
|
||||
" ",
|
||||
(f"{indent} It can be used to annotate any class or object, "
|
||||
f"because it"
|
||||
), # noqa E124
|
||||
f"{indent} promises nothing.",
|
||||
"",
|
||||
" Attributes:",
|
||||
"",
|
||||
" Methods:",
|
||||
"",
|
||||
""
|
||||
])
|
||||
|
||||
class IEmpty(Interface):
|
||||
""" This is an empty interface.
|
||||
|
||||
It can be used to annotate any class or object, because it
|
||||
promises nothing.
|
||||
"""
|
||||
|
||||
self.assertEqual(self._callFUT(IEmpty), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_with_attribute_no_docstring(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``IHasAttribute``",
|
||||
" This interface has an attribute.",
|
||||
" Attributes:",
|
||||
" ``an_attribute`` -- no documentation",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasAttribute(Interface):
|
||||
""" This interface has an attribute.
|
||||
"""
|
||||
an_attribute = Attribute('an_attribute')
|
||||
|
||||
self.assertEqual(self._callFUT(IHasAttribute), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_with_attribute_with_docstring(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``IHasAttribute``",
|
||||
" This interface has an attribute.",
|
||||
" Attributes:",
|
||||
" ``an_attribute`` -- This attribute is documented.",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasAttribute(Interface):
|
||||
""" This interface has an attribute.
|
||||
"""
|
||||
an_attribute = Attribute('an_attribute',
|
||||
'This attribute is documented.')
|
||||
|
||||
self.assertEqual(self._callFUT(IHasAttribute), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_with_method_no_args_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``IHasMethod``",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" ``aMethod()`` -- no documentation",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod():
|
||||
pass # pragma: no cover
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_with_method_positional_args_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``IHasMethod``",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" ``aMethod(first, second)`` -- no documentation",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod(first, second):
|
||||
pass # pragma: no cover
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_with_method_starargs_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``IHasMethod``",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" ``aMethod(first, second, *rest)`` -- no documentation",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod(first, second, *rest):
|
||||
pass # pragma: no cover
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_with_method_kwargs_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``IHasMethod``",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" ``aMethod(first, second, **kw)`` -- no documentation",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod(first, second, **kw):
|
||||
pass # pragma: no cover
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_with_method_with_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``IHasMethod``",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" ``aMethod()`` -- This method is documented.",
|
||||
""
|
||||
])
|
||||
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod():
|
||||
"""This method is documented.
|
||||
"""
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asReStructuredText_derived_ignores_base(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"``IDerived``",
|
||||
" IDerived doc",
|
||||
" This interface extends:",
|
||||
" o ``IBase``",
|
||||
" Attributes:",
|
||||
" ``attr1`` -- no documentation",
|
||||
" ``attr2`` -- attr2 doc",
|
||||
" Methods:",
|
||||
" ``method3()`` -- method3 doc",
|
||||
" ``method4()`` -- no documentation",
|
||||
" ``method5()`` -- method5 doc",
|
||||
"",
|
||||
])
|
||||
|
||||
class IBase(Interface):
|
||||
def method1():
|
||||
pass # pragma: no cover
|
||||
|
||||
def method2():
|
||||
pass # pragma: no cover
|
||||
|
||||
class IDerived(IBase):
|
||||
"IDerived doc"
|
||||
attr1 = Attribute('attr1')
|
||||
attr2 = Attribute('attr2', 'attr2 doc')
|
||||
|
||||
def method3():
|
||||
"method3 doc"
|
||||
|
||||
def method4():
|
||||
pass # pragma: no cover
|
||||
|
||||
def method5():
|
||||
"method5 doc"
|
||||
|
||||
self.assertEqual(self._callFUT(IDerived), EXPECTED)
|
||||
|
||||
|
||||
class Test__justify_and_indent(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, text, level, **kw):
|
||||
from zope.interface.document import _justify_and_indent
|
||||
return _justify_and_indent(text, level, **kw)
|
||||
|
||||
def test_simple_level_0(self):
|
||||
LINES = ['Three blind mice', 'See how they run']
|
||||
text = '\n'.join(LINES)
|
||||
self.assertEqual(self._callFUT(text, 0), text)
|
||||
|
||||
def test_simple_level_1(self):
|
||||
LINES = ['Three blind mice', 'See how they run']
|
||||
text = '\n'.join(LINES)
|
||||
self.assertEqual(self._callFUT(text, 1),
|
||||
'\n'.join([' ' + line for line in LINES]))
|
||||
|
||||
def test_simple_level_2(self):
|
||||
LINES = ['Three blind mice', 'See how they run']
|
||||
text = '\n'.join(LINES)
|
||||
self.assertEqual(self._callFUT(text, 1),
|
||||
'\n'.join([' ' + line for line in LINES]))
|
||||
|
||||
def test_simple_w_CRLF(self):
|
||||
LINES = ['Three blind mice', 'See how they run']
|
||||
text = '\r\n'.join(LINES)
|
||||
self.assertEqual(self._callFUT(text, 1),
|
||||
'\n'.join([' ' + line for line in LINES]))
|
||||
|
||||
def test_with_munge(self):
|
||||
TEXT = ("This is a piece of text longer than 15 characters, \n"
|
||||
"and split across multiple lines.")
|
||||
EXPECTED = (" This is a piece\n"
|
||||
" of text longer\n"
|
||||
" than 15 characters,\n"
|
||||
" and split across\n"
|
||||
" multiple lines.\n"
|
||||
" ")
|
||||
self.assertEqual(self._callFUT(TEXT, 1, munge=1, width=15), EXPECTED)
|
||||
@@ -0,0 +1,33 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Test Element meta-class.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
from zope.interface.interface import Element
|
||||
|
||||
|
||||
class TestElement(unittest.TestCase):
|
||||
|
||||
def test_taggedValues(self):
|
||||
"""Test that we can update tagged values of more than one element
|
||||
"""
|
||||
|
||||
e1 = Element("foo")
|
||||
e2 = Element("bar")
|
||||
e1.setTaggedValue("x", 1)
|
||||
e2.setTaggedValue("x", 2)
|
||||
self.assertEqual(e1.getTaggedValue("x"), 1)
|
||||
self.assertEqual(e2.getTaggedValue("x"), 2)
|
||||
@@ -0,0 +1,190 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2010 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
""" zope.interface.exceptions unit tests
|
||||
"""
|
||||
import unittest
|
||||
|
||||
|
||||
def _makeIface():
|
||||
from zope.interface import Interface
|
||||
|
||||
class IDummy(Interface):
|
||||
pass
|
||||
|
||||
return IDummy
|
||||
|
||||
|
||||
class DoesNotImplementTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.exceptions import DoesNotImplement
|
||||
return DoesNotImplement
|
||||
|
||||
def _makeOne(self, *args):
|
||||
iface = _makeIface()
|
||||
return self._getTargetClass()(iface, *args)
|
||||
|
||||
def test___str__(self):
|
||||
dni = self._makeOne()
|
||||
self.assertEqual(
|
||||
str(dni),
|
||||
"An object has failed to implement interface "
|
||||
"zope.interface.tests.test_exceptions.IDummy: "
|
||||
"Does not declaratively implement the interface."
|
||||
)
|
||||
|
||||
def test___str__w_candidate(self):
|
||||
dni = self._makeOne('candidate')
|
||||
self.assertEqual(
|
||||
str(dni),
|
||||
"The object 'candidate' has failed to implement interface "
|
||||
"zope.interface.tests.test_exceptions.IDummy: "
|
||||
"Does not declaratively implement the interface."
|
||||
)
|
||||
|
||||
|
||||
class BrokenImplementationTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
return BrokenImplementation
|
||||
|
||||
def _makeOne(self, *args):
|
||||
iface = _makeIface()
|
||||
return self._getTargetClass()(iface, 'missing', *args)
|
||||
|
||||
def test___str__(self):
|
||||
dni = self._makeOne()
|
||||
self.assertEqual(
|
||||
str(dni),
|
||||
'An object has failed to implement interface '
|
||||
'zope.interface.tests.test_exceptions.IDummy: '
|
||||
"The 'missing' attribute was not provided.")
|
||||
|
||||
def test___str__w_candidate(self):
|
||||
dni = self._makeOne('candidate')
|
||||
self.assertEqual(
|
||||
str(dni),
|
||||
'The object \'candidate\' has failed to implement interface '
|
||||
'zope.interface.tests.test_exceptions.IDummy: '
|
||||
"The 'missing' attribute was not provided.")
|
||||
|
||||
|
||||
def broken_function():
|
||||
"""
|
||||
This is a global function with a simple argument list.
|
||||
|
||||
It exists to be able to report the same information when
|
||||
formatting signatures.
|
||||
"""
|
||||
|
||||
|
||||
class BrokenMethodImplementationTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
return BrokenMethodImplementation
|
||||
|
||||
message = 'I said so'
|
||||
|
||||
def _makeOne(self, *args):
|
||||
return self._getTargetClass()('aMethod', self.message, *args)
|
||||
|
||||
def test___str__(self):
|
||||
dni = self._makeOne()
|
||||
self.assertEqual(
|
||||
str(dni),
|
||||
"An object has failed to implement interface <Unknown>: "
|
||||
"The contract of 'aMethod' is violated because I said so."
|
||||
)
|
||||
|
||||
def test___str__w_candidate_no_implementation(self):
|
||||
dni = self._makeOne('some_function', '<IFoo>', 'candidate')
|
||||
self.assertEqual(
|
||||
str(dni),
|
||||
"The object 'candidate' has failed to implement interface <IFoo>: "
|
||||
"The contract of 'aMethod' is violated because I said so."
|
||||
)
|
||||
|
||||
def test___str__w_candidate_w_implementation(self):
|
||||
self.message = 'implementation is wonky'
|
||||
dni = self._makeOne(broken_function, '<IFoo>', 'candidate')
|
||||
self.assertEqual(
|
||||
str(dni),
|
||||
"The object 'candidate' has failed to implement interface <IFoo>: "
|
||||
"The contract of 'aMethod' is violated because "
|
||||
"'broken_function()' is wonky."
|
||||
)
|
||||
|
||||
def test___str__w_candidate_w_implementation_not_callable(self):
|
||||
self.message = 'implementation is not callable'
|
||||
dni = self._makeOne(42, '<IFoo>', 'candidate')
|
||||
self.assertEqual(
|
||||
str(dni),
|
||||
"The object 'candidate' has failed to implement interface <IFoo>: "
|
||||
"The contract of 'aMethod' is violated because "
|
||||
"'42' is not callable."
|
||||
)
|
||||
|
||||
def test___repr__w_candidate(self):
|
||||
dni = self._makeOne(None, 'candidate')
|
||||
self.assertEqual(
|
||||
repr(dni),
|
||||
"BrokenMethodImplementation("
|
||||
"'aMethod', 'I said so', None, 'candidate')"
|
||||
)
|
||||
|
||||
|
||||
class MultipleInvalidTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.exceptions import MultipleInvalid
|
||||
return MultipleInvalid
|
||||
|
||||
def _makeOne(self, excs):
|
||||
iface = _makeIface()
|
||||
return self._getTargetClass()(iface, 'target', excs)
|
||||
|
||||
def test__str__(self):
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
excs = [
|
||||
BrokenMethodImplementation('aMethod', 'I said so'),
|
||||
Exception("Regular exception")
|
||||
]
|
||||
dni = self._makeOne(excs)
|
||||
self.assertEqual(
|
||||
str(dni),
|
||||
"The object 'target' has failed to implement interface "
|
||||
"zope.interface.tests.test_exceptions.IDummy:\n"
|
||||
" The contract of 'aMethod' is violated because I said so\n"
|
||||
" Regular exception"
|
||||
)
|
||||
|
||||
def test__repr__(self):
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
excs = [
|
||||
BrokenMethodImplementation('aMethod', 'I said so'),
|
||||
# Use multiple arguments to normalize repr; versions of Python
|
||||
# prior to 3.7 add a trailing comma if there's just one.
|
||||
Exception("Regular", "exception")
|
||||
]
|
||||
dni = self._makeOne(excs)
|
||||
self.assertEqual(
|
||||
repr(dni),
|
||||
"MultipleInvalid("
|
||||
"<InterfaceClass zope.interface.tests.test_exceptions.IDummy>,"
|
||||
" 'target',"
|
||||
" (BrokenMethodImplementation('aMethod', 'I said so'),"
|
||||
" Exception('Regular', 'exception')))"
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,130 @@
|
||||
import unittest
|
||||
|
||||
|
||||
class _ConformsToIObjectEvent:
|
||||
|
||||
def _makeOne(self, target=None):
|
||||
if target is None:
|
||||
target = object()
|
||||
return self._getTargetClass()(target)
|
||||
|
||||
def test_class_conforms_to_IObjectEvent(self):
|
||||
from zope.interface.interfaces import IObjectEvent
|
||||
from zope.interface.verify import verifyClass
|
||||
verifyClass(IObjectEvent, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_IObjectEvent(self):
|
||||
from zope.interface.interfaces import IObjectEvent
|
||||
from zope.interface.verify import verifyObject
|
||||
verifyObject(IObjectEvent, self._makeOne())
|
||||
|
||||
|
||||
class _ConformsToIRegistrationEvent(_ConformsToIObjectEvent):
|
||||
|
||||
def test_class_conforms_to_IRegistrationEvent(self):
|
||||
from zope.interface.interfaces import IRegistrationEvent
|
||||
from zope.interface.verify import verifyClass
|
||||
verifyClass(IRegistrationEvent, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_IRegistrationEvent(self):
|
||||
from zope.interface.interfaces import IRegistrationEvent
|
||||
from zope.interface.verify import verifyObject
|
||||
verifyObject(IRegistrationEvent, self._makeOne())
|
||||
|
||||
|
||||
class ObjectEventTests(unittest.TestCase, _ConformsToIObjectEvent):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.interfaces import ObjectEvent
|
||||
return ObjectEvent
|
||||
|
||||
def test_ctor(self):
|
||||
target = object()
|
||||
event = self._makeOne(target)
|
||||
self.assertIs(event.object, target)
|
||||
|
||||
|
||||
class RegistrationEventTests(unittest.TestCase,
|
||||
_ConformsToIRegistrationEvent):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.interfaces import RegistrationEvent
|
||||
return RegistrationEvent
|
||||
|
||||
def test___repr__(self):
|
||||
target = object()
|
||||
event = self._makeOne(target)
|
||||
r = repr(event)
|
||||
self.assertEqual(r.splitlines(),
|
||||
['RegistrationEvent event:', repr(target)])
|
||||
|
||||
|
||||
class RegisteredTests(unittest.TestCase,
|
||||
_ConformsToIRegistrationEvent):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.interfaces import Registered
|
||||
return Registered
|
||||
|
||||
def test_class_conforms_to_IRegistered(self):
|
||||
from zope.interface.interfaces import IRegistered
|
||||
from zope.interface.verify import verifyClass
|
||||
verifyClass(IRegistered, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_IRegistered(self):
|
||||
from zope.interface.interfaces import IRegistered
|
||||
from zope.interface.verify import verifyObject
|
||||
verifyObject(IRegistered, self._makeOne())
|
||||
|
||||
|
||||
class UnregisteredTests(unittest.TestCase,
|
||||
_ConformsToIRegistrationEvent):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.interfaces import Unregistered
|
||||
return Unregistered
|
||||
|
||||
def test_class_conforms_to_IUnregistered(self):
|
||||
from zope.interface.interfaces import IUnregistered
|
||||
from zope.interface.verify import verifyClass
|
||||
verifyClass(IUnregistered, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_IUnregistered(self):
|
||||
from zope.interface.interfaces import IUnregistered
|
||||
from zope.interface.verify import verifyObject
|
||||
verifyObject(IUnregistered, self._makeOne())
|
||||
|
||||
|
||||
class InterfaceClassTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.interface import InterfaceClass
|
||||
return InterfaceClass
|
||||
|
||||
def _getTargetInterface(self):
|
||||
from zope.interface.interfaces import IInterface
|
||||
return IInterface
|
||||
|
||||
def _makeOne(self):
|
||||
from zope.interface.interface import Interface
|
||||
return Interface
|
||||
|
||||
def test_class_conforms(self):
|
||||
from zope.interface.verify import verifyClass
|
||||
verifyClass(self._getTargetInterface(), self._getTargetClass())
|
||||
|
||||
def test_instance_conforms(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
verifyObject(self._getTargetInterface(), self._makeOne())
|
||||
|
||||
def test_instance_consistent__iro__(self):
|
||||
from zope.interface import ro
|
||||
self.assertTrue(ro.is_consistent(self._getTargetInterface()))
|
||||
|
||||
def test_class_consistent__iro__(self):
|
||||
from zope.interface import implementedBy
|
||||
from zope.interface import ro
|
||||
|
||||
self.assertTrue(
|
||||
ro.is_consistent(implementedBy(self._getTargetClass()))
|
||||
)
|
||||
@@ -0,0 +1,301 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Test interface declarations against ExtensionClass-like classes.
|
||||
|
||||
These tests are to make sure we do something sane in the presence of
|
||||
classic ExtensionClass classes and instances.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from zope.interface import Interface
|
||||
from zope.interface import classImplements
|
||||
from zope.interface import classImplementsOnly
|
||||
from zope.interface import directlyProvidedBy
|
||||
from zope.interface import directlyProvides
|
||||
from zope.interface import implementedBy
|
||||
from zope.interface import implementer
|
||||
from zope.interface import providedBy
|
||||
from zope.interface.tests import odd
|
||||
|
||||
|
||||
class I1(Interface):
|
||||
pass
|
||||
|
||||
|
||||
class I2(Interface):
|
||||
pass
|
||||
|
||||
|
||||
class I3(Interface):
|
||||
pass
|
||||
|
||||
|
||||
class I31(I3):
|
||||
pass
|
||||
|
||||
|
||||
class I4(Interface):
|
||||
pass
|
||||
|
||||
|
||||
class I5(Interface):
|
||||
pass
|
||||
|
||||
|
||||
class Odd:
|
||||
pass
|
||||
|
||||
|
||||
Odd = odd.MetaClass('Odd', Odd.__bases__, {})
|
||||
|
||||
|
||||
class B(Odd):
|
||||
__implemented__ = I2
|
||||
|
||||
|
||||
# TODO: We are going to need more magic to make classProvides work with odd
|
||||
# classes. This will work in the next iteration. For now, we'll use
|
||||
# a different mechanism.
|
||||
|
||||
# from zope.interface import classProvides
|
||||
class A(Odd):
|
||||
pass
|
||||
|
||||
|
||||
classImplements(A, I1)
|
||||
|
||||
|
||||
class C(A, B):
|
||||
pass
|
||||
|
||||
|
||||
classImplements(C, I31)
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def test_ObjectSpecification(self):
|
||||
c = C()
|
||||
directlyProvides(c, I4)
|
||||
self.assertEqual([i.getName() for i in providedBy(c)],
|
||||
['I4', 'I31', 'I1', 'I2']
|
||||
)
|
||||
self.assertEqual([i.getName() for i in providedBy(c).flattened()],
|
||||
['I4', 'I31', 'I3', 'I1', 'I2', 'Interface']
|
||||
)
|
||||
self.assertIn(I1, providedBy(c))
|
||||
self.assertNotIn(I3, providedBy(c))
|
||||
self.assertTrue(providedBy(c).extends(I3))
|
||||
self.assertTrue(providedBy(c).extends(I31))
|
||||
self.assertFalse(providedBy(c).extends(I5))
|
||||
|
||||
class COnly(A, B):
|
||||
pass
|
||||
classImplementsOnly(COnly, I31)
|
||||
|
||||
class D(COnly):
|
||||
pass
|
||||
classImplements(D, I5)
|
||||
|
||||
classImplements(D, I5)
|
||||
|
||||
c = D()
|
||||
directlyProvides(c, I4)
|
||||
self.assertEqual([i.getName() for i in providedBy(c)],
|
||||
['I4', 'I5', 'I31'])
|
||||
self.assertEqual([i.getName() for i in providedBy(c).flattened()],
|
||||
['I4', 'I5', 'I31', 'I3', 'Interface'])
|
||||
self.assertNotIn(I1, providedBy(c))
|
||||
self.assertNotIn(I3, providedBy(c))
|
||||
self.assertTrue(providedBy(c).extends(I3))
|
||||
self.assertFalse(providedBy(c).extends(I1))
|
||||
self.assertTrue(providedBy(c).extends(I31))
|
||||
self.assertTrue(providedBy(c).extends(I5))
|
||||
|
||||
class COnly(A, B):
|
||||
__implemented__ = I31
|
||||
|
||||
class D(COnly):
|
||||
pass
|
||||
|
||||
classImplements(D, I5)
|
||||
classImplements(D, I5)
|
||||
|
||||
c = D()
|
||||
directlyProvides(c, I4)
|
||||
self.assertEqual([i.getName() for i in providedBy(c)],
|
||||
['I4', 'I5', 'I31'])
|
||||
self.assertEqual([i.getName() for i in providedBy(c).flattened()],
|
||||
['I4', 'I5', 'I31', 'I3', 'Interface'])
|
||||
self.assertNotIn(I1, providedBy(c))
|
||||
self.assertNotIn(I3, providedBy(c))
|
||||
self.assertTrue(providedBy(c).extends(I3))
|
||||
self.assertFalse(providedBy(c).extends(I1))
|
||||
self.assertTrue(providedBy(c).extends(I31))
|
||||
self.assertTrue(providedBy(c).extends(I5))
|
||||
|
||||
def test_classImplements(self):
|
||||
|
||||
@implementer(I3)
|
||||
class A(Odd):
|
||||
pass
|
||||
|
||||
@implementer(I4)
|
||||
class B(Odd):
|
||||
pass
|
||||
|
||||
class C(A, B):
|
||||
pass
|
||||
|
||||
classImplements(C, I1, I2)
|
||||
self.assertEqual([i.getName() for i in implementedBy(C)],
|
||||
['I1', 'I2', 'I3', 'I4'])
|
||||
|
||||
classImplements(C, I5)
|
||||
self.assertEqual([i.getName() for i in implementedBy(C)],
|
||||
['I1', 'I2', 'I5', 'I3', 'I4'])
|
||||
|
||||
def test_classImplementsOnly(self):
|
||||
|
||||
@implementer(I3)
|
||||
class A(Odd):
|
||||
pass
|
||||
|
||||
@implementer(I4)
|
||||
class B(Odd):
|
||||
pass
|
||||
|
||||
class C(A, B):
|
||||
pass
|
||||
|
||||
classImplementsOnly(C, I1, I2)
|
||||
self.assertEqual([i.__name__ for i in implementedBy(C)],
|
||||
['I1', 'I2'])
|
||||
|
||||
def test_directlyProvides(self):
|
||||
|
||||
class IA1(Interface):
|
||||
pass
|
||||
|
||||
class IA2(Interface):
|
||||
pass
|
||||
|
||||
class IB(Interface):
|
||||
pass
|
||||
|
||||
class IC(Interface):
|
||||
pass
|
||||
|
||||
class A(Odd):
|
||||
pass
|
||||
classImplements(A, IA1, IA2)
|
||||
|
||||
class B(Odd):
|
||||
pass
|
||||
classImplements(B, IB)
|
||||
|
||||
class C(A, B):
|
||||
pass
|
||||
classImplements(C, IC)
|
||||
|
||||
ob = C()
|
||||
directlyProvides(ob, I1, I2)
|
||||
self.assertIn(I1, providedBy(ob))
|
||||
self.assertIn(I2, providedBy(ob))
|
||||
self.assertIn(IA1, providedBy(ob))
|
||||
self.assertIn(IA2, providedBy(ob))
|
||||
self.assertIn(IB, providedBy(ob))
|
||||
self.assertIn(IC, providedBy(ob))
|
||||
|
||||
directlyProvides(ob, directlyProvidedBy(ob) - I2)
|
||||
self.assertIn(I1, providedBy(ob))
|
||||
self.assertNotIn(I2, providedBy(ob))
|
||||
self.assertNotIn(I2, providedBy(ob))
|
||||
directlyProvides(ob, directlyProvidedBy(ob), I2)
|
||||
self.assertIn(I2, providedBy(ob))
|
||||
|
||||
# see above
|
||||
# def TODO_test_classProvides_fails_for_odd_class(self):
|
||||
# try:
|
||||
# class A(Odd):
|
||||
# classProvides(I1)
|
||||
# except TypeError:
|
||||
# pass # Success
|
||||
# self.assert_(
|
||||
# False,
|
||||
# "Shouldn't be able to use directlyProvides on odd class."
|
||||
# )
|
||||
|
||||
def test_implementedBy(self):
|
||||
|
||||
class I2(I1):
|
||||
pass
|
||||
|
||||
class C1(Odd):
|
||||
pass
|
||||
|
||||
classImplements(C1, I2)
|
||||
|
||||
class C2(C1):
|
||||
pass
|
||||
|
||||
classImplements(C2, I3)
|
||||
|
||||
self.assertEqual([i.getName() for i in implementedBy(C2)],
|
||||
['I3', 'I2'])
|
||||
|
||||
def test_odd_metaclass_that_doesnt_subclass_type(self):
|
||||
# This was originally a doctest in odd.py.
|
||||
# It verifies that the metaclass the rest of these tests use
|
||||
# works as expected.
|
||||
|
||||
# This is used for testing support for ExtensionClass in new
|
||||
# interfaces.
|
||||
|
||||
class A:
|
||||
a = 1
|
||||
|
||||
A = odd.MetaClass('A', A.__bases__, A.__dict__)
|
||||
|
||||
class B:
|
||||
b = 1
|
||||
|
||||
B = odd.MetaClass('B', B.__bases__, B.__dict__)
|
||||
|
||||
class C(A, B):
|
||||
pass
|
||||
|
||||
self.assertEqual(C.__bases__, (A, B))
|
||||
|
||||
a = A()
|
||||
aa = A()
|
||||
self.assertEqual(a.a, 1)
|
||||
self.assertEqual(aa.a, 1)
|
||||
|
||||
aa.a = 2
|
||||
self.assertEqual(a.a, 1)
|
||||
self.assertEqual(aa.a, 2)
|
||||
|
||||
c = C()
|
||||
self.assertEqual(c.a, 1)
|
||||
self.assertEqual(c.b, 1)
|
||||
|
||||
c.b = 2
|
||||
self.assertEqual(c.b, 2)
|
||||
|
||||
C.c = 1
|
||||
self.assertEqual(c.c, 1)
|
||||
c.c
|
||||
|
||||
self.assertIs(C.__class__.__class__, C.__class__)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,487 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2014 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Resolution ordering utility tests"""
|
||||
import unittest
|
||||
|
||||
|
||||
# pylint:disable=blacklisted-name
|
||||
# pylint:disable=protected-access
|
||||
# pylint:disable=attribute-defined-outside-init
|
||||
|
||||
class Test__mergeOrderings(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, orderings):
|
||||
from zope.interface.ro import _legacy_mergeOrderings
|
||||
return _legacy_mergeOrderings(orderings)
|
||||
|
||||
def test_empty(self):
|
||||
self.assertEqual(self._callFUT([]), [])
|
||||
|
||||
def test_single(self):
|
||||
self.assertEqual(self._callFUT(['a', 'b', 'c']), ['a', 'b', 'c'])
|
||||
|
||||
def test_w_duplicates(self):
|
||||
self.assertEqual(self._callFUT([['a'], ['b', 'a']]), ['b', 'a'])
|
||||
|
||||
def test_suffix_across_multiple_duplicates(self):
|
||||
O1 = ['x', 'y', 'z']
|
||||
O2 = ['q', 'z']
|
||||
O3 = [1, 3, 5]
|
||||
O4 = ['z']
|
||||
self.assertEqual(self._callFUT([O1, O2, O3, O4]),
|
||||
['x', 'y', 'q', 1, 3, 5, 'z'])
|
||||
|
||||
|
||||
class Test__flatten(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, ob):
|
||||
from zope.interface.ro import _legacy_flatten
|
||||
return _legacy_flatten(ob)
|
||||
|
||||
def test_w_empty_bases(self):
|
||||
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
foo = Foo()
|
||||
foo.__bases__ = ()
|
||||
self.assertEqual(self._callFUT(foo), [foo])
|
||||
|
||||
def test_w_single_base(self):
|
||||
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(Foo), [Foo, object])
|
||||
|
||||
def test_w_bases(self):
|
||||
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
class Bar(Foo):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(Bar), [Bar, Foo, object])
|
||||
|
||||
def test_w_diamond(self):
|
||||
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
class Bar(Foo):
|
||||
pass
|
||||
|
||||
class Baz(Foo):
|
||||
pass
|
||||
|
||||
class Qux(Bar, Baz):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(Qux),
|
||||
[Qux, Bar, Foo, object, Baz, Foo, object])
|
||||
|
||||
|
||||
class Test_ro(unittest.TestCase):
|
||||
maxDiff = None
|
||||
|
||||
def _callFUT(self, ob, **kwargs):
|
||||
from zope.interface.ro import _legacy_ro
|
||||
return _legacy_ro(ob, **kwargs)
|
||||
|
||||
def test_w_empty_bases(self):
|
||||
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
foo = Foo()
|
||||
foo.__bases__ = ()
|
||||
self.assertEqual(self._callFUT(foo), [foo])
|
||||
|
||||
def test_w_single_base(self):
|
||||
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(Foo), [Foo, object])
|
||||
|
||||
def test_w_bases(self):
|
||||
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
class Bar(Foo):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(Bar), [Bar, Foo, object])
|
||||
|
||||
def test_w_diamond(self):
|
||||
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
class Bar(Foo):
|
||||
pass
|
||||
|
||||
class Baz(Foo):
|
||||
pass
|
||||
|
||||
class Qux(Bar, Baz):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(Qux),
|
||||
[Qux, Bar, Baz, Foo, object])
|
||||
|
||||
def _make_IOErr(self):
|
||||
# This can't be done in the standard C3 ordering.
|
||||
|
||||
class Foo:
|
||||
def __init__(self, name, *bases):
|
||||
self.__name__ = name
|
||||
self.__bases__ = bases
|
||||
|
||||
def __repr__(self): # pragma: no cover
|
||||
return self.__name__
|
||||
|
||||
# Mimic what classImplements(IOError, IIOError)
|
||||
# does.
|
||||
IEx = Foo('IEx')
|
||||
IStdErr = Foo('IStdErr', IEx)
|
||||
IEnvErr = Foo('IEnvErr', IStdErr)
|
||||
IIOErr = Foo('IIOErr', IEnvErr)
|
||||
IOSErr = Foo('IOSErr', IEnvErr)
|
||||
|
||||
IOErr = Foo('IOErr', IEnvErr, IIOErr, IOSErr)
|
||||
return IOErr, [IOErr, IIOErr, IOSErr, IEnvErr, IStdErr, IEx]
|
||||
|
||||
def test_non_orderable(self):
|
||||
IOErr, bases = self._make_IOErr()
|
||||
|
||||
self.assertEqual(self._callFUT(IOErr), bases)
|
||||
|
||||
def test_mixed_inheritance_and_implementation(self):
|
||||
# https://github.com/zopefoundation/zope.interface/issues/8
|
||||
# This test should fail, but doesn't, as described in that issue.
|
||||
# pylint:disable=inherit-non-class
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementedBy
|
||||
from zope.interface import implementer
|
||||
from zope.interface import providedBy
|
||||
|
||||
class IFoo(Interface):
|
||||
pass
|
||||
|
||||
@implementer(IFoo)
|
||||
class ImplementsFoo:
|
||||
pass
|
||||
|
||||
class ExtendsFoo(ImplementsFoo):
|
||||
pass
|
||||
|
||||
class ImplementsNothing:
|
||||
pass
|
||||
|
||||
class ExtendsFooImplementsNothing(ExtendsFoo, ImplementsNothing):
|
||||
pass
|
||||
|
||||
self.assertEqual(
|
||||
self._callFUT(providedBy(ExtendsFooImplementsNothing())),
|
||||
[implementedBy(ExtendsFooImplementsNothing),
|
||||
implementedBy(ExtendsFoo),
|
||||
implementedBy(ImplementsFoo),
|
||||
IFoo,
|
||||
Interface,
|
||||
implementedBy(ImplementsNothing),
|
||||
implementedBy(object)])
|
||||
|
||||
|
||||
class C3Setting:
|
||||
|
||||
def __init__(self, setting, value):
|
||||
self._setting = setting
|
||||
self._value = value
|
||||
|
||||
def __enter__(self):
|
||||
from zope.interface import ro
|
||||
setattr(ro.C3, self._setting.__name__, self._value)
|
||||
|
||||
def __exit__(self, t, v, tb):
|
||||
from zope.interface import ro
|
||||
setattr(ro.C3, self._setting.__name__, self._setting)
|
||||
|
||||
|
||||
class Test_c3_ro(Test_ro):
|
||||
|
||||
def setUp(self):
|
||||
Test_ro.setUp(self)
|
||||
from zope.testing.loggingsupport import InstalledHandler
|
||||
self.log_handler = handler = InstalledHandler('zope.interface.ro')
|
||||
self.addCleanup(handler.uninstall)
|
||||
|
||||
def _callFUT(self, ob, **kwargs):
|
||||
from zope.interface.ro import ro
|
||||
return ro(ob, **kwargs)
|
||||
|
||||
def _make_complex_diamond(self, base):
|
||||
# https://github.com/zopefoundation/zope.interface/issues/21
|
||||
|
||||
class F(base):
|
||||
pass
|
||||
|
||||
class E(base):
|
||||
pass
|
||||
|
||||
class D(base):
|
||||
pass
|
||||
|
||||
class C(D, F):
|
||||
pass
|
||||
|
||||
class B(D, E):
|
||||
pass
|
||||
|
||||
class A(B, C):
|
||||
pass
|
||||
|
||||
if hasattr(A, 'mro'):
|
||||
self.assertEqual(A.mro(), self._callFUT(A))
|
||||
|
||||
return A
|
||||
|
||||
def test_complex_diamond_object(self):
|
||||
self._make_complex_diamond(object)
|
||||
|
||||
def test_complex_diamond_interface(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
IA = self._make_complex_diamond(Interface)
|
||||
|
||||
self.assertEqual(
|
||||
[x.__name__ for x in IA.__iro__],
|
||||
['A', 'B', 'C', 'D', 'E', 'F', 'Interface']
|
||||
)
|
||||
|
||||
def test_complex_diamond_use_legacy_argument(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
A = self._make_complex_diamond(Interface)
|
||||
legacy_A_iro = self._callFUT(A, use_legacy_ro=True)
|
||||
self.assertNotEqual(A.__iro__, legacy_A_iro)
|
||||
|
||||
# And logging happened as a side-effect.
|
||||
self._check_handler_complex_diamond()
|
||||
|
||||
def test_complex_diamond_compare_legacy_argument(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
A = self._make_complex_diamond(Interface)
|
||||
computed_A_iro = self._callFUT(A, log_changed_ro=True)
|
||||
# It matches, of course, but we did log a warning.
|
||||
self.assertEqual(tuple(computed_A_iro), A.__iro__)
|
||||
self._check_handler_complex_diamond()
|
||||
|
||||
def _check_handler_complex_diamond(self):
|
||||
handler = self.log_handler
|
||||
self.assertEqual(1, len(handler.records))
|
||||
record = handler.records[0]
|
||||
|
||||
expected = """\
|
||||
Object <InterfaceClass {name}> has different legacy and C3 MROs:
|
||||
Legacy RO (len=7) C3 RO (len=7; inconsistent=no)
|
||||
[AWS-SECRET-REMOVED]==========================
|
||||
zope.interface.tests.test_ro.A zope.interface.tests.test_ro.A
|
||||
zope.interface.tests.test_ro.B zope.interface.tests.test_ro.B
|
||||
- zope.interface.tests.test_ro.E
|
||||
zope.interface.tests.test_ro.C zope.interface.tests.test_ro.C
|
||||
zope.interface.tests.test_ro.D zope.interface.tests.test_ro.D
|
||||
+ zope.interface.tests.test_ro.E
|
||||
zope.interface.tests.test_ro.F zope.interface.tests.test_ro.F
|
||||
zope.interface.Interface zope.interface.Interface""".format(
|
||||
name="zope.interface.tests.test_ro.A"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
'\n'.join(ln.rstrip() for ln in record.getMessage().splitlines()),
|
||||
expected,
|
||||
)
|
||||
|
||||
def test_ExtendedPathIndex_implement_thing_implementedby_super(self):
|
||||
# See
|
||||
# https://github.com/zopefoundation/zope.interface/pull/182#issuecomment-598754056
|
||||
from zope.interface import ro
|
||||
|
||||
# pylint:disable=inherit-non-class
|
||||
class _Based:
|
||||
__bases__ = ()
|
||||
|
||||
def __init__(self, name, bases=(), attrs=None):
|
||||
self.__name__ = name
|
||||
self.__bases__ = bases
|
||||
|
||||
def __repr__(self):
|
||||
return self.__name__
|
||||
|
||||
Interface = _Based('Interface', (), {})
|
||||
|
||||
class IPluggableIndex(Interface):
|
||||
pass
|
||||
|
||||
class ILimitedResultIndex(IPluggableIndex):
|
||||
pass
|
||||
|
||||
class IQueryIndex(IPluggableIndex):
|
||||
pass
|
||||
|
||||
class IPathIndex(Interface):
|
||||
pass
|
||||
|
||||
# A parent class who implements two distinct interfaces whose
|
||||
# only common ancestor is Interface. An easy case.
|
||||
# @implementer(IPathIndex, IQueryIndex)
|
||||
# class PathIndex(object):
|
||||
# pass
|
||||
obj = _Based('object')
|
||||
PathIndex = _Based('PathIndex', (IPathIndex, IQueryIndex, obj))
|
||||
|
||||
# Child class that tries to put an interface the parent declares
|
||||
# later ahead of the parent.
|
||||
# @implementer(ILimitedResultIndex, IQueryIndex)
|
||||
# class ExtendedPathIndex(PathIndex):
|
||||
# pass
|
||||
ExtendedPathIndex = _Based(
|
||||
'ExtendedPathIndex',
|
||||
(ILimitedResultIndex, IQueryIndex, PathIndex)
|
||||
)
|
||||
|
||||
# We were able to resolve it, and in exactly the same way as
|
||||
# the legacy RO did, even though it is inconsistent.
|
||||
result = self._callFUT(
|
||||
ExtendedPathIndex, log_changed_ro=True, strict=False
|
||||
)
|
||||
self.assertEqual(result, [
|
||||
ExtendedPathIndex,
|
||||
ILimitedResultIndex,
|
||||
PathIndex,
|
||||
IPathIndex,
|
||||
IQueryIndex,
|
||||
IPluggableIndex,
|
||||
Interface,
|
||||
obj])
|
||||
|
||||
record, = self.log_handler.records
|
||||
self.assertIn('used the legacy', record.getMessage())
|
||||
|
||||
with self.assertRaises(ro.InconsistentResolutionOrderError):
|
||||
self._callFUT(ExtendedPathIndex, strict=True)
|
||||
|
||||
def test_OSError_IOError(self):
|
||||
from zope.interface import providedBy
|
||||
from zope.interface.common import interfaces
|
||||
|
||||
self.assertEqual(
|
||||
list(providedBy(OSError()).flattened()),
|
||||
[
|
||||
interfaces.IOSError,
|
||||
interfaces.IIOError,
|
||||
interfaces.IEnvironmentError,
|
||||
interfaces.IStandardError,
|
||||
interfaces.IException,
|
||||
interfaces.Interface,
|
||||
])
|
||||
|
||||
def test_non_orderable(self):
|
||||
import warnings
|
||||
|
||||
from zope.interface import ro
|
||||
try:
|
||||
# If we've already warned, we must reset that state.
|
||||
del ro.__warningregistry__
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('error')
|
||||
with C3Setting(
|
||||
ro.C3.WARN_BAD_IRO, True
|
||||
), C3Setting(
|
||||
ro.C3.STRICT_IRO, False
|
||||
):
|
||||
with self.assertRaises(ro.InconsistentResolutionOrderWarning):
|
||||
super().test_non_orderable()
|
||||
|
||||
IOErr, _ = self._make_IOErr()
|
||||
with self.assertRaises(ro.InconsistentResolutionOrderError):
|
||||
self._callFUT(IOErr, strict=True)
|
||||
|
||||
with C3Setting(
|
||||
ro.C3.TRACK_BAD_IRO, True
|
||||
), C3Setting(
|
||||
ro.C3.STRICT_IRO, False
|
||||
):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore')
|
||||
self._callFUT(IOErr)
|
||||
self.assertIn(IOErr, ro.C3.BAD_IROS)
|
||||
|
||||
iro = self._callFUT(IOErr, strict=False)
|
||||
legacy_iro = self._callFUT(IOErr, use_legacy_ro=True, strict=False)
|
||||
self.assertEqual(iro, legacy_iro)
|
||||
|
||||
|
||||
class TestC3(unittest.TestCase):
|
||||
def _makeOne(self, C, strict=False, base_mros=None):
|
||||
from zope.interface.ro import C3
|
||||
return C3.resolver(C, strict, base_mros)
|
||||
|
||||
def test_base_mros_given(self):
|
||||
c3 = self._makeOne(
|
||||
type(self),
|
||||
base_mros={unittest.TestCase: unittest.TestCase.__mro__}
|
||||
)
|
||||
memo = c3.memo
|
||||
self.assertIn(unittest.TestCase, memo)
|
||||
# We used the StaticMRO class
|
||||
self.assertIsNone(memo[unittest.TestCase].had_inconsistency)
|
||||
|
||||
def test_one_base_optimization(self):
|
||||
c3 = self._makeOne(type(self))
|
||||
# Even though we didn't call .mro() yet, the MRO has been
|
||||
# computed.
|
||||
self.assertIsNotNone(c3._C3__mro) # pylint:disable=no-member
|
||||
c3._merge = None
|
||||
self.assertEqual(c3.mro(), list(type(self).__mro__))
|
||||
|
||||
|
||||
class Test_ROComparison(unittest.TestCase):
|
||||
|
||||
class MockC3:
|
||||
direct_inconsistency = False
|
||||
bases_had_inconsistency = False
|
||||
|
||||
def _makeOne(self, c3=None, c3_ro=(), legacy_ro=()):
|
||||
from zope.interface.ro import _ROComparison
|
||||
return _ROComparison(c3 or self.MockC3(), c3_ro, legacy_ro)
|
||||
|
||||
def test_inconsistent_label(self):
|
||||
comp = self._makeOne()
|
||||
self.assertEqual('no', comp._inconsistent_label)
|
||||
|
||||
comp.c3.direct_inconsistency = True
|
||||
self.assertEqual("direct", comp._inconsistent_label)
|
||||
|
||||
comp.c3.bases_had_inconsistency = True
|
||||
self.assertEqual("direct+bases", comp._inconsistent_label)
|
||||
|
||||
comp.c3.direct_inconsistency = False
|
||||
self.assertEqual('bases', comp._inconsistent_label)
|
||||
@@ -0,0 +1,83 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Test interface sorting
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
from zope.interface import Interface
|
||||
|
||||
|
||||
class I1(Interface):
|
||||
pass
|
||||
|
||||
|
||||
class I2(I1):
|
||||
pass
|
||||
|
||||
|
||||
class I3(I1):
|
||||
pass
|
||||
|
||||
|
||||
class I4(Interface):
|
||||
pass
|
||||
|
||||
|
||||
class I5(I4):
|
||||
pass
|
||||
|
||||
|
||||
class I6(I2):
|
||||
pass
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def test(self):
|
||||
iface_list = [I1, I3, I5, I6, I4, I2]
|
||||
iface_list.sort()
|
||||
self.assertEqual(iface_list, [I1, I2, I3, I4, I5, I6])
|
||||
|
||||
def test_w_None(self):
|
||||
iface_list = [I1, None, I3, I5, I6, I4, I2]
|
||||
iface_list.sort()
|
||||
self.assertEqual(iface_list, [I1, I2, I3, I4, I5, I6, None])
|
||||
|
||||
def test_w_equal_names(self):
|
||||
# interfaces with equal names but different modules should sort by
|
||||
# module name
|
||||
from zope.interface.tests.m1 import I1 as m1_I1
|
||||
iface_list = [I1, m1_I1]
|
||||
iface_list.sort()
|
||||
self.assertEqual(iface_list, [m1_I1, I1])
|
||||
|
||||
def test_I1_I2(self):
|
||||
self.assertLess(I1.__name__, I2.__name__)
|
||||
self.assertEqual(I1.__module__, I2.__module__)
|
||||
self.assertEqual(I1.__module__, __name__)
|
||||
self.assertLess(I1, I2)
|
||||
|
||||
def _makeI1(self):
|
||||
|
||||
class I1(Interface):
|
||||
pass
|
||||
|
||||
return I1
|
||||
|
||||
def test_nested(self):
|
||||
nested_I1 = self._makeI1()
|
||||
self.assertEqual(I1, nested_I1)
|
||||
self.assertEqual(nested_I1, I1)
|
||||
self.assertEqual(hash(I1), hash(nested_I1))
|
||||
@@ -0,0 +1,656 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
""" zope.interface.verify unit tests
|
||||
"""
|
||||
import unittest
|
||||
|
||||
|
||||
# pylint:disable=inherit-non-class,no-method-argument,no-self-argument
|
||||
|
||||
class Test_verifyClass(unittest.TestCase):
|
||||
|
||||
verifier = None
|
||||
|
||||
def setUp(self):
|
||||
self.verifier = self._get_FUT()
|
||||
|
||||
@classmethod
|
||||
def _get_FUT(cls):
|
||||
from zope.interface.verify import verifyClass
|
||||
return verifyClass
|
||||
|
||||
def _adjust_object_before_verify(self, x):
|
||||
return x
|
||||
|
||||
def _callFUT(self, iface, klass, **kwargs):
|
||||
return self.verifier(iface,
|
||||
self._adjust_object_before_verify(klass),
|
||||
**kwargs)
|
||||
|
||||
def test_class_doesnt_implement(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface.exceptions import DoesNotImplement
|
||||
|
||||
class ICurrent(Interface):
|
||||
pass
|
||||
|
||||
class Current:
|
||||
pass
|
||||
|
||||
self.assertRaises(DoesNotImplement, self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_class_doesnt_implement_but_classImplements_later(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import classImplements
|
||||
|
||||
class ICurrent(Interface):
|
||||
pass
|
||||
|
||||
class Current:
|
||||
pass
|
||||
|
||||
classImplements(Current, ICurrent)
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_class_doesnt_have_required_method_simple(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
def method():
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_class_has_required_method_simple(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
def method():
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_class_doesnt_have_required_method_derived(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
|
||||
class IBase(Interface):
|
||||
def method():
|
||||
"""docstring"""
|
||||
|
||||
class IDerived(IBase):
|
||||
pass
|
||||
|
||||
@implementer(IDerived)
|
||||
class Current:
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenImplementation,
|
||||
self._callFUT, IDerived, Current)
|
||||
|
||||
def test_class_has_required_method_derived(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class IBase(Interface):
|
||||
def method():
|
||||
"""docstring"""
|
||||
|
||||
class IDerived(IBase):
|
||||
pass
|
||||
|
||||
@implementer(IDerived)
|
||||
class Current:
|
||||
|
||||
def method(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(IDerived, Current)
|
||||
|
||||
def test_method_takes_wrong_arg_names_but_OK(self):
|
||||
# We no longer require names to match.
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, b):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_not_enough_args(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_doesnt_take_required_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(*args):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_doesnt_take_required_only_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(**kw):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_takes_extra_arg(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, a, b):
|
||||
raise NotImplementedError()
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_takes_extra_arg_with_default(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, a, b=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_only_positional_args(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, *args):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_only_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, **kw):
|
||||
raise NotImplementedError()
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_takes_extra_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, a, *args):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_extra_starargs_and_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, a, *args, **kw):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_doesnt_take_required_positional_and_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a, *args):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, a):
|
||||
raise NotImplementedError()
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_takes_required_positional_and_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a, *args):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, a, *args):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_only_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a, *args):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, *args):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_required_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(**kwargs):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, **kw):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_positional_plus_required_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(*args):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, a, *args):
|
||||
raise NotImplementedError()
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_doesnt_take_required_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(**kwargs):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def method(self, a):
|
||||
raise NotImplementedError()
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_class_has_method_for_iface_attr(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = Attribute("The foo Attribute")
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def attr(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_class_has_nonmethod_for_method(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
def method():
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
method = 1
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_class_has_attribute_for_attribute(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = Attribute("The foo Attribute")
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
attr = 1
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_class_misses_attribute_for_attribute(self):
|
||||
# This check *passes* for verifyClass
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = Attribute("The foo Attribute")
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_w_callable_non_func_method(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.interface import Method
|
||||
|
||||
class QuasiMethod(Method):
|
||||
def __call__(self, *args, **kw):
|
||||
raise NotImplementedError()
|
||||
|
||||
class QuasiCallable:
|
||||
def __call__(self, *args, **kw):
|
||||
raise NotImplementedError()
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = QuasiMethod('This is callable')
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
attr = QuasiCallable()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_w_decorated_method(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
def decorator(func):
|
||||
# this is, in fact, zope.proxy.non_overridable
|
||||
return property(lambda self: func.__get__(self))
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
"""docstring"""
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
@decorator
|
||||
def method(self, a):
|
||||
raise NotImplementedError()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_dict_IFullMapping(self):
|
||||
# A dict should be an IFullMapping, but this exposes two
|
||||
# issues. First, on CPython, methods of builtin types are
|
||||
# "method_descriptor" objects, and are harder to introspect.
|
||||
# Second, on PyPy, the signatures can be just plain wrong,
|
||||
# specifying as required arguments that are actually optional.
|
||||
# See https://github.com/zopefoundation/zope.interface/issues/118
|
||||
from zope.interface.common.mapping import IFullMapping
|
||||
self._callFUT(IFullMapping, dict, tentative=True)
|
||||
|
||||
def test_list_ISequence(self):
|
||||
# As for test_dict_IFullMapping
|
||||
from zope.interface.common.sequence import ISequence
|
||||
self._callFUT(ISequence, list, tentative=True)
|
||||
|
||||
def test_tuple_IReadSequence(self):
|
||||
# As for test_dict_IFullMapping
|
||||
from zope.interface.common.sequence import IReadSequence
|
||||
self._callFUT(IReadSequence, tuple, tentative=True)
|
||||
|
||||
def test_multiple_invalid(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import classImplements
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
from zope.interface.exceptions import DoesNotImplement
|
||||
from zope.interface.exceptions import MultipleInvalid
|
||||
|
||||
class ISeveralMethods(Interface):
|
||||
def meth1(arg1):
|
||||
"Method 1"
|
||||
def meth2(arg1):
|
||||
"Method 2"
|
||||
|
||||
class SeveralMethods:
|
||||
pass
|
||||
|
||||
with self.assertRaises(MultipleInvalid) as exc:
|
||||
self._callFUT(ISeveralMethods, SeveralMethods)
|
||||
|
||||
ex = exc.exception
|
||||
self.assertEqual(3, len(ex.exceptions))
|
||||
self.assertIsInstance(ex.exceptions[0], DoesNotImplement)
|
||||
self.assertIsInstance(ex.exceptions[1], BrokenImplementation)
|
||||
self.assertIsInstance(ex.exceptions[2], BrokenImplementation)
|
||||
|
||||
# If everything else is correct, only the single error is raised
|
||||
# without the wrapper.
|
||||
classImplements(SeveralMethods, ISeveralMethods)
|
||||
SeveralMethods.meth1 = lambda self, arg1: "Hi"
|
||||
|
||||
with self.assertRaises(BrokenImplementation):
|
||||
self._callFUT(ISeveralMethods, SeveralMethods)
|
||||
|
||||
|
||||
class Test_verifyObject(Test_verifyClass):
|
||||
|
||||
@classmethod
|
||||
def _get_FUT(cls):
|
||||
from zope.interface.verify import verifyObject
|
||||
return verifyObject
|
||||
|
||||
def _adjust_object_before_verify(self, target):
|
||||
if isinstance(target, (type, type(OldSkool))):
|
||||
target = target()
|
||||
return target
|
||||
|
||||
def test_class_misses_attribute_for_attribute(self):
|
||||
# This check *fails* for verifyObject
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = Attribute("The foo Attribute")
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_module_hit(self):
|
||||
from zope.interface.tests import dummy
|
||||
from zope.interface.tests.idummy import IDummyModule
|
||||
|
||||
self._callFUT(IDummyModule, dummy)
|
||||
|
||||
def test_module_miss(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface.exceptions import DoesNotImplement
|
||||
from zope.interface.tests import dummy
|
||||
|
||||
# same name, different object
|
||||
class IDummyModule(Interface):
|
||||
pass
|
||||
|
||||
self.assertRaises(DoesNotImplement,
|
||||
self._callFUT, IDummyModule, dummy)
|
||||
|
||||
def test_staticmethod_hit_on_class(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import provider
|
||||
from zope.interface.verify import verifyObject
|
||||
|
||||
class IFoo(Interface):
|
||||
|
||||
def bar(a, b):
|
||||
"The bar method"
|
||||
|
||||
@provider(IFoo)
|
||||
class Foo:
|
||||
|
||||
@staticmethod
|
||||
def bar(a, b):
|
||||
raise AssertionError("We're never actually called")
|
||||
|
||||
# Don't use self._callFUT, we don't want to instantiate the
|
||||
# class.
|
||||
verifyObject(IFoo, Foo)
|
||||
|
||||
|
||||
class OldSkool:
|
||||
pass
|
||||
209
.venv/lib/python3.12/site-packages/zope/interface/verify.py
Normal file
209
.venv/lib/python3.12/site-packages/zope/interface/verify.py
Normal file
@@ -0,0 +1,209 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Verify interface implementations
|
||||
"""
|
||||
import inspect
|
||||
import sys
|
||||
from types import FunctionType
|
||||
from types import MethodType
|
||||
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
from zope.interface.exceptions import DoesNotImplement
|
||||
from zope.interface.exceptions import Invalid
|
||||
from zope.interface.exceptions import MultipleInvalid
|
||||
from zope.interface.interface import Method
|
||||
from zope.interface.interface import fromFunction
|
||||
from zope.interface.interface import fromMethod
|
||||
|
||||
|
||||
__all__ = [
|
||||
'verifyObject',
|
||||
'verifyClass',
|
||||
]
|
||||
|
||||
# This will be monkey-patched when running under Zope 2, so leave this
|
||||
# here:
|
||||
MethodTypes = (MethodType, )
|
||||
|
||||
|
||||
def _verify(iface, candidate, tentative=False, vtype=None):
|
||||
"""
|
||||
Verify that *candidate* might correctly provide *iface*.
|
||||
|
||||
This involves:
|
||||
|
||||
- Making sure the candidate claims that it provides the
|
||||
interface using ``iface.providedBy`` (unless *tentative* is `True`, in
|
||||
which case this step is skipped). This means that the candidate's class
|
||||
declares that it `implements <zope.interface.implementer>` the
|
||||
interface, or the candidate itself declares that it `provides
|
||||
<zope.interface.provider>`
|
||||
the interface
|
||||
|
||||
- Making sure the candidate defines all the necessary methods
|
||||
|
||||
- Making sure the methods have the correct signature (to the
|
||||
extent possible)
|
||||
|
||||
- Making sure the candidate defines all the necessary attributes
|
||||
|
||||
:return bool: Returns a true value if everything that could be
|
||||
checked passed.
|
||||
:raises zope.interface.Invalid: If any of the previous
|
||||
conditions does not hold.
|
||||
|
||||
.. versionchanged:: 5.0
|
||||
If multiple methods or attributes are invalid, all such errors
|
||||
are collected and reported. Previously, only the first error was
|
||||
reported. As a special case, if only one such error is present, it is
|
||||
raised alone, like before.
|
||||
"""
|
||||
|
||||
if vtype == 'c':
|
||||
tester = iface.implementedBy
|
||||
else:
|
||||
tester = iface.providedBy
|
||||
|
||||
excs = []
|
||||
if not tentative and not tester(candidate):
|
||||
excs.append(DoesNotImplement(iface, candidate))
|
||||
|
||||
for name, desc in iface.namesAndDescriptions(all=True):
|
||||
try:
|
||||
_verify_element(iface, name, desc, candidate, vtype)
|
||||
except Invalid as e:
|
||||
excs.append(e)
|
||||
|
||||
if excs:
|
||||
if len(excs) == 1:
|
||||
raise excs[0]
|
||||
raise MultipleInvalid(iface, candidate, excs)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _verify_element(iface, name, desc, candidate, vtype):
|
||||
# Here the `desc` is either an `Attribute` or `Method` instance
|
||||
try:
|
||||
attr = getattr(candidate, name)
|
||||
except AttributeError:
|
||||
|
||||
if (not isinstance(desc, Method)) and vtype == 'c':
|
||||
# We can't verify non-methods on classes, since the
|
||||
# class may provide attrs in it's __init__.
|
||||
return
|
||||
|
||||
# TODO: This should use ``raise...from``
|
||||
raise BrokenImplementation(iface, desc, candidate)
|
||||
|
||||
if not isinstance(desc, Method):
|
||||
# If it's not a method, there's nothing else we can test
|
||||
return
|
||||
|
||||
if inspect.ismethoddescriptor(attr) or inspect.isbuiltin(attr):
|
||||
# The first case is what you get for things like ``dict.pop``
|
||||
# on CPython (e.g., ``verifyClass(IFullMapping, dict))``). The
|
||||
# second case is what you get for things like ``dict().pop`` on
|
||||
# CPython (e.g., ``verifyObject(IFullMapping, dict()))``.
|
||||
# In neither case can we get a signature, so there's nothing
|
||||
# to verify. Even the inspect module gives up and raises
|
||||
# ValueError: no signature found. The ``__text_signature__`` attribute
|
||||
# isn't typically populated either.
|
||||
#
|
||||
# Note that on PyPy 2 or 3 (up through 7.3 at least), these are not
|
||||
# true for things like ``dict.pop`` (but might be true for C
|
||||
# extensions?)
|
||||
return
|
||||
|
||||
if isinstance(attr, FunctionType):
|
||||
|
||||
if isinstance(candidate, type) and vtype == 'c':
|
||||
# This is an "unbound method".
|
||||
# Only unwrap this if we're verifying implementedBy;
|
||||
# otherwise we can unwrap @staticmethod on classes that directly
|
||||
# provide an interface.
|
||||
meth = fromFunction(attr, iface, name=name, imlevel=1)
|
||||
else:
|
||||
# Nope, just a normal function
|
||||
meth = fromFunction(attr, iface, name=name)
|
||||
|
||||
elif (
|
||||
isinstance(attr, MethodTypes) and
|
||||
type(attr.__func__) is FunctionType
|
||||
):
|
||||
meth = fromMethod(attr, iface, name)
|
||||
|
||||
elif isinstance(attr, property) and vtype == 'c':
|
||||
# Without an instance we cannot be sure it's not a
|
||||
# callable.
|
||||
# TODO: This should probably check inspect.isdatadescriptor(),
|
||||
# a more general form than ``property``
|
||||
return
|
||||
|
||||
else:
|
||||
if not callable(attr):
|
||||
raise BrokenMethodImplementation(
|
||||
desc,
|
||||
"implementation is not a method",
|
||||
attr,
|
||||
iface,
|
||||
candidate
|
||||
)
|
||||
# sigh, it's callable, but we don't know how to introspect it, so
|
||||
# we have to give it a pass.
|
||||
return
|
||||
|
||||
# Make sure that the required and implemented method signatures are
|
||||
# the same.
|
||||
mess = _incompat(desc.getSignatureInfo(), meth.getSignatureInfo())
|
||||
if mess:
|
||||
raise BrokenMethodImplementation(desc, mess, attr, iface, candidate)
|
||||
|
||||
|
||||
def verifyClass(iface, candidate, tentative=False):
|
||||
"""
|
||||
Verify that the *candidate* might correctly provide *iface*.
|
||||
"""
|
||||
return _verify(iface, candidate, tentative, vtype='c')
|
||||
|
||||
|
||||
def verifyObject(iface, candidate, tentative=False):
|
||||
return _verify(iface, candidate, tentative, vtype='o')
|
||||
|
||||
|
||||
verifyObject.__doc__ = _verify.__doc__
|
||||
|
||||
_MSG_TOO_MANY = 'implementation requires too many arguments'
|
||||
|
||||
|
||||
def _incompat(required, implemented):
|
||||
# if (required['positional'] !=
|
||||
# implemented['positional'][:len(required['positional'])]
|
||||
# and implemented['kwargs'] is None):
|
||||
# return 'imlementation has different argument names'
|
||||
if len(implemented['required']) > len(required['required']):
|
||||
return _MSG_TOO_MANY
|
||||
|
||||
if (
|
||||
(len(implemented['positional']) < len(required['positional'])) and
|
||||
not implemented['varargs']
|
||||
):
|
||||
return "implementation doesn't allow enough arguments"
|
||||
|
||||
if required['kwargs'] and not implemented['kwargs']:
|
||||
return "implementation doesn't support keyword arguments"
|
||||
|
||||
if required['varargs'] and not implemented['varargs']:
|
||||
return "implementation doesn't support variable arguments"
|
||||
Reference in New Issue
Block a user