1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153 |
- ##############################################################################
- #
- # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
- # All Rights Reserved.
- #
- # This software is subject to the provisions of the Zope Public License,
- # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
- # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
- # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
- # FOR A PARTICULAR PURPOSE.
- #
- ##############################################################################
- """Interface object implementation
- """
- # pylint:disable=protected-access
- import sys
- from types import MethodType
- from types import FunctionType
- import weakref
- from zope.interface._compat import _use_c_impl
- from zope.interface._compat import PYTHON2 as PY2
- from zope.interface.exceptions import Invalid
- from zope.interface.ro import ro as calculate_ro
- from zope.interface import ro
- __all__ = [
- # Most of the public API from this module is directly exported
- # from zope.interface. The only remaining public API intended to
- # be imported from here should be those few things documented as
- # such.
- 'InterfaceClass',
- 'Specification',
- 'adapter_hooks',
- ]
- CO_VARARGS = 4
- CO_VARKEYWORDS = 8
- # Put in the attrs dict of an interface by ``taggedValue`` and ``invariants``
- TAGGED_DATA = '__interface_tagged_values__'
- # Put in the attrs dict of an interface by ``interfacemethod``
- INTERFACE_METHODS = '__interface_methods__'
- _decorator_non_return = object()
- _marker = object()
- def invariant(call):
- f_locals = sys._getframe(1).f_locals
- tags = f_locals.setdefault(TAGGED_DATA, {})
- invariants = tags.setdefault('invariants', [])
- invariants.append(call)
- return _decorator_non_return
- def taggedValue(key, value):
- """Attaches a tagged value to an interface at definition time."""
- f_locals = sys._getframe(1).f_locals
- tagged_values = f_locals.setdefault(TAGGED_DATA, {})
- tagged_values[key] = value
- return _decorator_non_return
- class Element(object):
- """
- Default implementation of `zope.interface.interfaces.IElement`.
- """
- # We can't say this yet because we don't have enough
- # infrastructure in place.
- #
- #implements(IElement)
- def __init__(self, __name__, __doc__=''): # pylint:disable=redefined-builtin
- if not __doc__ and __name__.find(' ') >= 0:
- __doc__ = __name__
- __name__ = None
- self.__name__ = __name__
- self.__doc__ = __doc__
- # Tagged values are rare, especially on methods or attributes.
- # Deferring the allocation can save substantial memory.
- self.__tagged_values = None
- def getName(self):
- """ Returns the name of the object. """
- return self.__name__
- def getDoc(self):
- """ Returns the documentation for the object. """
- return self.__doc__
- ###
- # Tagged values.
- #
- # Direct tagged values are set only in this instance. Others
- # may be inherited (for those subclasses that have that concept).
- ###
- def getTaggedValue(self, tag):
- """ Returns the value associated with 'tag'. """
- if not self.__tagged_values:
- raise KeyError(tag)
- return self.__tagged_values[tag]
- def queryTaggedValue(self, tag, default=None):
- """ Returns the value associated with 'tag'. """
- return self.__tagged_values.get(tag, default) if self.__tagged_values else default
- def getTaggedValueTags(self):
- """ Returns a collection of all tags. """
- return self.__tagged_values.keys() if self.__tagged_values else ()
- def setTaggedValue(self, tag, value):
- """ Associates 'value' with 'key'. """
- if self.__tagged_values is None:
- self.__tagged_values = {}
- self.__tagged_values[tag] = value
- queryDirectTaggedValue = queryTaggedValue
- getDirectTaggedValue = getTaggedValue
- getDirectTaggedValueTags = getTaggedValueTags
- SpecificationBasePy = object # filled by _use_c_impl.
- @_use_c_impl
- class SpecificationBase(object):
- # This object is the base of the inheritance hierarchy for ClassProvides:
- #
- # ClassProvides < ClassProvidesBase, Declaration
- # Declaration < Specification < SpecificationBase
- # ClassProvidesBase < SpecificationBase
- #
- # In order to have compatible instance layouts, we need to declare
- # the storage used by Specification and Declaration here (and
- # those classes must have ``__slots__ = ()``); fortunately this is
- # not a waste of space because those are the only two inheritance
- # trees. These all translate into tp_members in C.
- __slots__ = (
- # Things used here.
- '_implied',
- # Things used in Specification.
- '_dependents',
- '_bases',
- '_v_attrs',
- '__iro__',
- '__sro__',
- '__weakref__',
- )
- def providedBy(self, ob):
- """Is the interface implemented by an object
- """
- spec = providedBy(ob)
- return self in spec._implied
- def implementedBy(self, cls):
- """Test whether the specification is implemented by a class or factory.
- Raise TypeError if argument is neither a class nor a callable.
- """
- spec = implementedBy(cls)
- return self in spec._implied
- def isOrExtends(self, interface):
- """Is the interface the same as or extend the given interface
- """
- return interface in self._implied # pylint:disable=no-member
- __call__ = isOrExtends
- class NameAndModuleComparisonMixin(object):
- # Internal use. Implement the basic sorting operators (but not (in)equality
- # or hashing). Subclasses must provide ``__name__`` and ``__module__``
- # attributes. Subclasses will be mutually comparable; but because equality
- # and hashing semantics are missing from this class, take care in how
- # you define those two attributes: If you stick with the default equality
- # and hashing (identity based) you should make sure that all possible ``__name__``
- # and ``__module__`` pairs are unique ACROSS ALL SUBCLASSES. (Actually, pretty
- # much the same thing goes if you define equality and hashing to be based on
- # those two attributes: they must still be consistent ACROSS ALL SUBCLASSES.)
- # pylint:disable=assigning-non-slot
- __slots__ = ()
- def _compare(self, other):
- """
- Compare *self* to *other* based on ``__name__`` and ``__module__``.
- Return 0 if they are equal, return 1 if *self* is
- greater than *other*, and return -1 if *self* is less than
- *other*.
- If *other* does not have ``__name__`` or ``__module__``, then
- return ``NotImplemented``.
- .. caution::
- This allows comparison to things well outside the type hierarchy,
- perhaps not symmetrically.
- For example, ``class Foo(object)`` and ``class Foo(Interface)``
- in the same file would compare equal, depending on the order of
- operands. Writing code like this by hand would be unusual, but it could
- happen with dynamic creation of types and interfaces.
- None is treated as a pseudo interface that implies the loosest
- contact possible, no contract. For that reason, all interfaces
- sort before None.
- """
- if other is self:
- return 0
- if other is None:
- return -1
- n1 = (self.__name__, self.__module__)
- try:
- n2 = (other.__name__, other.__module__)
- except AttributeError:
- return NotImplemented
- # This spelling works under Python3, which doesn't have cmp().
- return (n1 > n2) - (n1 < n2)
- def __lt__(self, other):
- c = self._compare(other)
- if c is NotImplemented:
- return c
- return c < 0
- def __le__(self, other):
- c = self._compare(other)
- if c is NotImplemented:
- return c
- return c <= 0
- def __gt__(self, other):
- c = self._compare(other)
- if c is NotImplemented:
- return c
- return c > 0
- def __ge__(self, other):
- c = self._compare(other)
- if c is NotImplemented:
- return c
- return c >= 0
- @_use_c_impl
- class InterfaceBase(NameAndModuleComparisonMixin, SpecificationBasePy):
- """Base class that wants to be replaced with a C base :)
- """
- __slots__ = (
- '__name__',
- '__ibmodule__',
- '_v_cached_hash',
- )
- def __init__(self, name=None, module=None):
- self.__name__ = name
- self.__ibmodule__ = module
- def _call_conform(self, conform):
- raise NotImplementedError
- @property
- def __module_property__(self):
- # This is for _InterfaceMetaClass
- return self.__ibmodule__
- def __call__(self, obj, alternate=_marker):
- """Adapt an object to the interface
- """
- try:
- conform = obj.__conform__
- except AttributeError:
- conform = None
- if conform is not None:
- adapter = self._call_conform(conform)
- if adapter is not None:
- return adapter
- adapter = self.__adapt__(obj)
- if adapter is not None:
- return adapter
- if alternate is not _marker:
- return alternate
- raise TypeError("Could not adapt", obj, self)
- def __adapt__(self, obj):
- """Adapt an object to the receiver
- """
- if self.providedBy(obj):
- return obj
- for hook in adapter_hooks:
- adapter = hook(self, obj)
- if adapter is not None:
- return adapter
- return None
- def __hash__(self):
- # pylint:disable=assigning-non-slot,attribute-defined-outside-init
- try:
- return self._v_cached_hash
- except AttributeError:
- self._v_cached_hash = hash((self.__name__, self.__module__))
- return self._v_cached_hash
- def __eq__(self, other):
- c = self._compare(other)
- if c is NotImplemented:
- return c
- return c == 0
- def __ne__(self, other):
- if other is self:
- return False
- c = self._compare(other)
- if c is NotImplemented:
- return c
- return c != 0
- adapter_hooks = _use_c_impl([], 'adapter_hooks')
- class Specification(SpecificationBase):
- """Specifications
- An interface specification is used to track interface declarations
- and component registrations.
- This class is a base class for both interfaces themselves and for
- interface specifications (declarations).
- Specifications are mutable. If you reassign their bases, their
- relations with other specifications are adjusted accordingly.
- """
- __slots__ = ()
- # The root of all Specifications. This will be assigned `Interface`,
- # once it is defined.
- _ROOT = None
- # Copy some base class methods for speed
- isOrExtends = SpecificationBase.isOrExtends
- providedBy = SpecificationBase.providedBy
- def __init__(self, bases=()):
- # There are many leaf interfaces with no dependents,
- # and a few with very many. It's a heavily left-skewed
- # distribution. In a survey of Plone and Zope related packages
- # that loaded 2245 InterfaceClass objects and 2235 ClassProvides
- # instances, there were a total of 7000 Specification objects created.
- # 4700 had 0 dependents, 1400 had 1, 382 had 2 and so on. Only one
- # for <type> had 1664. So there's savings to be had deferring
- # the creation of dependents.
- self._dependents = None # type: weakref.WeakKeyDictionary
- self._bases = ()
- self._implied = {}
- self._v_attrs = None
- self.__iro__ = ()
- self.__sro__ = ()
- self.__bases__ = tuple(bases)
- @property
- def dependents(self):
- if self._dependents is None:
- self._dependents = weakref.WeakKeyDictionary()
- return self._dependents
- def subscribe(self, dependent):
- self._dependents[dependent] = self.dependents.get(dependent, 0) + 1
- def unsubscribe(self, dependent):
- try:
- n = self._dependents[dependent]
- except TypeError:
- raise KeyError(dependent)
- n -= 1
- if not n:
- del self.dependents[dependent]
- else:
- assert n > 0
- self.dependents[dependent] = n
- def __setBases(self, bases):
- # Remove ourselves as a dependent of our old bases
- for b in self.__bases__:
- b.unsubscribe(self)
- # Register ourselves as a dependent of our new bases
- self._bases = bases
- for b in bases:
- b.subscribe(self)
- self.changed(self)
- __bases__ = property(
- lambda self: self._bases,
- __setBases,
- )
- # This method exists for tests to override the way we call
- # ro.calculate_ro(), usually by adding extra kwargs. We don't
- # want to have a mutable dictionary as a class member that we pass
- # ourself because mutability is bad, and passing **kw is slower than
- # calling the bound function.
- _do_calculate_ro = calculate_ro
- def _calculate_sro(self):
- """
- Calculate and return the resolution order for this object, using its ``__bases__``.
- Ensures that ``Interface`` is always the last (lowest priority) element.
- """
- # We'd like to make Interface the lowest priority as a
- # property of the resolution order algorithm. That almost
- # works out naturally, but it fails when class inheritance has
- # some bases that DO implement an interface, and some that DO
- # NOT. In such a mixed scenario, you wind up with a set of
- # bases to consider that look like this: [[..., Interface],
- # [..., object], ...]. Depending on the order of inheritance,
- # Interface can wind up before or after object, and that can
- # happen at any point in the tree, meaning Interface can wind
- # up somewhere in the middle of the order. Since Interface is
- # treated as something that everything winds up implementing
- # anyway (a catch-all for things like adapters), having it high up
- # the order is bad. It's also bad to have it at the end, just before
- # some concrete class: concrete classes should be HIGHER priority than
- # interfaces (because there's only one class, but many implementations).
- #
- # One technically nice way to fix this would be to have
- # ``implementedBy(object).__bases__ = (Interface,)``
- #
- # But: (1) That fails for old-style classes and (2) that causes
- # everything to appear to *explicitly* implement Interface, when up
- # to this point it's been an implicit virtual sort of relationship.
- #
- # So we force the issue by mutating the resolution order.
- # Note that we let C3 use pre-computed __sro__ for our bases.
- # This requires that by the time this method is invoked, our bases
- # have settled their SROs. Thus, ``changed()`` must first
- # update itself before telling its descendents of changes.
- sro = self._do_calculate_ro(base_mros={
- b: b.__sro__
- for b in self.__bases__
- })
- root = self._ROOT
- if root is not None and sro and sro[-1] is not root:
- # In one dataset of 1823 Interface objects, 1117 ClassProvides objects,
- # sro[-1] was root 4496 times, and only not root 118 times. So it's
- # probably worth checking.
- # Once we don't have to deal with old-style classes,
- # we can add a check and only do this if base_count > 1,
- # if we tweak the bootstrapping for ``<implementedBy object>``
- sro = [
- x
- for x in sro
- if x is not root
- ]
- sro.append(root)
- return sro
- def changed(self, originally_changed):
- """
- We, or something we depend on, have changed.
- By the time this is called, the things we depend on,
- such as our bases, should themselves be stable.
- """
- self._v_attrs = None
- implied = self._implied
- implied.clear()
- ancestors = self._calculate_sro()
- self.__sro__ = tuple(ancestors)
- self.__iro__ = tuple([ancestor for ancestor in ancestors
- if isinstance(ancestor, InterfaceClass)
- ])
- for ancestor in ancestors:
- # We directly imply our ancestors:
- implied[ancestor] = ()
- # Now, advise our dependents of change
- # (being careful not to create the WeakKeyDictionary if not needed):
- for dependent in tuple(self._dependents.keys() if self._dependents else ()):
- dependent.changed(originally_changed)
- # Just in case something called get() at some point
- # during that process and we have a cycle of some sort
- # make sure we didn't cache incomplete results.
- self._v_attrs = None
- def interfaces(self):
- """Return an iterator for the interfaces in the specification.
- """
- seen = {}
- for base in self.__bases__:
- for interface in base.interfaces():
- if interface not in seen:
- seen[interface] = 1
- yield interface
- def extends(self, interface, strict=True):
- """Does the specification extend the given interface?
- Test whether an interface in the specification extends the
- given interface
- """
- return ((interface in self._implied)
- and
- ((not strict) or (self != interface))
- )
- def weakref(self, callback=None):
- return weakref.ref(self, callback)
- def get(self, name, default=None):
- """Query for an attribute description
- """
- attrs = self._v_attrs
- if attrs is None:
- attrs = self._v_attrs = {}
- attr = attrs.get(name)
- if attr is None:
- for iface in self.__iro__:
- attr = iface.direct(name)
- if attr is not None:
- attrs[name] = attr
- break
- return default if attr is None else attr
- class _InterfaceMetaClass(type):
- # Handling ``__module__`` on ``InterfaceClass`` is tricky. We need
- # to be able to read it on a type and get the expected string. We
- # also need to be able to set it on an instance and get the value
- # we set. So far so good. But what gets tricky is that we'd like
- # to store the value in the C structure (``InterfaceBase.__ibmodule__``) for
- # direct access during equality, sorting, and hashing. "No
- # problem, you think, I'll just use a property" (well, the C
- # equivalents, ``PyMemberDef`` or ``PyGetSetDef``).
- #
- # Except there is a problem. When a subclass is created, the
- # metaclass (``type``) always automatically puts the expected
- # string in the class's dictionary under ``__module__``, thus
- # overriding the property inherited from the superclass. Writing
- # ``Subclass.__module__`` still works, but
- # ``Subclass().__module__`` fails.
- #
- # There are multiple ways to work around this:
- #
- # (1) Define ``InterfaceBase.__getattribute__`` to watch for
- # ``__module__`` and return the C storage.
- #
- # This works, but slows down *all* attribute access (except,
- # ironically, to ``__module__``) by about 25% (40ns becomes 50ns)
- # (when implemented in C). Since that includes methods like
- # ``providedBy``, that's probably not acceptable.
- #
- # All the other methods involve modifying subclasses. This can be
- # done either on the fly in some cases, as instances are
- # constructed, or by using a metaclass. These next few can be done on the fly.
- #
- # (2) Make ``__module__`` a descriptor in each subclass dictionary.
- # It can't be a straight up ``@property`` descriptor, though, because accessing
- # it on the class returns a ``property`` object, not the desired string.
- #
- # (3) Implement a data descriptor (``__get__`` and ``__set__``)
- # that is both a subclass of string, and also does the redirect of
- # ``__module__`` to ``__ibmodule__`` and does the correct thing
- # with the ``instance`` argument to ``__get__`` is None (returns
- # the class's value.) (Why must it be a subclass of string? Because
- # when it' s in the class's dict, it's defined on an *instance* of the
- # metaclass; descriptors in an instance's dict aren't honored --- their
- # ``__get__`` is never invoked --- so it must also *be* the value we want
- # returned.)
- #
- # This works, preserves the ability to read and write
- # ``__module__``, and eliminates any penalty accessing other
- # attributes. But it slows down accessing ``__module__`` of
- # instances by 200% (40ns to 124ns), requires editing class dicts on the fly
- # (in InterfaceClass.__init__), thus slightly slowing down all interface creation,
- # and is ugly.
- #
- # (4) As in the last step, but make it a non-data descriptor (no ``__set__``).
- #
- # If you then *also* store a copy of ``__ibmodule__`` in
- # ``__module__`` in the instance's dict, reading works for both
- # class and instance and is full speed for instances. But the cost
- # is storage space, and you can't write to it anymore, not without
- # things getting out of sync.
- #
- # (Actually, ``__module__`` was never meant to be writable. Doing
- # so would break BTrees and normal dictionaries, as well as the
- # repr, maybe more.)
- #
- # That leaves us with a metaclass. (Recall that a class is an
- # instance of its metaclass, so properties/descriptors defined in
- # the metaclass are used when accessing attributes on the
- # instance/class. We'll use that to define ``__module__``.) Here
- # we can have our cake and eat it too: no extra storage, and
- # C-speed access to the underlying storage. The only substantial
- # cost is that metaclasses tend to make people's heads hurt. (But
- # still less than the descriptor-is-string, hopefully.)
- __slots__ = ()
- def __new__(cls, name, bases, attrs):
- # Figure out what module defined the interface.
- # This is copied from ``InterfaceClass.__init__``;
- # reviewers aren't sure how AttributeError or KeyError
- # could be raised.
- __module__ = sys._getframe(1).f_globals['__name__']
- # Get the C optimized __module__ accessor and give it
- # to the new class.
- moduledescr = InterfaceBase.__dict__['__module__']
- if isinstance(moduledescr, str):
- # We're working with the Python implementation,
- # not the C version
- moduledescr = InterfaceBase.__dict__['__module_property__']
- attrs['__module__'] = moduledescr
- kind = type.__new__(cls, name, bases, attrs)
- kind.__module = __module__
- return kind
- @property
- def __module__(cls):
- return cls.__module
- def __repr__(cls):
- return "<class '%s.%s'>" % (
- cls.__module,
- cls.__name__,
- )
- _InterfaceClassBase = _InterfaceMetaClass(
- 'InterfaceClass',
- # From least specific to most specific.
- (InterfaceBase, Specification, Element),
- {'__slots__': ()}
- )
- def interfacemethod(func):
- """
- Convert a method specification to an actual method of the interface.
- This is a decorator that functions like `staticmethod` et al.
- The primary use of this decorator is to allow interface definitions to
- define the ``__adapt__`` method, but other interface methods can be
- overridden this way too.
- .. seealso:: `zope.interface.interfaces.IInterfaceDeclaration.interfacemethod`
- """
- f_locals = sys._getframe(1).f_locals
- methods = f_locals.setdefault(INTERFACE_METHODS, {})
- methods[func.__name__] = func
- return _decorator_non_return
- class InterfaceClass(_InterfaceClassBase):
- """
- Prototype (scarecrow) Interfaces Implementation.
- Note that it is not possible to change the ``__name__`` or ``__module__``
- after an instance of this object has been constructed.
- """
- # We can't say this yet because we don't have enough
- # infrastructure in place.
- #
- #implements(IInterface)
- def __new__(cls, name=None, bases=(), attrs=None, __doc__=None, # pylint:disable=redefined-builtin
- __module__=None):
- assert isinstance(bases, tuple)
- attrs = attrs or {}
- needs_custom_class = attrs.pop(INTERFACE_METHODS, None)
- if needs_custom_class:
- needs_custom_class.update(
- {'__classcell__': attrs.pop('__classcell__')}
- if '__classcell__' in attrs
- else {}
- )
- if '__adapt__' in needs_custom_class:
- # We need to tell the C code to call this.
- needs_custom_class['_CALL_CUSTOM_ADAPT'] = 1
- if issubclass(cls, _InterfaceClassWithCustomMethods):
- cls_bases = (cls,)
- elif cls is InterfaceClass:
- cls_bases = (_InterfaceClassWithCustomMethods,)
- else:
- cls_bases = (cls, _InterfaceClassWithCustomMethods)
- cls = type(cls)( # pylint:disable=self-cls-assignment
- name + "<WithCustomMethods>",
- cls_bases,
- needs_custom_class
- )
- elif PY2 and bases and len(bases) > 1:
- bases_with_custom_methods = tuple(
- type(b)
- for b in bases
- if issubclass(type(b), _InterfaceClassWithCustomMethods)
- )
- # If we have a subclass of InterfaceClass in *bases*,
- # Python 3 is smart enough to pass that as *cls*, but Python
- # 2 just passes whatever the first base in *bases* is. This means that if
- # we have multiple inheritance, and one of our bases has already defined
- # a custom method like ``__adapt__``, we do the right thing automatically
- # and extend it on Python 3, but not necessarily on Python 2. To fix this, we need
- # to run the MRO algorithm and get the most derived base manually.
- # Note that this only works for consistent resolution orders
- if bases_with_custom_methods:
- cls = type( # pylint:disable=self-cls-assignment
- name + "<WithCustomMethods>",
- bases_with_custom_methods,
- {}
- ).__mro__[1] # Not the class we created, the most derived.
- return _InterfaceClassBase.__new__(cls)
- def __init__(self, name, bases=(), attrs=None, __doc__=None, # pylint:disable=redefined-builtin
- __module__=None):
- # We don't call our metaclass parent directly
- # pylint:disable=non-parent-init-called
- # pylint:disable=super-init-not-called
- if not all(isinstance(base, InterfaceClass) for base in bases):
- raise TypeError('Expected base interfaces')
- if attrs is None:
- attrs = {}
- if __module__ is None:
- __module__ = attrs.get('__module__')
- if isinstance(__module__, str):
- del attrs['__module__']
- else:
- try:
- # Figure out what module defined the interface.
- # This is how cPython figures out the module of
- # a class, but of course it does it in C. :-/
- __module__ = sys._getframe(1).f_globals['__name__']
- except (AttributeError, KeyError): # pragma: no cover
- pass
- InterfaceBase.__init__(self, name, __module__)
- # These asserts assisted debugging the metaclass
- # assert '__module__' not in self.__dict__
- # assert self.__ibmodule__ is self.__module__ is __module__
- d = attrs.get('__doc__')
- if d is not None:
- if not isinstance(d, Attribute):
- if __doc__ is None:
- __doc__ = d
- del attrs['__doc__']
- if __doc__ is None:
- __doc__ = ''
- Element.__init__(self, name, __doc__)
- tagged_data = attrs.pop(TAGGED_DATA, None)
- if tagged_data is not None:
- for key, val in tagged_data.items():
- self.setTaggedValue(key, val)
- Specification.__init__(self, bases)
- self.__attrs = self.__compute_attrs(attrs)
- self.__identifier__ = "%s.%s" % (__module__, name)
- def __compute_attrs(self, attrs):
- # Make sure that all recorded attributes (and methods) are of type
- # `Attribute` and `Method`
- def update_value(aname, aval):
- if isinstance(aval, Attribute):
- aval.interface = self
- if not aval.__name__:
- aval.__name__ = aname
- elif isinstance(aval, FunctionType):
- aval = fromFunction(aval, self, name=aname)
- else:
- raise InvalidInterface("Concrete attribute, " + aname)
- return aval
- return {
- aname: update_value(aname, aval)
- for aname, aval in attrs.items()
- if aname not in (
- # __locals__: Python 3 sometimes adds this.
- '__locals__',
- # __qualname__: PEP 3155 (Python 3.3+)
- '__qualname__',
- # __annotations__: PEP 3107 (Python 3.0+)
- '__annotations__',
- )
- and aval is not _decorator_non_return
- }
- def interfaces(self):
- """Return an iterator for the interfaces in the specification.
- """
- yield self
- def getBases(self):
- return self.__bases__
- def isEqualOrExtendedBy(self, other):
- """Same interface or extends?"""
- return self == other or other.extends(self)
- def names(self, all=False): # pylint:disable=redefined-builtin
- """Return the attribute names defined by the interface."""
- if not all:
- return self.__attrs.keys()
- r = self.__attrs.copy()
- for base in self.__bases__:
- r.update(dict.fromkeys(base.names(all)))
- return r.keys()
- def __iter__(self):
- return iter(self.names(all=True))
- def namesAndDescriptions(self, all=False): # pylint:disable=redefined-builtin
- """Return attribute names and descriptions defined by interface."""
- if not all:
- return self.__attrs.items()
- r = {}
- for base in self.__bases__[::-1]:
- r.update(dict(base.namesAndDescriptions(all)))
- r.update(self.__attrs)
- return r.items()
- def getDescriptionFor(self, name):
- """Return the attribute description for the given name."""
- r = self.get(name)
- if r is not None:
- return r
- raise KeyError(name)
- __getitem__ = getDescriptionFor
- def __contains__(self, name):
- return self.get(name) is not None
- def direct(self, name):
- return self.__attrs.get(name)
- def queryDescriptionFor(self, name, default=None):
- return self.get(name, default)
- def validateInvariants(self, obj, errors=None):
- """validate object to defined invariants."""
- for iface in self.__iro__:
- for invariant in iface.queryDirectTaggedValue('invariants', ()):
- try:
- invariant(obj)
- except Invalid as error:
- if errors is not None:
- errors.append(error)
- else:
- raise
- if errors:
- raise Invalid(errors)
- def queryTaggedValue(self, tag, default=None):
- """
- Queries for the value associated with *tag*, returning it from the nearest
- interface in the ``__iro__``.
- If not found, returns *default*.
- """
- for iface in self.__iro__:
- value = iface.queryDirectTaggedValue(tag, _marker)
- if value is not _marker:
- return value
- return default
- def getTaggedValue(self, tag):
- """ Returns the value associated with 'tag'. """
- value = self.queryTaggedValue(tag, default=_marker)
- if value is _marker:
- raise KeyError(tag)
- return value
- def getTaggedValueTags(self):
- """ Returns a list of all tags. """
- keys = set()
- for base in self.__iro__:
- keys.update(base.getDirectTaggedValueTags())
- return keys
- def __repr__(self):
- try:
- return self._v_repr
- except AttributeError:
- name = str(self)
- r = "<%s %s>" % (self.__class__.__name__, name)
- self._v_repr = r # pylint:disable=attribute-defined-outside-init
- return r
- def __str__(self):
- name = self.__name__
- m = self.__ibmodule__
- if m:
- name = '%s.%s' % (m, name)
- return name
- def _call_conform(self, conform):
- try:
- return conform(self)
- except TypeError: # pragma: no cover
- # We got a TypeError. It might be an error raised by
- # the __conform__ implementation, or *we* may have
- # made the TypeError by calling an unbound method
- # (object is a class). In the later case, we behave
- # as though there is no __conform__ method. We can
- # detect this case by checking whether there is more
- # than one traceback object in the traceback chain:
- if sys.exc_info()[2].tb_next is not None:
- # There is more than one entry in the chain, so
- # reraise the error:
- raise
- # This clever trick is from Phillip Eby
- return None # pragma: no cover
- def __reduce__(self):
- return self.__name__
- Interface = InterfaceClass("Interface", __module__='zope.interface')
- # Interface is the only member of its own SRO.
- Interface._calculate_sro = lambda: (Interface,)
- Interface.changed(Interface)
- assert Interface.__sro__ == (Interface,)
- Specification._ROOT = Interface
- ro._ROOT = Interface
- class _InterfaceClassWithCustomMethods(InterfaceClass):
- """
- Marker class for interfaces with custom methods that override InterfaceClass methods.
- """
- class Attribute(Element):
- """Attribute descriptions
- """
- # We can't say this yet because we don't have enough
- # infrastructure in place.
- #
- # implements(IAttribute)
- interface = None
- def _get_str_info(self):
- """Return extra data to put at the end of __str__."""
- return ""
- def __str__(self):
- of = ''
- if self.interface is not None:
- of = self.interface.__module__ + '.' + self.interface.__name__ + '.'
- # self.__name__ may be None during construction (e.g., debugging)
- return of + (self.__name__ or '<unknown>') + self._get_str_info()
- def __repr__(self):
- return "<%s.%s object at 0x%x %s>" % (
- type(self).__module__,
- type(self).__name__,
- id(self),
- self
- )
- class Method(Attribute):
- """Method interfaces
- The idea here is that you have objects that describe methods.
- This provides an opportunity for rich meta-data.
- """
- # We can't say this yet because we don't have enough
- # infrastructure in place.
- #
- # implements(IMethod)
- positional = required = ()
- _optional = varargs = kwargs = None
- def _get_optional(self):
- if self._optional is None:
- return {}
- return self._optional
- def _set_optional(self, opt):
- self._optional = opt
- def _del_optional(self):
- self._optional = None
- optional = property(_get_optional, _set_optional, _del_optional)
- def __call__(self, *args, **kw):
- raise BrokenImplementation(self.interface, self.__name__)
- def getSignatureInfo(self):
- return {'positional': self.positional,
- 'required': self.required,
- 'optional': self.optional,
- 'varargs': self.varargs,
- 'kwargs': self.kwargs,
- }
- def getSignatureString(self):
- sig = []
- for v in self.positional:
- sig.append(v)
- if v in self.optional.keys():
- sig[-1] += "=" + repr(self.optional[v])
- if self.varargs:
- sig.append("*" + self.varargs)
- if self.kwargs:
- sig.append("**" + self.kwargs)
- return "(%s)" % ", ".join(sig)
- _get_str_info = getSignatureString
- def fromFunction(func, interface=None, imlevel=0, name=None):
- name = name or func.__name__
- method = Method(name, func.__doc__)
- defaults = getattr(func, '__defaults__', None) or ()
- code = func.__code__
- # Number of positional arguments
- na = code.co_argcount - imlevel
- names = code.co_varnames[imlevel:]
- opt = {}
- # Number of required arguments
- defaults_count = len(defaults)
- if not defaults_count:
- # PyPy3 uses ``__defaults_count__`` for builtin methods
- # like ``dict.pop``. Surprisingly, these don't have recorded
- # ``__defaults__``
- defaults_count = getattr(func, '__defaults_count__', 0)
- nr = na - defaults_count
- if nr < 0:
- defaults = defaults[-nr:]
- nr = 0
- # Determine the optional arguments.
- opt.update(dict(zip(names[nr:], defaults)))
- method.positional = names[:na]
- method.required = names[:nr]
- method.optional = opt
- argno = na
- # Determine the function's variable argument's name (i.e. *args)
- if code.co_flags & CO_VARARGS:
- method.varargs = names[argno]
- argno = argno + 1
- else:
- method.varargs = None
- # Determine the function's keyword argument's name (i.e. **kw)
- if code.co_flags & CO_VARKEYWORDS:
- method.kwargs = names[argno]
- else:
- method.kwargs = None
- method.interface = interface
- for key, value in func.__dict__.items():
- method.setTaggedValue(key, value)
- return method
- def fromMethod(meth, interface=None, name=None):
- if isinstance(meth, MethodType):
- func = meth.__func__
- else:
- func = meth
- return fromFunction(func, interface, imlevel=1, name=name)
- # Now we can create the interesting interfaces and wire them up:
- def _wire():
- from zope.interface.declarations import classImplements
- # From lest specific to most specific.
- from zope.interface.interfaces import IElement
- classImplements(Element, IElement)
- from zope.interface.interfaces import IAttribute
- classImplements(Attribute, IAttribute)
- from zope.interface.interfaces import IMethod
- classImplements(Method, IMethod)
- from zope.interface.interfaces import ISpecification
- classImplements(Specification, ISpecification)
- from zope.interface.interfaces import IInterface
- classImplements(InterfaceClass, IInterface)
- # We import this here to deal with module dependencies.
- # pylint:disable=wrong-import-position
- from zope.interface.declarations import implementedBy
- from zope.interface.declarations import providedBy
- from zope.interface.exceptions import InvalidInterface
- from zope.interface.exceptions import BrokenImplementation
- # This ensures that ``Interface`` winds up in the flattened()
- # list of the immutable declaration. It correctly overrides changed()
- # as a no-op, so we bypass that.
- from zope.interface.declarations import _empty
- Specification.changed(_empty, _empty)
|