123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712 |
- ##############################################################################
- #
- # 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 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 STRING_TYPES
-
- _BLANK = u''
-
- class BaseAdapterRegistry(object):
-
- # 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 = []
-
- # {order -> {required -> {provided -> {name -> [value]}}}}
- # where the remarks about adapters above apply
- self._subscribers = []
-
- # Set, with a reference count, keeping track of the interfaces
- # for which we have provided components:
- self._provided = {}
-
- # 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):
- 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)
-
- 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, STRING_TYPES):
- raise ValueError('name is not a string')
- if value is None:
- self.unregister(required, provided, name, value)
- return
-
- required = tuple(map(_convert_None_to_Interface, required))
- name = _normalize_name(name)
- order = len(required)
- byorder = self._adapters
- while len(byorder) <= order:
- byorder.append({})
- components = byorder[order]
- key = required + (provided,)
-
- for k in key:
- d = components.get(k)
- if d is None:
- d = {}
- 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 registered(self, required, provided, name=_BLANK):
- required = tuple(map(_convert_None_to_Interface, required))
- name = _normalize_name(name)
- order = len(required)
- byorder = self._adapters
- 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 unregister(self, required, provided, name, value=None):
- required = tuple(map(_convert_None_to_Interface, 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(map(_convert_None_to_Interface, required))
- name = _BLANK
- order = len(required)
- byorder = self._subscribers
- while len(byorder) <= order:
- byorder.append({})
- components = byorder[order]
- key = required + (provided,)
-
- for k in key:
- d = components.get(k)
- if d is None:
- d = {}
- components[k] = d
- components = d
-
- components[name] = 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 unsubscribe(self, required, provided, value=None):
- required = tuple(map(_convert_None_to_Interface, 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(_BLANK)
- if not old:
- # this is belt-and-suspenders against the failure of cleanup below
- return # pragma: no cover
-
- if value is None:
- new = ()
- else:
- new = tuple([v for v in old if v != value])
-
- if new == old:
- return
-
- if new:
- components[_BLANK] = new
- else:
- # Instead of setting components[_BLANK] = 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[_BLANK]
- 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)
-
- self.changed(self)
-
- # XXX hack to fake out twisted's use of a private api. We need to get them
- # to use the new registed method.
- def get(self, _): # pragma: no cover
- class XXXTwistedFakeOut:
- selfImplied = {}
- return XXXTwistedFakeOut
-
-
- _not_in_mapping = object()
- class LookupBaseFallback(object):
-
- 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=_BLANK, default=None):
- if not isinstance(name, STRING_TYPES):
- 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=_BLANK, default=None):
- if not isinstance(name, STRING_TYPES):
- 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=_BLANK, default=None):
- return self.adapter_hook(provided, object, name, default)
-
- def adapter_hook(self, provided, object, name=_BLANK, default=None):
- if not isinstance(name, STRING_TYPES):
- 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:
- 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
-
- LookupBasePy = LookupBaseFallback # BBB
-
- try:
- from zope.interface._zope_interface_coptimizations import LookupBase
- except ImportError:
- LookupBase = LookupBaseFallback
-
-
- class VerifyingBaseFallback(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)
-
- VerifyingBasePy = VerifyingBaseFallback #BBB
-
- try:
- from zope.interface._zope_interface_coptimizations import VerifyingBase
- except ImportError:
- VerifyingBase = VerifyingBaseFallback
-
-
- class AdapterLookupBase(object):
-
- def __init__(self, registry):
- self._registry = registry
- self._required = {}
- self.init_extendors()
- super(AdapterLookupBase, self).__init__()
-
- def changed(self, ignored=None):
- super(AdapterLookupBase, self).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=_BLANK):
- 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=_BLANK, default=None):
- factory = self.lookup(map(providedBy, objects), provided, name)
- if factory is None:
- return default
-
- result = factory(*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, _BLANK,
- result, 0, order)
-
- self._subscribe(*required)
-
- return result
-
- def subscribers(self, objects, provided):
- subscriptions = self.subscriptions(map(providedBy, 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):
-
- LookupClass = AdapterLookup
-
- def __init__(self, bases=()):
- # AdapterRegisties are invalidating registries, so
- # we need to keep track of out invalidating subregistries.
- self._v_subregistries = weakref.WeakKeyDictionary()
-
- super(AdapterRegistry, self).__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(AdapterRegistry, self)._setBases(bases)
-
- def changed(self, originally_changed):
- super(AdapterRegistry, self).changed(originally_changed)
-
- for sub in self._v_subregistries.keys():
- sub.changed(originally_changed)
-
-
- class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase):
- pass
-
- @implementer(IAdapterRegistry)
- class VerifyingAdapterRegistry(BaseAdapterRegistry):
-
- 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):
- 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):
- 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):
- 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)
|