123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- ##############################################################################
- # Copyright (c) 2020 Zope Foundation and Contributors.
- # All Rights Reserved.
- #
- # This software is subject to the provisions of the Zope Public License,
- # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
- # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
- # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
- # FOR A PARTICULAR PURPOSE.
- ##############################################################################
- import itertools
- from types import FunctionType
- from zope.interface import Interface
- from zope.interface import classImplements
- from zope.interface.interface import InterfaceClass
- from zope.interface.interface import _decorator_non_return
- from zope.interface.interface import fromFunction
- __all__ = [
- # Nothing public here.
- ]
- # pylint:disable=inherit-non-class,
- # pylint:disable=no-self-argument,no-method-argument
- # pylint:disable=unexpected-special-method-signature
- class optional:
- # Apply this decorator to a method definition to make it
- # optional (remove it from the list of required names), overriding
- # the definition inherited from the ABC.
- def __init__(self, method):
- self.__doc__ = method.__doc__
- class ABCInterfaceClass(InterfaceClass):
- """
- An interface that is automatically derived from a
- :class:`abc.ABCMeta` type.
- Internal use only.
- The body of the interface definition *must* define
- a property ``abc`` that is the ABC to base the interface on.
- If ``abc`` is *not* in the interface definition, a regular
- interface will be defined instead (but ``extra_classes`` is still
- respected).
- Use the ``@optional`` decorator on method definitions if
- the ABC defines methods that are not actually required in all cases
- because the Python language has multiple ways to implement a protocol.
- For example, the ``iter()`` protocol can be implemented with
- ``__iter__`` or the pair ``__len__`` and ``__getitem__``.
- When created, any existing classes that are registered to conform
- to the ABC are declared to implement this interface. This is *not*
- automatically updated as the ABC registry changes. If the body of the
- interface definition defines ``extra_classes``, it should be a
- tuple giving additional classes to declare implement the interface.
- Note that this is not fully symmetric. For example, it is usually
- the case that a subclass relationship carries the interface
- declarations over::
- >>> from zope.interface import Interface
- >>> class I1(Interface):
- ... pass
- ...
- >>> from zope.interface import implementer
- >>> @implementer(I1)
- ... class Root(object):
- ... pass
- ...
- >>> class Child(Root):
- ... pass
- ...
- >>> child = Child()
- >>> isinstance(child, Root)
- True
- >>> from zope.interface import providedBy
- >>> list(providedBy(child))
- [<InterfaceClass __main__.I1>]
- However, that's not the case with ABCs and ABC interfaces. Just
- because ``isinstance(A(), AnABC)`` and ``isinstance(B(), AnABC)``
- are both true, that doesn't mean there's any class hierarchy
- relationship between ``A`` and ``B``, or between either of them
- and ``AnABC``. Thus, if ``AnABC`` implemented ``IAnABC``, it would
- not follow that either ``A`` or ``B`` implements ``IAnABC`` (nor
- their instances provide it)::
- >>> class SizedClass(object):
- ... def __len__(self): return 1
- ...
- >>> from collections.abc import Sized
- >>> isinstance(SizedClass(), Sized)
- True
- >>> from zope.interface import classImplements
- >>> classImplements(Sized, I1)
- None
- >>> list(providedBy(SizedClass()))
- []
- Thus, to avoid conflicting assumptions, ABCs should not be
- declared to implement their parallel ABC interface. Only concrete
- classes specifically registered with the ABC should be declared to
- do so.
- .. versionadded:: 5.0.0
- """
- # If we could figure out invalidation, and used some special
- # Specification/Declaration instances, and override the method
- # ``providedBy`` here, perhaps we could more closely integrate with ABC
- # virtual inheritance?
- def __init__(self, name, bases, attrs):
- # go ahead and give us a name to ease debugging.
- self.__name__ = name
- extra_classes = attrs.pop('extra_classes', ())
- ignored_classes = attrs.pop('ignored_classes', ())
- if 'abc' not in attrs:
- # Something like ``IList(ISequence)``: We're extending
- # abc interfaces but not an ABC interface ourself.
- InterfaceClass.__init__(self, name, bases, attrs)
- ABCInterfaceClass.__register_classes(
- self, extra_classes, ignored_classes,
- )
- self.__class__ = InterfaceClass
- return
- based_on = attrs.pop('abc')
- self.__abc = based_on
- self.__extra_classes = tuple(extra_classes)
- self.__ignored_classes = tuple(ignored_classes)
- assert name[1:] == based_on.__name__, (name, based_on)
- methods = {
- # Passing the name is important in case of aliases,
- # e.g., ``__ror__ = __or__``.
- k: self.__method_from_function(v, k)
- for k, v in vars(based_on).items()
- if isinstance(v, FunctionType) and
- not self.__is_private_name(k) and
- not self.__is_reverse_protocol_name(k)
- }
- methods['__doc__'] = self.__create_class_doc(attrs)
- # Anything specified in the body takes precedence.
- methods.update(attrs)
- InterfaceClass.__init__(self, name, bases, methods)
- self.__register_classes()
- @staticmethod
- def __optional_methods_to_docs(attrs):
- optionals = {k: v for k, v in attrs.items() if isinstance(v, optional)}
- for k in optionals:
- attrs[k] = _decorator_non_return
- if not optionals:
- return ''
- docs = "\n\nThe following methods are optional:\n - " + "\n-".join(
- f"{k}\n{v.__doc__}" for k, v in optionals.items()
- )
- return docs
- def __create_class_doc(self, attrs):
- based_on = self.__abc
- def ref(c):
- mod = c.__module__
- name = c.__name__
- if mod == str.__module__:
- return "`%s`" % name
- if mod == '_io':
- mod = 'io'
- return f"`{mod}.{name}`"
- implementations_doc = "\n - ".join(
- ref(c)
- for c in sorted(self.getRegisteredConformers(), key=ref)
- )
- if implementations_doc:
- implementations_doc = (
- "\n\nKnown implementations are:\n\n - " + implementations_doc
- )
- based_on_doc = (based_on.__doc__ or '')
- based_on_doc = based_on_doc.splitlines()
- based_on_doc = based_on_doc[0] if based_on_doc else ''
- doc = """Interface for the ABC `{}.{}`.\n\n{}{}{}""".format(
- based_on.__module__, based_on.__name__,
- attrs.get('__doc__', based_on_doc),
- self.__optional_methods_to_docs(attrs),
- implementations_doc
- )
- return doc
- @staticmethod
- def __is_private_name(name):
- if name.startswith('__') and name.endswith('__'):
- return False
- return name.startswith('_')
- @staticmethod
- def __is_reverse_protocol_name(name):
- # The reverse names, like __rand__,
- # aren't really part of the protocol. The interpreter has
- # very complex behaviour around invoking those. PyPy
- # doesn't always even expose them as attributes.
- return name.startswith('__r') and name.endswith('__')
- def __method_from_function(self, function, name):
- method = fromFunction(function, self, name=name)
- # Eliminate the leading *self*, which is implied in
- # an interface, but explicit in an ABC.
- method.positional = method.positional[1:]
- return method
- def __register_classes(self, conformers=None, ignored_classes=None):
- # Make the concrete classes already present in our ABC's registry
- # declare that they implement this interface.
- conformers = (
- conformers if conformers is not None
- else self.getRegisteredConformers()
- )
- ignored = (
- ignored_classes if ignored_classes is not None
- else self.__ignored_classes
- )
- for cls in conformers:
- if cls in ignored:
- continue
- classImplements(cls, self)
- def getABC(self):
- """
- Return the ABC this interface represents.
- """
- return self.__abc
- def getRegisteredConformers(self):
- """
- Return an iterable of the classes that are known to conform to
- the ABC this interface parallels.
- """
- based_on = self.__abc
- # The registry only contains things that aren't already
- # known to be subclasses of the ABC. But the ABC is in charge
- # of checking that, so its quite possible that registrations
- # are in fact ignored, winding up just in the _abc_cache.
- try:
- registered = (
- list(based_on._abc_registry) + list(based_on._abc_cache)
- )
- except AttributeError:
- # Rewritten in C in CPython 3.7.
- # These expose the underlying weakref.
- from abc import _get_dump
- data = _get_dump(based_on)
- registry = data[0]
- cache = data[1]
- registered = [x() for x in itertools.chain(registry, cache)]
- registered = [x for x in registered if x is not None]
- return set(itertools.chain(registered, self.__extra_classes))
- def _create_ABCInterface():
- # It's a two-step process to create the root ABCInterface, because without
- # specifying a corresponding ABC, using the normal constructor gets us a
- # plain InterfaceClass object, and there is no ABC to associate with the
- # root.
- abc_name_bases_attrs = ('ABCInterface', (Interface,), {})
- instance = ABCInterfaceClass.__new__(
- ABCInterfaceClass, *abc_name_bases_attrs,
- )
- InterfaceClass.__init__(instance, *abc_name_bases_attrs)
- return instance
- ABCInterface = _create_ABCInterface()
|