1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219 |
- ##############################################################################
- # 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.
- ##############################################################################
- """Implementation of interface declarations
- There are three flavors of declarations:
- - Declarations are used to simply name declared interfaces.
- - ImplementsDeclarations are used to express the interfaces that a
- class implements (that instances of the class provides).
- Implements specifications support inheriting interfaces.
- - ProvidesDeclarations are used to express interfaces directly
- provided by objects.
- """
- __docformat__ = 'restructuredtext'
- import sys
- import weakref
- from types import FunctionType
- from types import MethodType
- from types import ModuleType
- from zope.interface._compat import _use_c_impl
- from zope.interface.interface import Interface
- from zope.interface.interface import InterfaceBase
- from zope.interface.interface import InterfaceClass
- from zope.interface.interface import NameAndModuleComparisonMixin
- from zope.interface.interface import Specification
- from zope.interface.interface import SpecificationBase
- __all__ = [
- # None. The public APIs of this module are
- # re-exported from zope.interface directly.
- ]
- # pylint:disable=too-many-lines
- # Registry of class-implementation specifications
- BuiltinImplementationSpecifications = {}
- def _next_super_class(ob):
- # When ``ob`` is an instance of ``super``, return
- # the next class in the MRO that we should actually be
- # looking at. Watch out for diamond inheritance!
- self_class = ob.__self_class__
- class_that_invoked_super = ob.__thisclass__
- complete_mro = self_class.__mro__
- next_class = complete_mro[complete_mro.index(class_that_invoked_super) + 1]
- return next_class
- class named:
- def __init__(self, name):
- self.name = name
- def __call__(self, ob):
- ob.__component_name__ = self.name
- return ob
- class Declaration(Specification):
- """Interface declarations"""
- __slots__ = ()
- def __init__(self, *bases):
- Specification.__init__(self, _normalizeargs(bases))
- def __contains__(self, interface):
- """Test whether an interface is in the specification
- """
- return self.extends(interface) and interface in self.interfaces()
- def __iter__(self):
- """Return an iterator for the interfaces in the specification
- """
- return self.interfaces()
- def flattened(self):
- """Return an iterator of all included and extended interfaces
- """
- return iter(self.__iro__)
- def __sub__(self, other):
- """Remove interfaces from a specification
- """
- return Declaration(*[
- i for i in self.interfaces()
- if not [
- j
- for j in other.interfaces()
- if i.extends(j, 0) # non-strict extends
- ]
- ])
- def __add__(self, other):
- """
- Add two specifications or a specification and an interface
- and produce a new declaration.
- .. versionchanged:: 5.4.0
- Now tries to preserve a consistent resolution order. Interfaces
- being added to this object are added to the front of the resulting
- resolution order if they already extend an interface in this
- object. Previously, they were always added to the end of the order,
- which easily resulted in invalid orders.
- """
- before = []
- result = list(self.interfaces())
- seen = set(result)
- for i in other.interfaces():
- if i in seen:
- continue
- seen.add(i)
- if any(i.extends(x) for x in result):
- # It already extends us, e.g., is a subclass,
- # so it needs to go at the front of the RO.
- before.append(i)
- else:
- result.append(i)
- return Declaration(*(before + result))
- # XXX: Is __radd__ needed? No tests break if it's removed.
- # If it is needed, does it need to handle the C3 ordering differently?
- # I (JAM) don't *think* it does.
- __radd__ = __add__
- @staticmethod
- def _add_interfaces_to_cls(interfaces, cls):
- # Strip redundant interfaces already provided
- # by the cls so we don't produce invalid
- # resolution orders.
- implemented_by_cls = implementedBy(cls)
- interfaces = tuple([
- iface
- for iface in interfaces
- if not implemented_by_cls.isOrExtends(iface)
- ])
- return interfaces + (implemented_by_cls,)
- @staticmethod
- def _argument_names_for_repr(interfaces):
- # These don't actually have to be interfaces, they could be other
- # Specification objects like Implements. Also, the first
- # one is typically/nominally the cls.
- ordered_names = []
- names = set()
- for iface in interfaces:
- duplicate_transform = repr
- if isinstance(iface, InterfaceClass):
- # Special case to get 'foo.bar.IFace'
- # instead of '<InterfaceClass foo.bar.IFace>'
- this_name = iface.__name__
- duplicate_transform = str
- elif isinstance(iface, type):
- # Likewise for types. (Ignoring legacy old-style
- # classes.)
- this_name = iface.__name__
- duplicate_transform = _implements_name
- elif (
- isinstance(iface, Implements) and
- not iface.declared and
- iface.inherit in interfaces
- ):
- # If nothing is declared, there's no need to even print this;
- # it would just show as ``classImplements(Class)``, and the
- # ``Class`` has typically already.
- continue
- else:
- this_name = repr(iface)
- already_seen = this_name in names
- names.add(this_name)
- if already_seen:
- this_name = duplicate_transform(iface)
- ordered_names.append(this_name)
- return ', '.join(ordered_names)
- class _ImmutableDeclaration(Declaration):
- # A Declaration that is immutable. Used as a singleton to
- # return empty answers for things like ``implementedBy``.
- # We have to define the actual singleton after normalizeargs
- # is defined, and that in turn is defined after InterfaceClass and
- # Implements.
- __slots__ = ()
- __instance = None
- def __new__(cls):
- if _ImmutableDeclaration.__instance is None:
- _ImmutableDeclaration.__instance = object.__new__(cls)
- return _ImmutableDeclaration.__instance
- def __reduce__(self):
- return "_empty"
- @property
- def __bases__(self):
- return ()
- @__bases__.setter
- def __bases__(self, new_bases):
- # We expect the superclass constructor to set ``self.__bases__ = ()``.
- # Rather than attempt to special case that in the constructor and
- # allow setting __bases__ only at that time, it's easier to just allow
- # setting the empty tuple at any time. That makes ``x.__bases__ =
- # x.__bases__`` a nice no-op too. (Skipping the superclass constructor
- # altogether is a recipe for maintenance headaches.)
- if new_bases != ():
- raise TypeError(
- "Cannot set non-empty bases on shared empty Declaration."
- )
- # As the immutable empty declaration, we cannot be changed.
- # This means there's no logical reason for us to have dependents
- # or subscriptions: we'll never notify them. So there's no need for
- # us to keep track of any of that.
- @property
- def dependents(self):
- return {}
- changed = subscribe = unsubscribe = lambda self, _ignored: None
- def interfaces(self):
- # An empty iterator
- return iter(())
- def extends(self, interface, strict=True):
- return interface is self._ROOT
- def get(self, name, default=None):
- return default
- def weakref(self, callback=None):
- # We're a singleton, we never go away. So there's no need to return
- # distinct weakref objects here; their callbacks will never be called.
- # Instead, we only need to return a callable that returns ourself. The
- # easiest one is to return _ImmutableDeclaration itself; testing on
- # Python 3.8 shows that's faster than a function that returns _empty.
- # (Remember, one goal is to avoid allocating any object, and that
- # includes a method.)
- return _ImmutableDeclaration
- @property
- def _v_attrs(self):
- # _v_attrs is not a public, documented property, but some client code
- # uses it anyway as a convenient place to cache things. To keep the
- # empty declaration truly immutable, we must ignore that. That
- # includes ignoring assignments as well.
- return {}
- @_v_attrs.setter
- def _v_attrs(self, new_attrs):
- pass
- ##############################################################################
- #
- # Implementation specifications
- #
- # These specify interfaces implemented by instances of classes
- class Implements(NameAndModuleComparisonMixin,
- Declaration):
- # Inherit from NameAndModuleComparisonMixin to be mutually comparable with
- # InterfaceClass objects. (The two must be mutually comparable to be able
- # to work in e.g., BTrees.) Instances of this class generally don't have a
- # __module__ other than `zope.interface.declarations`, whereas they *do*
- # have a __name__ that is the fully qualified name of the object they are
- # representing.
- # Note, though, that equality and hashing are still identity based. This
- # accounts for things like nested objects that have the same name
- # (typically only in tests) and is consistent with pickling. As far as
- # comparisons to InterfaceClass goes, we'll never have equal name and
- # module to those, so we're still consistent there. Instances of this
- # class are essentially intended to be unique and are heavily cached (note
- # how our __reduce__ handles this) so having identity based hash and eq
- # should also work.
- # We want equality and hashing to be based on identity. However, we can't
- # actually implement __eq__/__ne__ to do this because sometimes we get
- # wrapped in a proxy. We need to let the proxy types implement these
- # methods so they can handle unwrapping and then rely on: (1) the
- # interpreter automatically changing `implements == proxy` into `proxy ==
- # implements` (which will call proxy.__eq__ to do the unwrapping) and then
- # (2) the default equality and hashing semantics being identity based.
- # class whose specification should be used as additional base
- inherit = None
- # interfaces actually declared for a class
- declared = ()
- # Weak cache of {class: <implements>} for super objects.
- # Created on demand. These are rare, as of 5.0 anyway. Using a class
- # level default doesn't take space in instances. Using _v_attrs would be
- # another place to store this without taking space unless needed.
- _super_cache = None
- __name__ = '?'
- @classmethod
- def named(cls, name, *bases):
- # Implementation method: Produce an Implements interface with a fully
- # fleshed out __name__ before calling the constructor, which sets
- # bases to the given interfaces and which may pass this object to
- # other objects (e.g., to adjust dependents). If they're sorting or
- # comparing by name, this needs to be set.
- inst = cls.__new__(cls)
- inst.__name__ = name
- inst.__init__(*bases)
- return inst
- def changed(self, originally_changed):
- try:
- del self._super_cache
- except AttributeError:
- pass
- return super().changed(originally_changed)
- def __repr__(self):
- if self.inherit:
- name = (
- getattr(self.inherit, '__name__', None) or
- _implements_name(self.inherit)
- )
- else:
- name = self.__name__
- declared_names = self._argument_names_for_repr(self.declared)
- if declared_names:
- declared_names = ', ' + declared_names
- return f'classImplements({name}{declared_names})'
- def __reduce__(self):
- return implementedBy, (self.inherit, )
- def _implements_name(ob):
- # Return the __name__ attribute to be used by its __implemented__
- # property.
- # This must be stable for the "same" object across processes
- # because it is used for sorting. It needn't be unique, though, in cases
- # like nested classes named Foo created by different functions, because
- # equality and hashing is still based on identity.
- # It might be nice to use __qualname__ on Python 3, but that would produce
- # different values between Py2 and Py3.
- # Special-case 'InterfaceBase': its '__module__' member descriptor
- # behaves differently across Python 3.x versions.
- if ob is InterfaceBase:
- return 'zope.interface.interface.InterfaceBase'
- return (getattr(ob, '__module__', '?') or '?') + \
- '.' + (getattr(ob, '__name__', '?') or '?')
- def _implementedBy_super(sup):
- # TODO: This is now simple enough we could probably implement
- # in C if needed.
- # If the class MRO is strictly linear, we could just
- # follow the normal algorithm for the next class in the
- # search order (e.g., just return
- # ``implemented_by_next``). But when diamond inheritance
- # or mixins + interface declarations are present, we have
- # to consider the whole MRO and compute a new Implements
- # that excludes the classes being skipped over but
- # includes everything else.
- implemented_by_self = implementedBy(sup.__self_class__)
- cache = implemented_by_self._super_cache # pylint:disable=protected-access
- if cache is None:
- cache = implemented_by_self._super_cache = weakref.WeakKeyDictionary()
- key = sup.__thisclass__
- try:
- return cache[key]
- except KeyError:
- pass
- next_cls = _next_super_class(sup)
- # For ``implementedBy(cls)``:
- # .__bases__ is .declared + [implementedBy(b) for b in cls.__bases__]
- # .inherit is cls
- implemented_by_next = implementedBy(next_cls)
- mro = sup.__self_class__.__mro__
- ix_next_cls = mro.index(next_cls)
- classes_to_keep = mro[ix_next_cls:]
- new_bases = [implementedBy(c) for c in classes_to_keep]
- new = Implements.named(
- implemented_by_self.__name__ + ':' + implemented_by_next.__name__,
- *new_bases
- )
- new.inherit = implemented_by_next.inherit
- new.declared = implemented_by_next.declared
- # I don't *think* that new needs to subscribe to ``implemented_by_self``;
- # it auto-subscribed to its bases, and that should be good enough.
- cache[key] = new
- return new
- @_use_c_impl
- def implementedBy(
- cls
- ): # pylint:disable=too-many-return-statements,too-many-branches
- """Return the interfaces implemented for a class' instances
- The value returned is an `~zope.interface.interfaces.IDeclaration`.
- """
- try:
- if isinstance(cls, super):
- # Yes, this needs to be inside the try: block. Some objects
- # like security proxies even break isinstance.
- return _implementedBy_super(cls)
- spec = cls.__dict__.get('__implemented__')
- except AttributeError:
- # we can't get the class dict. This is probably due to a
- # security proxy. If this is the case, then probably no
- # descriptor was installed for the class.
- # We don't want to depend directly on zope.security in
- # zope.interface, but we'll try to make reasonable
- # accommodations in an indirect way.
- # We'll check to see if there's an implements:
- spec = getattr(cls, '__implemented__', None)
- if spec is None:
- # There's no spec stred in the class. Maybe its a builtin:
- spec = BuiltinImplementationSpecifications.get(cls)
- if spec is not None:
- return spec
- return _empty
- if spec.__class__ == Implements:
- # we defaulted to _empty or there was a spec. Good enough.
- # Return it.
- return spec
- # TODO: need old style __implements__ compatibility?
- # Hm, there's an __implemented__, but it's not a spec. Must be
- # an old-style declaration. Just compute a spec for it
- return Declaration(*_normalizeargs((spec, )))
- if isinstance(spec, Implements):
- return spec
- if spec is None:
- spec = BuiltinImplementationSpecifications.get(cls)
- if spec is not None:
- return spec
- # TODO: need old style __implements__ compatibility?
- spec_name = _implements_name(cls)
- if spec is not None:
- # old-style __implemented__ = foo declaration
- spec = (spec, ) # tuplefy, as it might be just an int
- spec = Implements.named(spec_name, *_normalizeargs(spec))
- spec.inherit = None # old-style implies no inherit
- del cls.__implemented__ # get rid of the old-style declaration
- else:
- try:
- bases = cls.__bases__
- except AttributeError:
- if not callable(cls):
- raise TypeError("ImplementedBy called for non-factory", cls)
- bases = ()
- spec = Implements.named(spec_name, *[implementedBy(c) for c in bases])
- spec.inherit = cls
- try:
- cls.__implemented__ = spec
- if not hasattr(cls, '__providedBy__'):
- cls.__providedBy__ = objectSpecificationDescriptor
- if isinstance(cls, type) and '__provides__' not in cls.__dict__:
- # Make sure we get a __provides__ descriptor
- cls.__provides__ = ClassProvides(
- cls, getattr(cls, '__class__', type(cls)),
- )
- except TypeError:
- if not isinstance(cls, type):
- raise TypeError("ImplementedBy called for non-type", cls)
- BuiltinImplementationSpecifications[cls] = spec
- return spec
- def classImplementsOnly(cls, *interfaces):
- """
- Declare the only interfaces implemented by instances of a class
- The arguments after the class are one or more interfaces or interface
- specifications (`~zope.interface.interfaces.IDeclaration` objects).
- The interfaces given (including the interfaces in the specifications)
- replace any previous declarations, *including* inherited definitions. If
- you wish to preserve inherited declarations, you can pass
- ``implementedBy(cls)`` in *interfaces*. This can be used to alter the
- interface resolution order.
- """
- spec = implementedBy(cls)
- # Clear out everything inherited. It's important to
- # also clear the bases right now so that we don't improperly discard
- # interfaces that are already implemented by *old* bases that we're
- # about to get rid of.
- spec.declared = ()
- spec.inherit = None
- spec.__bases__ = ()
- _classImplements_ordered(spec, interfaces, ())
- def classImplements(cls, *interfaces):
- """
- Declare additional interfaces implemented for instances of a class
- The arguments after the class are one or more interfaces or interface
- specifications (`~zope.interface.interfaces.IDeclaration` objects).
- The interfaces given (including the interfaces in the specifications)
- are added to any interfaces previously declared. An effort is made to
- keep a consistent C3 resolution order, but this cannot be guaranteed.
- .. versionchanged:: 5.0.0
- Each individual interface in *interfaces* may be added to either the
- beginning or end of the list of interfaces declared for *cls*,
- based on inheritance, in order to try to maintain a consistent
- resolution order. Previously, all interfaces were added to the end.
- .. versionchanged:: 5.1.0
- If *cls* is already declared to implement an interface (or derived
- interface) in *interfaces* through inheritance, the interface is
- ignored. Previously, it would redundantly be made direct base of *cls*,
- which often produced inconsistent interface resolution orders. Now, the
- order will be consistent, but may change. Also, if the ``__bases__``
- of the *cls* are later changed, the *cls* will no longer be considered
- to implement such an interface (changing the ``__bases__`` of *cls* has
- never been supported).
- """
- spec = implementedBy(cls)
- interfaces = tuple(_normalizeargs(interfaces))
- before = []
- after = []
- # Take steps to try to avoid producing an invalid resolution
- # order, while still allowing for BWC (in the past, we always
- # appended)
- for iface in interfaces:
- for b in spec.declared:
- if iface.extends(b):
- before.append(iface)
- break
- else:
- after.append(iface)
- _classImplements_ordered(spec, tuple(before), tuple(after))
- def classImplementsFirst(cls, iface):
- """
- Declare that instances of *cls* additionally provide *iface*.
- The second argument is an interface or interface specification.
- It is added as the highest priority (first in the IRO) interface;
- no attempt is made to keep a consistent resolution order.
- .. versionadded:: 5.0.0
- """
- spec = implementedBy(cls)
- _classImplements_ordered(spec, (iface,), ())
- def _classImplements_ordered(spec, before=(), after=()):
- # Elide everything already inherited.
- # Except, if it is the root, and we don't already declare anything else
- # that would imply it, allow the root through. (TODO: When we disallow
- # non-strict IRO, this part of the check can be removed because it's not
- # possible to re-declare like that.)
- before = [
- x
- for x in before
- if not spec.isOrExtends(x) or (x is Interface and not spec.declared)
- ]
- after = [
- x
- for x in after
- if not spec.isOrExtends(x) or (x is Interface and not spec.declared)
- ]
- # eliminate duplicates
- new_declared = []
- seen = set()
- for lst in before, spec.declared, after:
- for b in lst:
- if b not in seen:
- new_declared.append(b)
- seen.add(b)
- spec.declared = tuple(new_declared)
- # compute the bases
- bases = new_declared # guaranteed no dupes
- if spec.inherit is not None:
- for c in spec.inherit.__bases__:
- b = implementedBy(c)
- if b not in seen:
- seen.add(b)
- bases.append(b)
- spec.__bases__ = tuple(bases)
- def _implements_advice(cls):
- interfaces, do_classImplements = cls.__dict__['__implements_advice_data__']
- del cls.__implements_advice_data__
- do_classImplements(cls, *interfaces)
- return cls
- class implementer:
- """
- Declare the interfaces implemented by instances of a class.
- This function is called as a class decorator.
- The arguments are one or more interfaces or interface specifications
- (`~zope.interface.interfaces.IDeclaration` objects).
- The interfaces given (including the interfaces in the specifications) are
- added to any interfaces previously declared, unless the interface is
- already implemented.
- Previous declarations include declarations for base classes unless
- implementsOnly was used.
- This function is provided for convenience. It provides a more convenient
- way to call `classImplements`. For example::
- @implementer(I1)
- class C(object):
- pass
- is equivalent to calling::
- classImplements(C, I1)
- after the class has been created.
- .. seealso:: `classImplements`
- The change history provided there applies to this function too.
- """
- __slots__ = ('interfaces',)
- def __init__(self, *interfaces):
- self.interfaces = interfaces
- def __call__(self, ob):
- if isinstance(ob, type):
- # This is the common branch for classes.
- classImplements(ob, *self.interfaces)
- return ob
- spec_name = _implements_name(ob)
- spec = Implements.named(spec_name, *self.interfaces)
- try:
- ob.__implemented__ = spec
- except AttributeError:
- raise TypeError("Can't declare implements", ob)
- return ob
- class implementer_only:
- """Declare the only interfaces implemented by instances of a class
- This function is called as a class decorator.
- The arguments are one or more interfaces or interface
- specifications (`~zope.interface.interfaces.IDeclaration` objects).
- Previous declarations including declarations for base classes
- are overridden.
- This function is provided for convenience. It provides a more
- convenient way to call `classImplementsOnly`. For example::
- @implementer_only(I1)
- class C(object): pass
- is equivalent to calling::
- classImplementsOnly(I1)
- after the class has been created.
- """
- def __init__(self, *interfaces):
- self.interfaces = interfaces
- def __call__(self, ob):
- if isinstance(ob, (FunctionType, MethodType)):
- # XXX Does this decorator make sense for anything but classes?
- # I don't think so. There can be no inheritance of interfaces
- # on a method or function....
- raise ValueError('The implementer_only decorator is not '
- 'supported for methods or functions.')
- # Assume it's a class:
- classImplementsOnly(ob, *self.interfaces)
- return ob
- ##############################################################################
- #
- # Instance declarations
- class Provides(Declaration): # Really named ProvidesClass
- """Implement ``__provides__``, the instance-specific specification
- When an object is pickled, we pickle the interfaces that it implements.
- """
- def __init__(self, cls, *interfaces):
- self.__args = (cls, ) + interfaces
- self._cls = cls
- Declaration.__init__(
- self, *self._add_interfaces_to_cls(interfaces, cls)
- )
- # Added to by ``moduleProvides``, et al
- _v_module_names = ()
- def __repr__(self):
- # The typical way to create instances of this object is via calling
- # ``directlyProvides(...)`` or ``alsoProvides()``, but that's not the
- # only way. Proxies, for example, directly use the ``Provides(...)``
- # function (which is the more generic method, and what we pickle as).
- # We're after the most readable, useful repr in the common case, so we
- # use the most common name.
- #
- # We also cooperate with ``moduleProvides`` to attempt to do the right
- # thing for that API. See it for details.
- function_name = 'directlyProvides'
- if self._cls is ModuleType and self._v_module_names:
- # See notes in ``moduleProvides``/``directlyProvides``
- providing_on_module = True
- interfaces = self.__args[1:]
- else:
- providing_on_module = False
- interfaces = (self._cls,) + self.__bases__
- ordered_names = self._argument_names_for_repr(interfaces)
- if providing_on_module:
- mod_names = self._v_module_names
- if len(mod_names) == 1:
- mod_names = "sys.modules[%r]" % mod_names[0]
- ordered_names = (
- f'{mod_names}, '
- ) + ordered_names
- return "{}({})".format(
- function_name,
- ordered_names,
- )
- def __reduce__(self):
- # This reduces to the Provides *function*, not
- # this class.
- return Provides, self.__args
- __module__ = 'zope.interface'
- def __get__(self, inst, cls):
- """Make sure that a class __provides__ doesn't leak to an instance
- """
- if inst is None and cls is self._cls:
- # We were accessed through a class, so we are the class'
- # provides spec. Just return this object, but only if we are
- # being called on the same class that we were defined for:
- return self
- raise AttributeError('__provides__')
- ProvidesClass = Provides
- # Registry of instance declarations
- # This is a memory optimization to allow objects to share specifications.
- InstanceDeclarations = weakref.WeakValueDictionary()
- def Provides(*interfaces): # pylint:disable=function-redefined
- """Declaration for an instance of *cls*.
- The correct signature is ``cls, *interfaces``.
- The *cls* is necessary to avoid the
- construction of inconsistent resolution orders.
- Instance declarations are shared among instances that have the same
- declaration. The declarations are cached in a weak value dictionary.
- """
- spec = InstanceDeclarations.get(interfaces)
- if spec is None:
- spec = ProvidesClass(*interfaces)
- InstanceDeclarations[interfaces] = spec
- return spec
- Provides.__safe_for_unpickling__ = True
- def directlyProvides(object, *interfaces): # pylint:disable=redefined-builtin
- """Declare interfaces declared directly for an object
- The arguments after the object are one or more interfaces or interface
- specifications (`~zope.interface.interfaces.IDeclaration` objects).
- The interfaces given (including the interfaces in the specifications)
- replace interfaces previously declared for the object.
- """
- cls = getattr(object, '__class__', None)
- if cls is not None and getattr(cls, '__class__', None) is cls:
- # It's a meta class (well, at least it it could be an extension class)
- # Note that we can't get here from the tests: there is no normal
- # class which isn't descriptor aware.
- if not isinstance(object, type):
- raise TypeError("Attempt to make an interface declaration on a "
- "non-descriptor-aware class")
- interfaces = _normalizeargs(interfaces)
- if cls is None:
- cls = type(object)
- if issubclass(cls, type):
- # we have a class or type. We'll use a special descriptor
- # that provides some extra caching
- object.__provides__ = ClassProvides(object, cls, *interfaces)
- else:
- provides = object.__provides__ = Provides(cls, *interfaces)
- # See notes in ``moduleProvides``.
- if issubclass(cls, ModuleType) and hasattr(object, '__name__'):
- provides._v_module_names += (object.__name__,)
- def alsoProvides(object, *interfaces): # pylint:disable=redefined-builtin
- """Declare interfaces declared directly for an object
- The arguments after the object are one or more interfaces or interface
- specifications (`~zope.interface.interfaces.IDeclaration` objects).
- The interfaces given (including the interfaces in the specifications) are
- added to the interfaces previously declared for the object.
- """
- directlyProvides(object, directlyProvidedBy(object), *interfaces)
- def noLongerProvides(object, interface): # pylint:disable=redefined-builtin
- """ Removes a directly provided interface from an object.
- """
- directlyProvides(object, directlyProvidedBy(object) - interface)
- if interface.providedBy(object):
- raise ValueError("Can only remove directly provided interfaces.")
- @_use_c_impl
- class ClassProvidesBase(SpecificationBase):
- __slots__ = (
- '_cls',
- '_implements',
- )
- def __get__(self, inst, cls):
- # member slots are set by subclass
- # pylint:disable=no-member
- if cls is self._cls:
- # We only work if called on the class we were defined for
- if inst is None:
- # We were accessed through a class, so we are the class'
- # provides spec. Just return this object as is:
- return self
- return self._implements
- raise AttributeError('__provides__')
- class ClassProvides(Declaration, ClassProvidesBase):
- """Special descriptor for class ``__provides__``
- The descriptor caches the implementedBy info, so that
- we can get declarations for objects without instance-specific
- interfaces a bit quicker.
- """
- __slots__ = (
- '__args',
- )
- def __init__(self, cls, metacls, *interfaces):
- self._cls = cls
- self._implements = implementedBy(cls)
- self.__args = (cls, metacls, ) + interfaces
- Declaration.__init__(
- self, *self._add_interfaces_to_cls(interfaces, metacls)
- )
- def __repr__(self):
- # There are two common ways to get instances of this object: The most
- # interesting way is calling ``@provider(..)`` as a decorator of a
- # class; this is the same as calling ``directlyProvides(cls, ...)``.
- #
- # The other way is by default: anything that invokes
- # ``implementedBy(x)`` will wind up putting an instance in
- # ``type(x).__provides__``; this includes the ``@implementer(...)``
- # decorator. Those instances won't have any interfaces.
- #
- # Thus, as our repr, we go with the ``directlyProvides()`` syntax.
- interfaces = (self._cls, ) + self.__args[2:]
- ordered_names = self._argument_names_for_repr(interfaces)
- return f"directlyProvides({ordered_names})"
- def __reduce__(self):
- return self.__class__, self.__args
- # Copy base-class method for speed
- __get__ = ClassProvidesBase.__get__
- # autopep8: off (it breaks the statements in the "if")
- def directlyProvidedBy(object): # pylint:disable=redefined-builtin
- """Return the interfaces directly provided by the given object
- The value returned is an `~zope.interface.interfaces.IDeclaration`.
- """
- provides = getattr(object, "__provides__", None)
- if (
- provides is None # no spec
- # We might have gotten the implements spec, as an
- # optimization. If so, it's like having only one base, that we
- # lop off to exclude class-supplied declarations:
- or isinstance(provides, Implements) # noqa W503
- ):
- return _empty
- # Strip off the class part of the spec:
- return Declaration(provides.__bases__[:-1])
- # autopep8: on
- class provider:
- """Declare interfaces provided directly by a class
- This function is called in a class definition.
- The arguments are one or more interfaces or interface specifications
- (`~zope.interface.interfaces.IDeclaration` objects).
- The given interfaces (including the interfaces in the specifications)
- are used to create the class's direct-object interface specification.
- An error will be raised if the module class has an direct interface
- specification. In other words, it is an error to call this function more
- than once in a class definition.
- Note that the given interfaces have nothing to do with the interfaces
- implemented by instances of the class.
- This function is provided for convenience. It provides a more convenient
- way to call `directlyProvides` for a class. For example::
- @provider(I1)
- class C:
- pass
- is equivalent to calling::
- directlyProvides(C, I1)
- after the class has been created.
- """
- def __init__(self, *interfaces):
- self.interfaces = interfaces
- def __call__(self, ob):
- directlyProvides(ob, *self.interfaces)
- return ob
- def moduleProvides(*interfaces):
- """Declare interfaces provided by a module
- This function is used in a module definition.
- The arguments are one or more interfaces or interface specifications
- (`~zope.interface.interfaces.IDeclaration` objects).
- The given interfaces (including the interfaces in the specifications) are
- used to create the module's direct-object interface specification. An
- error will be raised if the module already has an interface specification.
- In other words, it is an error to call this function more than once in a
- module definition.
- This function is provided for convenience. It provides a more convenient
- way to call directlyProvides. For example::
- moduleProvides(I1)
- is equivalent to::
- directlyProvides(sys.modules[__name__], I1)
- """
- frame = sys._getframe(1) # pylint:disable=protected-access
- locals = frame.f_locals # pylint:disable=redefined-builtin
- # Try to make sure we were called from a module body
- if (locals is not frame.f_globals) or ('__name__' not in locals):
- raise TypeError(
- "moduleProvides can only be used from a module definition.")
- if '__provides__' in locals:
- raise TypeError(
- "moduleProvides can only be used once in a module definition.")
- # Note: This is cached based on the key ``(ModuleType, *interfaces)``; One
- # consequence is that any module that provides the same interfaces gets
- # the same ``__repr__``, meaning that you can't tell what module such a
- # declaration came from. Adding the module name to ``_v_module_names``
- # attempts to correct for this; it works in some common situations, but
- # fails (1) after pickling (the data is lost) and (2) if declarations are
- # actually shared and (3) if the alternate spelling of
- # ``directlyProvides()`` is used. Problem (3) is fixed by cooperating
- # with ``directlyProvides`` to maintain this information, and problem (2)
- # is worked around by printing all the names, but (1) is unsolvable
- # without introducing new classes or changing the stored data...but it
- # doesn't actually matter, because ``ModuleType`` can't be pickled!
- p = locals["__provides__"] = Provides(ModuleType,
- *_normalizeargs(interfaces))
- p._v_module_names += (locals['__name__'],)
- ##############################################################################
- #
- # Declaration querying support
- # XXX: is this a fossil? Nobody calls it, no unit tests exercise it, no
- # doctests import it, and the package __init__ doesn't import it.
- # (Answer: Versions of zope.container prior to 4.4.0 called this,
- # and zope.proxy.decorator up through at least 4.3.5 called this.)
- def ObjectSpecification(direct, cls):
- """Provide object specifications
- These combine information for the object and for it's classes.
- """
- return Provides(cls, direct) # pragma: no cover fossil
- @_use_c_impl
- def getObjectSpecification(ob):
- try:
- provides = ob.__provides__
- except AttributeError:
- provides = None
- if provides is not None:
- if isinstance(provides, SpecificationBase):
- return provides
- try:
- cls = ob.__class__
- except AttributeError:
- # We can't get the class, so just consider provides
- return _empty
- return implementedBy(cls)
- @_use_c_impl
- def providedBy(ob):
- """
- Return the interfaces provided by *ob*.
- If *ob* is a :class:`super` object, then only interfaces implemented
- by the remainder of the classes in the method resolution order are
- considered. Interfaces directly provided by the object underlying *ob*
- are not.
- """
- # Here we have either a special object, an old-style declaration
- # or a descriptor
- # Try to get __providedBy__
- try:
- if isinstance(ob, super): # Some objects raise errors on isinstance()
- return implementedBy(ob)
- r = ob.__providedBy__
- except AttributeError:
- # Not set yet. Fall back to lower-level thing that computes it
- return getObjectSpecification(ob)
- try:
- # We might have gotten a descriptor from an instance of a
- # class (like an ExtensionClass) that doesn't support
- # descriptors. We'll make sure we got one by trying to get
- # the only attribute, which all specs have.
- r.extends
- except AttributeError:
- # The object's class doesn't understand descriptors.
- # Sigh. We need to get an object descriptor, but we have to be
- # careful. We want to use the instance's __provides__, if
- # there is one, but only if it didn't come from the class.
- try:
- r = ob.__provides__
- except AttributeError:
- # No __provides__, so just fall back to implementedBy
- return implementedBy(ob.__class__)
- # We need to make sure we got the __provides__ from the
- # instance. We'll do this by making sure we don't get the same
- # thing from the class:
- try:
- cp = ob.__class__.__provides__
- except AttributeError:
- # The ob doesn't have a class or the class has no
- # provides, assume we're done:
- return r
- if r is cp:
- # Oops, we got the provides from the class. This means
- # the object doesn't have it's own. We should use implementedBy
- return implementedBy(ob.__class__)
- return r
- @_use_c_impl
- class ObjectSpecificationDescriptor:
- """Implement the ``__providedBy__`` attribute
- The ``__providedBy__`` attribute computes the interfaces provided by an
- object. If an object has an ``__provides__`` attribute, that is returned.
- Otherwise, `implementedBy` the *cls* is returned.
- .. versionchanged:: 5.4.0
- Both the default (C) implementation and the Python implementation
- now let exceptions raised by accessing ``__provides__`` propagate.
- Previously, the C version ignored all exceptions.
- .. versionchanged:: 5.4.0
- The Python implementation now matches the C implementation and lets
- a ``__provides__`` of ``None`` override what the class is declared to
- implement.
- """
- def __get__(self, inst, cls):
- """Get an object specification for an object
- """
- if inst is None:
- return getObjectSpecification(cls)
- try:
- return inst.__provides__
- except AttributeError:
- return implementedBy(cls)
- ##############################################################################
- def _normalizeargs(sequence, output=None):
- """Normalize declaration arguments
- Normalization arguments might contain Declarions, tuples, or single
- interfaces.
- Anything but individual interfaces or implements specs will be expanded.
- """
- if output is None:
- output = []
- cls = sequence.__class__
- if InterfaceClass in cls.__mro__ or Implements in cls.__mro__:
- output.append(sequence)
- else:
- for v in sequence:
- _normalizeargs(v, output)
- return output
- _empty = _ImmutableDeclaration()
- objectSpecificationDescriptor = ObjectSpecificationDescriptor()
|