Primer commit

This commit is contained in:
Mongar28
2024-02-03 21:52:43 -05:00
commit b5c17f85f1
3325 changed files with 855400 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
##############################################################################
#
# 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
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
# The following are to make spec pickles cleaner
from zope.interface.declarations import Provides
from zope.interface.interfaces import IInterfaceDeclaration
moduleProvides(IInterfaceDeclaration)
__all__ = ('Interface', 'Attribute') + tuple(IInterfaceDeclaration)
assert all(k in globals() for k in __all__)

View File

@@ -0,0 +1,135 @@
##############################################################################
#
# 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.environ.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.environ.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 (only 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

View File

@@ -0,0 +1,35 @@
##############################################################################
#
# 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
##############################################################################
#
# 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

View File

@@ -0,0 +1,272 @@
##############################################################################
# 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 classImplements
from zope.interface import Interface
from zope.interface.interface import fromFunction
from zope.interface.interface import InterfaceClass
from zope.interface.interface import _decorator_non_return
__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(
"{}\n{}".format(k, 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 "`{}.{}`".format(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()

View File

@@ -0,0 +1,119 @@
##############################################################################
# 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 numbers
from zope.interface.common import io
__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 = ()

View File

@@ -0,0 +1,253 @@
##############################################################################
# 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 abc
from collections import OrderedDict
from collections import UserList
from collections import UserDict
from collections import UserString
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 is 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 = (basestring,) if str is bytes else () # pylint:disable=undefined-variable
@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)

View File

@@ -0,0 +1,606 @@
##############################################################################
# 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 timedelta, date, datetime, time, tzinfo
from zope.interface import Interface, Attribute
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):
"""Object 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-element 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__():
"""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() == '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)

View File

@@ -0,0 +1,208 @@
##############################################################################
#
# 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)
class IStandardError(IException):
"Interface for `StandardError` (no longer existing.)"
class IWarning(IException):
"Interface for `Warning`"
classImplements(Warning, IWarning)
class ISyntaxError(IStandardError):
"Interface for `SyntaxError`"
classImplements(SyntaxError, ISyntaxError)
class ILookupError(IStandardError):
"Interface for `LookupError`"
classImplements(LookupError, ILookupError)
class IValueError(IStandardError):
"Interface for `ValueError`"
classImplements(ValueError, IValueError)
class IRuntimeError(IStandardError):
"Interface for `RuntimeError`"
classImplements(RuntimeError, IRuntimeError)
class IArithmeticError(IStandardError):
"Interface for `ArithmeticError`"
classImplements(ArithmeticError, IArithmeticError)
class IAssertionError(IStandardError):
"Interface for `AssertionError`"
classImplements(AssertionError, IAssertionError)
class IAttributeError(IStandardError):
"Interface for `AttributeError`"
classImplements(AttributeError, IAttributeError)
class IDeprecationWarning(IWarning):
"Interface for `DeprecationWarning`"
classImplements(DeprecationWarning, IDeprecationWarning)
class IEOFError(IStandardError):
"Interface for `EOFError`"
classImplements(EOFError, IEOFError)
class IEnvironmentError(IStandardError):
"Interface for `EnvironmentError`"
classImplements(EnvironmentError, IEnvironmentError)
class IFloatingPointError(IArithmeticError):
"Interface for `FloatingPointError`"
classImplements(FloatingPointError, IFloatingPointError)
class IIOError(IEnvironmentError):
"Interface for `IOError`"
classImplements(IOError, IIOError)
class IImportError(IStandardError):
"Interface for `ImportError`"
classImplements(ImportError, IImportError)
class IIndentationError(ISyntaxError):
"Interface for `IndentationError`"
classImplements(IndentationError, IIndentationError)
class IIndexError(ILookupError):
"Interface for `IndexError`"
classImplements(IndexError, IIndexError)
class IKeyError(ILookupError):
"Interface for `KeyError`"
classImplements(KeyError, IKeyError)
class IKeyboardInterrupt(IStandardError):
"Interface for `KeyboardInterrupt`"
classImplements(KeyboardInterrupt, IKeyboardInterrupt)
class IMemoryError(IStandardError):
"Interface for `MemoryError`"
classImplements(MemoryError, IMemoryError)
class INameError(IStandardError):
"Interface for `NameError`"
classImplements(NameError, INameError)
class INotImplementedError(IRuntimeError):
"Interface for `NotImplementedError`"
classImplements(NotImplementedError, INotImplementedError)
class IOSError(IEnvironmentError):
"Interface for `OSError`"
classImplements(OSError, IOSError)
class IOverflowError(IArithmeticError):
"Interface for `ArithmeticError`"
classImplements(OverflowError, IOverflowError)
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)
class IRuntimeWarning(IWarning):
"Interface for `RuntimeWarning`"
classImplements(RuntimeWarning, IRuntimeWarning)
class IStopIteration(IException):
"Interface for `StopIteration`"
classImplements(StopIteration, IStopIteration)
class ISyntaxWarning(IWarning):
"Interface for `SyntaxWarning`"
classImplements(SyntaxWarning, ISyntaxWarning)
class ISystemError(IStandardError):
"Interface for `SystemError`"
classImplements(SystemError, ISystemError)
class ISystemExit(IException):
"Interface for `SystemExit`"
classImplements(SystemExit, ISystemExit)
class ITabError(IIndentationError):
"Interface for `TabError`"
classImplements(TabError, ITabError)
class ITypeError(IStandardError):
"Interface for `TypeError`"
classImplements(TypeError, ITypeError)
class IUnboundLocalError(INameError):
"Interface for `UnboundLocalError`"
classImplements(UnboundLocalError, IUnboundLocalError)
class IUnicodeError(IValueError):
"Interface for `UnicodeError`"
classImplements(UnicodeError, IUnicodeError)
class IUserWarning(IWarning):
"Interface for `UserWarning`"
classImplements(UserWarning, IUserWarning)
class IZeroDivisionError(IArithmeticError):
"Interface for `ZeroDivisionError`"
classImplements(ZeroDivisionError, IZeroDivisionError)

View File

@@ -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.
##############################################################################
"""
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

View File

@@ -0,0 +1,168 @@
##############################################################################
#
# 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``
"""

View File

@@ -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

View File

@@ -0,0 +1,189 @@
##############################################################################
#
# 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
"""

View File

@@ -0,0 +1,136 @@
##############################################################################
# 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.verify import verifyClass
from zope.interface.verify import verifyObject
from zope.interface.common import ABCInterface
from zope.interface.common import ABCInterfaceClass
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 ro
from zope.interface import implementedBy
from zope.interface import Interface
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))
result = constructor()
if hasattr(result, 'close'):
self.addCleanup(result.close)
return result

View File

@@ -0,0 +1,107 @@
##############################################################################
#
# 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.assertTrue(key in 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 = list(inst.keys()); inst_keys.sort()
state_keys = list(state.keys()) ; state_keys.sort()
self.assertEqual(inst_keys, state_keys)
def test_iter(self, inst, state):
# Return the keys of the mapping object
inst_keys = list(inst); inst_keys.sort()
state_keys = list(state.keys()) ; state_keys.sort()
self.assertEqual(inst_keys, state_keys)
def test_values(self, inst, state):
# Return the values of the mapping object
inst_values = list(inst.values()); inst_values.sort()
state_values = list(state.values()) ; state_values.sort()
self.assertEqual(inst_values, state_values)
def test_items(self, inst, state):
# Return the items of the mapping object
inst_items = list(inst.items()); inst_items.sort()
state_items = list(state.items()) ; state_items.sort()
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()

View File

@@ -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__)
}

View File

@@ -0,0 +1,142 @@
##############################################################################
# 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 unittest
from collections import abc
from collections import deque
from collections import OrderedDict
from types import MappingProxyType
from zope.interface import Invalid
# Note that importing z.i.c.collections does work on import.
from zope.interface.common import collections
from zope.interface._compat import PYPY
from . import add_abc_interface_tests
from . import VerifyClassMixin
from . import VerifyObjectMixin
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__)
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()),
}
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,
}

View File

@@ -0,0 +1,37 @@
##############################################################################
#
# 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 zope.interface.verify import verifyObject, verifyClass
from zope.interface.common.idatetime import ITimeDelta, ITimeDeltaClass
from zope.interface.common.idatetime import IDate, IDateClass
from zope.interface.common.idatetime import IDateTime, IDateTimeClass
from zope.interface.common.idatetime import ITime, ITimeClass, ITZInfo
from datetime import timedelta, date, datetime, time, tzinfo
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)

View File

@@ -0,0 +1,20 @@
##############################################################################
#
# 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)

View File

@@ -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 unittest
import io as abc
# Note that importing z.i.c.io does work on import.
from zope.interface.common import io
from . import add_abc_interface_tests
from . import VerifyClassMixin
from . import VerifyObjectMixin
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,
}

View File

@@ -0,0 +1,41 @@
##############################################################################
# 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
import numbers as abc
# Note that importing z.i.c.numbers does work on import.
from zope.interface.common import numbers
from . import add_abc_interface_tests
from . import VerifyClassMixin
from . import VerifyObjectMixin
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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,124 @@
##############################################################################
#
# 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(I, 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:
inline_literal = lambda s: "``{}``".format(s)
else:
inline_literal = lambda s: s
r = [inline_literal(I.getName())]
outp = r.append
level = 1
if I.getDoc():
outp(_justify_and_indent(_trim_doc_string(I.getDoc()), level))
bases = [base
for base in I.__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(I.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 = "{}{}".format(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(I, munge=0):
""" Output reStructuredText format. Note, this will whack any existing
'structured' format of the text."""
return asStructuredText(I, 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)

View File

@@ -0,0 +1,275 @@
##############################################################################
#
# 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 "The object {!r}".format(target)
@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
f = inspect.formatargspec
formatsig = lambda sig: f(*sig) # pylint:disable=deprecated-method
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 = ':' # We don't want a 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.
"""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,723 @@
##############################################################################
#
# 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.interfaces import ISpecification
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 ISubscriptionAdapterRegistration
from zope.interface.interfaces import IUtilityRegistration
from zope.interface.interfaces import Registered
from zope.interface.interfaces import Unregistered
from zope.interface.interface import Interface
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.adapter import AdapterRegistry
__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 "<{} {}>".format(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, self.provided, self.name, self.component, self.info,
self.factory
) = registry, provided, name, component, doc, 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,
)

View File

@@ -0,0 +1,665 @@
##############################################################################
#
# 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'
__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:
>>> _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.environ.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)
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
import warnings
warnings.warn(
"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.",
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, or ``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 = 'Legacy RO (len={})'.format(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):
"""
Check if the resolution order for *C*, as computed by :func:`ro`, is consistent
according to C3.
"""
return not C3.resolver(C, False, None).had_inconsistency

View File

@@ -0,0 +1,115 @@
from zope.interface._compat import _should_attempt_c_optimizations
class OptimizationTestMixin:
"""
Helper for testing that C optimizations are used
when appropriate.
"""
def _getTargetClass(self):
"""
Define this to return the implementation in use,
without the 'Py' or 'Fallback' suffix.
"""
raise NotImplementedError
def _getFallbackClass(self):
"""
Define this to 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 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

View File

@@ -0,0 +1,26 @@
##############################################################################
#
# 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())

View File

@@ -0,0 +1,23 @@
##############################################################################
#
# 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()

View File

@@ -0,0 +1,23 @@
##############################################################################
#
# 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.
"""

View File

@@ -0,0 +1,21 @@
##############################################################################
#
# 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, moduleProvides
class I1(Interface): pass
class I2(Interface): pass
moduleProvides(I1, I2)

View File

@@ -0,0 +1,124 @@
##############################################################################
#
# 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 "<odd class {} at {}>".format(self.__name__, 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

View File

@@ -0,0 +1,191 @@
##############################################################################
#
# 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 unittest
import sys
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.assertTrue(d is 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.assertTrue(d is 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")
self.assertTrue(f_locals is locals()) # ???
for d in module.__dict__, f_globals:
self.assertTrue(d is 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.assertTrue(_locals['f_locals'] is _locals)
self.assertTrue(_locals['module'] is None)
self.assertTrue(_locals['f_globals'] is _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])

View File

@@ -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

View File

@@ -0,0 +1,505 @@
##############################################################################
#
# 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 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
EXPECTED = '\n'.join([
"IEmpty",
"",
" This is an empty interface.",
" ",
(" It can be used to annotate any class or object, "
"because it 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_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
EXPECTED = '\n'.join([
"``IEmpty``",
"",
" This is an empty interface.",
" ",
(" It can be used to annotate any class or object, "
"because it 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)

View File

@@ -0,0 +1,31 @@
##############################################################################
#
# 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)

View File

@@ -0,0 +1,184 @@
##############################################################################
#
# 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

View File

@@ -0,0 +1,128 @@
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.assertTrue(event.object is 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 ro
from zope.interface import implementedBy
self.assertTrue(ro.is_consistent(implementedBy(self._getTargetClass())))

View File

@@ -0,0 +1,255 @@
##############################################################################
#
# 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.tests import odd
from zope.interface import Interface
from zope.interface import implementer
from zope.interface import directlyProvides
from zope.interface import providedBy
from zope.interface import directlyProvidedBy
from zope.interface import classImplements
from zope.interface import classImplementsOnly
from zope.interface import implementedBy
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.assertTrue(I1 in providedBy(c))
self.assertFalse(I3 in 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.assertFalse(I1 in providedBy(c))
self.assertFalse(I3 in 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.assertFalse(I1 in providedBy(c))
self.assertFalse(I3 in 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.assertTrue(I1 in providedBy(ob))
self.assertTrue(I2 in providedBy(ob))
self.assertTrue(IA1 in providedBy(ob))
self.assertTrue(IA2 in providedBy(ob))
self.assertTrue(IB in providedBy(ob))
self.assertTrue(IC in providedBy(ob))
directlyProvides(ob, directlyProvidedBy(ob)-I2)
self.assertTrue(I1 in providedBy(ob))
self.assertFalse(I2 in providedBy(ob))
self.assertFalse(I2 in providedBy(ob))
directlyProvides(ob, directlyProvidedBy(ob), I2)
self.assertTrue(I2 in 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

View File

@@ -0,0 +1,426 @@
##############################################################################
#
# 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,protected-access,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 implementer
from zope.interface import Interface
from zope.interface import providedBy
from zope.interface import implementedBy
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
O = base
class F(O):
pass
class E(O):
pass
class D(O):
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]
self.assertEqual('\n'.join(l.rstrip() for l in record.getMessage().splitlines()), """\
Object <InterfaceClass zope.interface.tests.test_ro.A> has different legacy and C3 MROs:
Legacy RO (len=7) C3 RO (len=7; inconsistent=no)
==================================================================
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""")
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.common import interfaces
from zope.interface import providedBy
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)

View File

@@ -0,0 +1,64 @@
##############################################################################
#
# 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):
l = [I1, I3, I5, I6, I4, I2]
l.sort()
self.assertEqual(l, [I1, I2, I3, I4, I5, I6])
def test_w_None(self):
l = [I1, None, I3, I5, I6, I4, I2]
l.sort()
self.assertEqual(l, [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
l = [I1, m1_I1]
l.sort()
self.assertEqual(l, [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))

Some files were not shown because too many files have changed in this diff Show More