1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015 |
- ##############################################################################
- #
- # Copyright (c) 2004 Zope Foundation and Contributors.
- # All Rights Reserved.
- #
- # This software is subject to the provisions of the Zope Public License,
- # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
- # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
- # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
- # FOR A PARTICULAR PURPOSE.
- #
- ##############################################################################
- """Adapter management
- """
- import itertools
- import weakref
-
- from zope.interface import implementer
- from zope.interface import providedBy
- from zope.interface import Interface
- from zope.interface import ro
- from zope.interface.interfaces import IAdapterRegistry
-
- from zope.interface._compat import _normalize_name
- from zope.interface._compat import _use_c_impl
-
- __all__ = [
- 'AdapterRegistry',
- 'VerifyingAdapterRegistry',
- ]
-
- # In the CPython implementation,
- # ``tuple`` and ``list`` cooperate so that ``tuple([some list])``
- # directly allocates and iterates at the C level without using a
- # Python iterator. That's not the case for
- # ``tuple(generator_expression)`` or ``tuple(map(func, it))``.
- ##
- # 3.8
- # ``tuple([t for t in range(10)])`` -> 610ns
- # ``tuple(t for t in range(10))`` -> 696ns
- # ``tuple(map(lambda t: t, range(10)))`` -> 881ns
- ##
- # 2.7
- # ``tuple([t fon t in range(10)])`` -> 625ns
- # ``tuple(t for t in range(10))`` -> 665ns
- # ``tuple(map(lambda t: t, range(10)))`` -> 958ns
- #
- # All three have substantial variance.
- ##
- # On PyPy, this is also the best option.
- ##
- # PyPy 2.7.18-7.3.3
- # ``tuple([t fon t in range(10)])`` -> 128ns
- # ``tuple(t for t in range(10))`` -> 175ns
- # ``tuple(map(lambda t: t, range(10)))`` -> 153ns
- ##
- # PyPy 3.7.9 7.3.3-beta
- # ``tuple([t fon t in range(10)])`` -> 82ns
- # ``tuple(t for t in range(10))`` -> 177ns
- # ``tuple(map(lambda t: t, range(10)))`` -> 168ns
- #
-
- class BaseAdapterRegistry:
- """
- A basic implementation of the data storage and algorithms required
- for a :class:`zope.interface.interfaces.IAdapterRegistry`.
-
- Subclasses can set the following attributes to control how the data
- is stored; in particular, these hooks can be helpful for ZODB
- persistence. They can be class attributes that are the named (or similar) type, or
- they can be methods that act as a constructor for an object that behaves
- like the types defined here; this object will not assume that they are type
- objects, but subclasses are free to do so:
-
- _sequenceType = list
- This is the type used for our two mutable top-level "byorder" sequences.
- Must support mutation operations like ``append()`` and ``del seq[index]``.
- These are usually small (< 10). Although at least one of them is
- accessed when performing lookups or queries on this object, the other
- is untouched. In many common scenarios, both are only required when
- mutating registrations and subscriptions (like what
- :meth:`zope.interface.interfaces.IComponents.registerUtility` does).
- This use pattern makes it an ideal candidate to be a
- :class:`~persistent.list.PersistentList`.
- _leafSequenceType = tuple
- This is the type used for the leaf sequences of subscribers.
- It could be set to a ``PersistentList`` to avoid many unnecessary data
- loads when subscribers aren't being used. Mutation operations are directed
- through :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`; if you use
- a mutable type, you'll need to override those.
- _mappingType = dict
- This is the mutable mapping type used for the keyed mappings.
- A :class:`~persistent.mapping.PersistentMapping`
- could be used to help reduce the number of data loads when the registry is large
- and parts of it are rarely used. Further reductions in data loads can come from
- using a :class:`~BTrees.OOBTree.OOBTree`, but care is required
- to be sure that all required/provided
- values are fully ordered (e.g., no required or provided values that are classes
- can be used).
- _providedType = dict
- This is the mutable mapping type used for the ``_provided`` mapping.
- This is separate from the generic mapping type because the values
- are always integers, so one might choose to use a more optimized data
- structure such as a :class:`~BTrees.OIBTree.OIBTree`.
- The same caveats regarding key types
- apply as for ``_mappingType``.
-
- It is possible to also set these on an instance, but because of the need to
- potentially also override :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`,
- this may be less useful in a persistent scenario; using a subclass is recommended.
-
- .. versionchanged:: 5.3.0
- Add support for customizing the way internal data
- structures are created.
- .. versionchanged:: 5.3.0
- Add methods :meth:`rebuild`, :meth:`allRegistrations`
- and :meth:`allSubscriptions`.
- """
-
- # List of methods copied from lookup sub-objects:
- _delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter',
- 'adapter_hook', 'lookupAll', 'names',
- 'subscriptions', 'subscribers')
-
- # All registries maintain a generation that can be used by verifying
- # registries
- _generation = 0
-
- def __init__(self, bases=()):
-
- # The comments here could be improved. Possibly this bit needs
- # explaining in a separate document, as the comments here can
- # be quite confusing. /regebro
-
- # {order -> {required -> {provided -> {name -> value}}}}
- # Here "order" is actually an index in a list, "required" and
- # "provided" are interfaces, and "required" is really a nested
- # key. So, for example:
- # for order == 0 (that is, self._adapters[0]), we have:
- # {provided -> {name -> value}}
- # but for order == 2 (that is, self._adapters[2]), we have:
- # {r1 -> {r2 -> {provided -> {name -> value}}}}
- #
- self._adapters = self._sequenceType()
-
- # {order -> {required -> {provided -> {name -> [value]}}}}
- # where the remarks about adapters above apply
- self._subscribers = self._sequenceType()
-
- # Set, with a reference count, keeping track of the interfaces
- # for which we have provided components:
- self._provided = self._providedType()
-
- # Create ``_v_lookup`` object to perform lookup. We make this a
- # separate object to to make it easier to implement just the
- # lookup functionality in C. This object keeps track of cache
- # invalidation data in two kinds of registries.
-
- # Invalidating registries have caches that are invalidated
- # when they or their base registies change. An invalidating
- # registry can only have invalidating registries as bases.
- # See LookupBaseFallback below for the pertinent logic.
-
- # Verifying registies can't rely on getting invalidation messages,
- # so have to check the generations of base registries to determine
- # if their cache data are current. See VerifyingBasePy below
- # for the pertinent object.
- self._createLookup()
-
- # Setting the bases causes the registries described above
- # to be initialized (self._setBases -> self.changed ->
- # self._v_lookup.changed).
-
- self.__bases__ = bases
-
- def _setBases(self, bases):
- """
- If subclasses need to track when ``__bases__`` changes, they
- can override this method.
-
- Subclasses must still call this method.
- """
- self.__dict__['__bases__'] = bases
- self.ro = ro.ro(self)
- self.changed(self)
-
- __bases__ = property(lambda self: self.__dict__['__bases__'],
- lambda self, bases: self._setBases(bases),
- )
-
- def _createLookup(self):
- self._v_lookup = self.LookupClass(self)
- for name in self._delegated:
- self.__dict__[name] = getattr(self._v_lookup, name)
-
- # Hooks for subclasses to define the types of objects used in
- # our data structures.
- # These have to be documented in the docstring, instead of local
- # comments, because Sphinx autodoc ignores the comment and just writes
- # "alias of list"
- _sequenceType = list
- _leafSequenceType = tuple
- _mappingType = dict
- _providedType = dict
-
- def _addValueToLeaf(self, existing_leaf_sequence, new_item):
- """
- Add the value *new_item* to the *existing_leaf_sequence*, which may
- be ``None``.
-
- Subclasses that redefine `_leafSequenceType` should override this method.
-
- :param existing_leaf_sequence:
- If *existing_leaf_sequence* is not *None*, it will be an instance
- of `_leafSequenceType`. (Unless the object has been unpickled
- from an old pickle and the class definition has changed, in which case
- it may be an instance of a previous definition, commonly a `tuple`.)
-
- :return:
- This method returns the new value to be stored. It may mutate the
- sequence in place if it was not ``None`` and the type is mutable, but
- it must also return it.
-
- .. versionadded:: 5.3.0
- """
- if existing_leaf_sequence is None:
- return (new_item,)
- return existing_leaf_sequence + (new_item,)
-
- def _removeValueFromLeaf(self, existing_leaf_sequence, to_remove):
- """
- Remove the item *to_remove* from the (non-``None``, non-empty)
- *existing_leaf_sequence* and return the mutated sequence.
-
- If there is more than one item that is equal to *to_remove*
- they must all be removed.
-
- Subclasses that redefine `_leafSequenceType` should override
- this method. Note that they can call this method to help
- in their implementation; this implementation will always
- return a new tuple constructed by iterating across
- the *existing_leaf_sequence* and omitting items equal to *to_remove*.
-
- :param existing_leaf_sequence:
- As for `_addValueToLeaf`, probably an instance of
- `_leafSequenceType` but possibly an older type; never `None`.
- :return:
- A version of *existing_leaf_sequence* with all items equal to
- *to_remove* removed. Must not return `None`. However,
- returning an empty
- object, even of another type such as the empty tuple, ``()`` is
- explicitly allowed; such an object will never be stored.
-
- .. versionadded:: 5.3.0
- """
- return tuple([v for v in existing_leaf_sequence if v != to_remove])
-
- def changed(self, originally_changed):
- self._generation += 1
- self._v_lookup.changed(originally_changed)
-
- def register(self, required, provided, name, value):
- if not isinstance(name, str):
- raise ValueError('name is not a string')
- if value is None:
- self.unregister(required, provided, name, value)
- return
-
- required = tuple([_convert_None_to_Interface(r) for r in required])
- name = _normalize_name(name)
- order = len(required)
- byorder = self._adapters
- while len(byorder) <= order:
- byorder.append(self._mappingType())
- components = byorder[order]
- key = required + (provided,)
-
- for k in key:
- d = components.get(k)
- if d is None:
- d = self._mappingType()
- components[k] = d
- components = d
-
- if components.get(name) is value:
- return
-
- components[name] = value
-
- n = self._provided.get(provided, 0) + 1
- self._provided[provided] = n
- if n == 1:
- self._v_lookup.add_extendor(provided)
-
- self.changed(self)
-
- def _find_leaf(self, byorder, required, provided, name):
- # Find the leaf value, if any, in the *byorder* list
- # for the interface sequence *required* and the interface
- # *provided*, given the already normalized *name*.
- #
- # If no such leaf value exists, returns ``None``
- required = tuple([_convert_None_to_Interface(r) for r in required])
- order = len(required)
- if len(byorder) <= order:
- return None
-
- components = byorder[order]
- key = required + (provided,)
-
- for k in key:
- d = components.get(k)
- if d is None:
- return None
- components = d
-
- return components.get(name)
-
- def registered(self, required, provided, name=''):
- return self._find_leaf(
- self._adapters,
- required,
- provided,
- _normalize_name(name)
- )
-
- @classmethod
- def _allKeys(cls, components, i, parent_k=()):
- if i == 0:
- for k, v in components.items():
- yield parent_k + (k,), v
- else:
- for k, v in components.items():
- new_parent_k = parent_k + (k,)
- yield from cls._allKeys(v, i - 1, new_parent_k)
-
- def _all_entries(self, byorder):
- # Recurse through the mapping levels of the `byorder` sequence,
- # reconstructing a flattened sequence of ``(required, provided, name, value)``
- # tuples that can be used to reconstruct the sequence with the appropriate
- # registration methods.
- #
- # Locally reference the `byorder` data; it might be replaced while
- # this method is running (see ``rebuild``).
- for i, components in enumerate(byorder):
- # We will have *i* levels of dictionaries to go before
- # we get to the leaf.
- for key, value in self._allKeys(components, i + 1):
- assert len(key) == i + 2
- required = key[:i]
- provided = key[-2]
- name = key[-1]
- yield (required, provided, name, value)
-
- def allRegistrations(self):
- """
- Yields tuples ``(required, provided, name, value)`` for all
- the registrations that this object holds.
-
- These tuples could be passed as the arguments to the
- :meth:`register` method on another adapter registry to
- duplicate the registrations this object holds.
-
- .. versionadded:: 5.3.0
- """
- yield from self._all_entries(self._adapters)
-
- def unregister(self, required, provided, name, value=None):
- required = tuple([_convert_None_to_Interface(r) for r in required])
- order = len(required)
- byorder = self._adapters
- if order >= len(byorder):
- return False
- components = byorder[order]
- key = required + (provided,)
-
- # Keep track of how we got to `components`:
- lookups = []
- for k in key:
- d = components.get(k)
- if d is None:
- return
- lookups.append((components, k))
- components = d
-
- old = components.get(name)
- if old is None:
- return
- if (value is not None) and (old is not value):
- return
-
- del components[name]
- if not components:
- # Clean out empty containers, since we don't want our keys
- # to reference global objects (interfaces) unnecessarily.
- # This is often a problem when an interface is slated for
- # removal; a hold-over entry in the registry can make it
- # difficult to remove such interfaces.
- for comp, k in reversed(lookups):
- d = comp[k]
- if d:
- break
- else:
- del comp[k]
- while byorder and not byorder[-1]:
- del byorder[-1]
- n = self._provided[provided] - 1
- if n == 0:
- del self._provided[provided]
- self._v_lookup.remove_extendor(provided)
- else:
- self._provided[provided] = n
-
- self.changed(self)
-
- def subscribe(self, required, provided, value):
- required = tuple([_convert_None_to_Interface(r) for r in required])
- name = ''
- order = len(required)
- byorder = self._subscribers
- while len(byorder) <= order:
- byorder.append(self._mappingType())
- components = byorder[order]
- key = required + (provided,)
-
- for k in key:
- d = components.get(k)
- if d is None:
- d = self._mappingType()
- components[k] = d
- components = d
-
- components[name] = self._addValueToLeaf(components.get(name), value)
-
- if provided is not None:
- n = self._provided.get(provided, 0) + 1
- self._provided[provided] = n
- if n == 1:
- self._v_lookup.add_extendor(provided)
-
- self.changed(self)
-
- def subscribed(self, required, provided, subscriber):
- subscribers = self._find_leaf(
- self._subscribers,
- required,
- provided,
- ''
- ) or ()
- return subscriber if subscriber in subscribers else None
-
- def allSubscriptions(self):
- """
- Yields tuples ``(required, provided, value)`` for all the
- subscribers that this object holds.
-
- These tuples could be passed as the arguments to the
- :meth:`subscribe` method on another adapter registry to
- duplicate the registrations this object holds.
-
- .. versionadded:: 5.3.0
- """
- for required, provided, _name, value in self._all_entries(self._subscribers):
- for v in value:
- yield (required, provided, v)
-
- def unsubscribe(self, required, provided, value=None):
- required = tuple([_convert_None_to_Interface(r) for r in required])
- order = len(required)
- byorder = self._subscribers
- if order >= len(byorder):
- return
- components = byorder[order]
- key = required + (provided,)
-
- # Keep track of how we got to `components`:
- lookups = []
- for k in key:
- d = components.get(k)
- if d is None:
- return
- lookups.append((components, k))
- components = d
-
- old = components.get('')
- if not old:
- # this is belt-and-suspenders against the failure of cleanup below
- return # pragma: no cover
- len_old = len(old)
- if value is None:
- # Removing everything; note that the type of ``new`` won't
- # necessarily match the ``_leafSequenceType``, but that's
- # OK because we're about to delete the entire entry
- # anyway.
- new = ()
- else:
- new = self._removeValueFromLeaf(old, value)
- # ``new`` may be the same object as ``old``, just mutated in place,
- # so we cannot compare it to ``old`` to check for changes. Remove
- # our reference to it now to avoid trying to do so below.
- del old
-
- if len(new) == len_old:
- # No changes, so nothing could have been removed.
- return
-
- if new:
- components[''] = new
- else:
- # Instead of setting components[u''] = new, we clean out
- # empty containers, since we don't want our keys to
- # reference global objects (interfaces) unnecessarily. This
- # is often a problem when an interface is slated for
- # removal; a hold-over entry in the registry can make it
- # difficult to remove such interfaces.
- del components['']
- for comp, k in reversed(lookups):
- d = comp[k]
- if d:
- break
- else:
- del comp[k]
- while byorder and not byorder[-1]:
- del byorder[-1]
-
- if provided is not None:
- n = self._provided[provided] + len(new) - len_old
- if n == 0:
- del self._provided[provided]
- self._v_lookup.remove_extendor(provided)
- else:
- self._provided[provided] = n
-
- self.changed(self)
-
- def rebuild(self):
- """
- Rebuild (and replace) all the internal data structures of this
- object.
-
- This is useful, especially for persistent implementations, if
- you suspect an issue with reference counts keeping interfaces
- alive even though they are no longer used.
-
- It is also useful if you or a subclass change the data types
- (``_mappingType`` and friends) that are to be used.
-
- This method replaces all internal data structures with new objects;
- it specifically does not re-use any storage.
-
- .. versionadded:: 5.3.0
- """
-
- # Grab the iterators, we're about to discard their data.
- registrations = self.allRegistrations()
- subscriptions = self.allSubscriptions()
-
- def buffer(it):
- # The generator doesn't actually start running until we
- # ask for its next(), by which time the attributes will change
- # unless we do so before calling __init__.
- try:
- first = next(it)
- except StopIteration:
- return iter(())
-
- return itertools.chain((first,), it)
-
- registrations = buffer(registrations)
- subscriptions = buffer(subscriptions)
-
-
- # Replace the base data structures as well as _v_lookup.
- self.__init__(self.__bases__)
- # Re-register everything previously registered and subscribed.
- #
- # XXX: This is going to call ``self.changed()`` a lot, all of
- # which is unnecessary (because ``self.__init__`` just
- # re-created those dependent objects and also called
- # ``self.changed()``). Is this a bottleneck that needs fixed?
- # (We could do ``self.changed = lambda _: None`` before
- # beginning and remove it after to disable the presumably expensive
- # part of passing that notification to the change of objects.)
- for args in registrations:
- self.register(*args)
- for args in subscriptions:
- self.subscribe(*args)
-
- # XXX hack to fake out twisted's use of a private api. We need to get them
- # to use the new registered method.
- def get(self, _): # pragma: no cover
- class XXXTwistedFakeOut:
- selfImplied = {}
- return XXXTwistedFakeOut
-
-
- _not_in_mapping = object()
-
- @_use_c_impl
- class LookupBase:
-
- def __init__(self):
- self._cache = {}
- self._mcache = {}
- self._scache = {}
-
- def changed(self, ignored=None):
- self._cache.clear()
- self._mcache.clear()
- self._scache.clear()
-
- def _getcache(self, provided, name):
- cache = self._cache.get(provided)
- if cache is None:
- cache = {}
- self._cache[provided] = cache
- if name:
- c = cache.get(name)
- if c is None:
- c = {}
- cache[name] = c
- cache = c
- return cache
-
- def lookup(self, required, provided, name='', default=None):
- if not isinstance(name, str):
- raise ValueError('name is not a string')
- cache = self._getcache(provided, name)
- required = tuple(required)
- if len(required) == 1:
- result = cache.get(required[0], _not_in_mapping)
- else:
- result = cache.get(tuple(required), _not_in_mapping)
-
- if result is _not_in_mapping:
- result = self._uncached_lookup(required, provided, name)
- if len(required) == 1:
- cache[required[0]] = result
- else:
- cache[tuple(required)] = result
-
- if result is None:
- return default
-
- return result
-
- def lookup1(self, required, provided, name='', default=None):
- if not isinstance(name, str):
- raise ValueError('name is not a string')
- cache = self._getcache(provided, name)
- result = cache.get(required, _not_in_mapping)
- if result is _not_in_mapping:
- return self.lookup((required, ), provided, name, default)
-
- if result is None:
- return default
-
- return result
-
- def queryAdapter(self, object, provided, name='', default=None):
- return self.adapter_hook(provided, object, name, default)
-
- def adapter_hook(self, provided, object, name='', default=None):
- if not isinstance(name, str):
- raise ValueError('name is not a string')
- required = providedBy(object)
- cache = self._getcache(provided, name)
- factory = cache.get(required, _not_in_mapping)
- if factory is _not_in_mapping:
- factory = self.lookup((required, ), provided, name)
-
- if factory is not None:
- if isinstance(object, super):
- object = object.__self__
- result = factory(object)
- if result is not None:
- return result
-
- return default
-
- def lookupAll(self, required, provided):
- cache = self._mcache.get(provided)
- if cache is None:
- cache = {}
- self._mcache[provided] = cache
-
- required = tuple(required)
- result = cache.get(required, _not_in_mapping)
- if result is _not_in_mapping:
- result = self._uncached_lookupAll(required, provided)
- cache[required] = result
-
- return result
-
-
- def subscriptions(self, required, provided):
- cache = self._scache.get(provided)
- if cache is None:
- cache = {}
- self._scache[provided] = cache
-
- required = tuple(required)
- result = cache.get(required, _not_in_mapping)
- if result is _not_in_mapping:
- result = self._uncached_subscriptions(required, provided)
- cache[required] = result
-
- return result
-
-
- @_use_c_impl
- class VerifyingBase(LookupBaseFallback):
- # Mixin for lookups against registries which "chain" upwards, and
- # whose lookups invalidate their own caches whenever a parent registry
- # bumps its own '_generation' counter. E.g., used by
- # zope.component.persistentregistry
-
- def changed(self, originally_changed):
- LookupBaseFallback.changed(self, originally_changed)
- self._verify_ro = self._registry.ro[1:]
- self._verify_generations = [r._generation for r in self._verify_ro]
-
- def _verify(self):
- if ([r._generation for r in self._verify_ro]
- != self._verify_generations):
- self.changed(None)
-
- def _getcache(self, provided, name):
- self._verify()
- return LookupBaseFallback._getcache(self, provided, name)
-
- def lookupAll(self, required, provided):
- self._verify()
- return LookupBaseFallback.lookupAll(self, required, provided)
-
- def subscriptions(self, required, provided):
- self._verify()
- return LookupBaseFallback.subscriptions(self, required, provided)
-
-
- class AdapterLookupBase:
-
- def __init__(self, registry):
- self._registry = registry
- self._required = {}
- self.init_extendors()
- super().__init__()
-
- def changed(self, ignored=None):
- super().changed(None)
- for r in self._required.keys():
- r = r()
- if r is not None:
- r.unsubscribe(self)
- self._required.clear()
-
-
- # Extendors
- # ---------
-
- # When given an target interface for an adapter lookup, we need to consider
- # adapters for interfaces that extend the target interface. This is
- # what the extendors dictionary is about. It tells us all of the
- # interfaces that extend an interface for which there are adapters
- # registered.
-
- # We could separate this by order and name, thus reducing the
- # number of provided interfaces to search at run time. The tradeoff,
- # however, is that we have to store more information. For example,
- # if the same interface is provided for multiple names and if the
- # interface extends many interfaces, we'll have to keep track of
- # a fair bit of information for each name. It's better to
- # be space efficient here and be time efficient in the cache
- # implementation.
-
- # TODO: add invalidation when a provided interface changes, in case
- # the interface's __iro__ has changed. This is unlikely enough that
- # we'll take our chances for now.
-
- def init_extendors(self):
- self._extendors = {}
- for p in self._registry._provided:
- self.add_extendor(p)
-
- def add_extendor(self, provided):
- _extendors = self._extendors
- for i in provided.__iro__:
- extendors = _extendors.get(i, ())
- _extendors[i] = (
- [e for e in extendors if provided.isOrExtends(e)]
- +
- [provided]
- +
- [e for e in extendors if not provided.isOrExtends(e)]
- )
-
- def remove_extendor(self, provided):
- _extendors = self._extendors
- for i in provided.__iro__:
- _extendors[i] = [e for e in _extendors.get(i, ())
- if e != provided]
-
-
- def _subscribe(self, *required):
- _refs = self._required
- for r in required:
- ref = r.weakref()
- if ref not in _refs:
- r.subscribe(self)
- _refs[ref] = 1
-
- def _uncached_lookup(self, required, provided, name=''):
- required = tuple(required)
- result = None
- order = len(required)
- for registry in self._registry.ro:
- byorder = registry._adapters
- if order >= len(byorder):
- continue
-
- extendors = registry._v_lookup._extendors.get(provided)
- if not extendors:
- continue
-
- components = byorder[order]
- result = _lookup(components, required, extendors, name, 0,
- order)
- if result is not None:
- break
-
- self._subscribe(*required)
-
- return result
-
- def queryMultiAdapter(self, objects, provided, name='', default=None):
- factory = self.lookup([providedBy(o) for o in objects], provided, name)
- if factory is None:
- return default
-
- result = factory(*[o.__self__ if isinstance(o, super) else o for o in objects])
- if result is None:
- return default
-
- return result
-
- def _uncached_lookupAll(self, required, provided):
- required = tuple(required)
- order = len(required)
- result = {}
- for registry in reversed(self._registry.ro):
- byorder = registry._adapters
- if order >= len(byorder):
- continue
- extendors = registry._v_lookup._extendors.get(provided)
- if not extendors:
- continue
- components = byorder[order]
- _lookupAll(components, required, extendors, result, 0, order)
-
- self._subscribe(*required)
-
- return tuple(result.items())
-
- def names(self, required, provided):
- return [c[0] for c in self.lookupAll(required, provided)]
-
- def _uncached_subscriptions(self, required, provided):
- required = tuple(required)
- order = len(required)
- result = []
- for registry in reversed(self._registry.ro):
- byorder = registry._subscribers
- if order >= len(byorder):
- continue
-
- if provided is None:
- extendors = (provided, )
- else:
- extendors = registry._v_lookup._extendors.get(provided)
- if extendors is None:
- continue
-
- _subscriptions(byorder[order], required, extendors, '',
- result, 0, order)
-
- self._subscribe(*required)
-
- return result
-
- def subscribers(self, objects, provided):
- subscriptions = self.subscriptions([providedBy(o) for o in objects], provided)
- if provided is None:
- result = ()
- for subscription in subscriptions:
- subscription(*objects)
- else:
- result = []
- for subscription in subscriptions:
- subscriber = subscription(*objects)
- if subscriber is not None:
- result.append(subscriber)
- return result
-
- class AdapterLookup(AdapterLookupBase, LookupBase):
- pass
-
- @implementer(IAdapterRegistry)
- class AdapterRegistry(BaseAdapterRegistry):
- """
- A full implementation of ``IAdapterRegistry`` that adds support for
- sub-registries.
- """
-
- LookupClass = AdapterLookup
-
- def __init__(self, bases=()):
- # AdapterRegisties are invalidating registries, so
- # we need to keep track of our invalidating subregistries.
- self._v_subregistries = weakref.WeakKeyDictionary()
-
- super().__init__(bases)
-
- def _addSubregistry(self, r):
- self._v_subregistries[r] = 1
-
- def _removeSubregistry(self, r):
- if r in self._v_subregistries:
- del self._v_subregistries[r]
-
- def _setBases(self, bases):
- old = self.__dict__.get('__bases__', ())
- for r in old:
- if r not in bases:
- r._removeSubregistry(self)
- for r in bases:
- if r not in old:
- r._addSubregistry(self)
-
- super()._setBases(bases)
-
- def changed(self, originally_changed):
- super().changed(originally_changed)
-
- for sub in self._v_subregistries.keys():
- sub.changed(originally_changed)
-
-
- class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase):
- pass
-
- @implementer(IAdapterRegistry)
- class VerifyingAdapterRegistry(BaseAdapterRegistry):
- """
- The most commonly-used adapter registry.
- """
-
- LookupClass = VerifyingAdapterLookup
-
- def _convert_None_to_Interface(x):
- if x is None:
- return Interface
- else:
- return x
-
- def _lookup(components, specs, provided, name, i, l):
- # this function is called very often.
- # The components.get in loops is executed 100 of 1000s times.
- # by loading get into a local variable the bytecode
- # "LOAD_FAST 0 (components)" in the loop can be eliminated.
- components_get = components.get
- if i < l:
- for spec in specs[i].__sro__:
- comps = components_get(spec)
- if comps:
- r = _lookup(comps, specs, provided, name, i+1, l)
- if r is not None:
- return r
- else:
- for iface in provided:
- comps = components_get(iface)
- if comps:
- r = comps.get(name)
- if r is not None:
- return r
-
- return None
-
- def _lookupAll(components, specs, provided, result, i, l):
- components_get = components.get # see _lookup above
- if i < l:
- for spec in reversed(specs[i].__sro__):
- comps = components_get(spec)
- if comps:
- _lookupAll(comps, specs, provided, result, i+1, l)
- else:
- for iface in reversed(provided):
- comps = components_get(iface)
- if comps:
- result.update(comps)
-
- def _subscriptions(components, specs, provided, name, result, i, l):
- components_get = components.get # see _lookup above
- if i < l:
- for spec in reversed(specs[i].__sro__):
- comps = components_get(spec)
- if comps:
- _subscriptions(comps, specs, provided, name, result, i+1, l)
- else:
- for iface in reversed(provided):
- comps = components_get(iface)
- if comps:
- comps = comps.get(name)
- if comps:
- result.extend(comps)
|