|
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184 |
- ##############################################################################
- # 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
- from types import FunctionType
- from types import MethodType
- from types import ModuleType
- import weakref
-
- from zope.interface.interface import Interface
- from zope.interface.interface import InterfaceClass
- from zope.interface.interface import SpecificationBase
- from zope.interface.interface import Specification
- from zope.interface.interface import NameAndModuleComparisonMixin
- from zope.interface._compat import _use_c_impl
-
- __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 'classImplements({}{})'.format(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.
- 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 l in before, spec.declared, after:
- for b in l:
- 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 = (
- '{}, '.format(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
- """Cache instance declarations
-
- 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 "directlyProvides({})".format(ordered_names)
-
- def __reduce__(self):
- return self.__class__, self.__args
-
- # Copy base-class method for speed
- __get__ = ClassProvidesBase.__get__
-
-
- 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)
- ):
- return _empty
-
- # Strip off the class part of the spec:
- return Declaration(provides.__bases__[:-1])
-
-
- 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()
|