|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723 |
- ##############################################################################
- #
- # Copyright (c) 2006 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.
- #
- ##############################################################################
- """Basic components support
- """
- from collections import defaultdict
-
- try:
- from zope.event import notify
- except ImportError: # pragma: no cover
- def notify(*arg, **kw): pass
-
- from zope.interface.interfaces import ISpecification
- from zope.interface.interfaces import ComponentLookupError
- from zope.interface.interfaces import IAdapterRegistration
- from zope.interface.interfaces import IComponents
- from zope.interface.interfaces import IHandlerRegistration
- from zope.interface.interfaces import ISubscriptionAdapterRegistration
- from zope.interface.interfaces import IUtilityRegistration
- from zope.interface.interfaces import Registered
- from zope.interface.interfaces import Unregistered
-
- from zope.interface.interface import Interface
- from zope.interface.declarations import implementedBy
- from zope.interface.declarations import implementer
- from zope.interface.declarations import implementer_only
- from zope.interface.declarations import providedBy
- from zope.interface.adapter import AdapterRegistry
-
- __all__ = [
- # Components is public API, but
- # the *Registration classes are just implementations
- # of public interfaces.
- 'Components',
- ]
-
- class _UnhashableComponentCounter:
- # defaultdict(int)-like object for unhashable components
-
- def __init__(self, otherdict):
- # [(component, count)]
- self._data = [item for item in otherdict.items()]
-
- def __getitem__(self, key):
- for component, count in self._data:
- if component == key:
- return count
- return 0
-
- def __setitem__(self, component, count):
- for i, data in enumerate(self._data):
- if data[0] == component:
- self._data[i] = component, count
- return
- self._data.append((component, count))
-
- def __delitem__(self, component):
- for i, data in enumerate(self._data):
- if data[0] == component:
- del self._data[i]
- return
- raise KeyError(component) # pragma: no cover
-
- def _defaultdict_int():
- return defaultdict(int)
-
- class _UtilityRegistrations:
-
- def __init__(self, utilities, utility_registrations):
- # {provided -> {component: count}}
- self._cache = defaultdict(_defaultdict_int)
- self._utilities = utilities
- self._utility_registrations = utility_registrations
-
- self.__populate_cache()
-
- def __populate_cache(self):
- for ((p, _), data) in iter(self._utility_registrations.items()):
- component = data[0]
- self.__cache_utility(p, component)
-
- def __cache_utility(self, provided, component):
- try:
- self._cache[provided][component] += 1
- except TypeError:
- # The component is not hashable, and we have a dict. Switch to a strategy
- # that doesn't use hashing.
- prov = self._cache[provided] = _UnhashableComponentCounter(self._cache[provided])
- prov[component] += 1
-
- def __uncache_utility(self, provided, component):
- provided = self._cache[provided]
- # It seems like this line could raise a TypeError if component isn't
- # hashable and we haven't yet switched to _UnhashableComponentCounter. However,
- # we can't actually get in that situation. In order to get here, we would
- # have had to cache the utility already which would have switched
- # the datastructure if needed.
- count = provided[component]
- count -= 1
- if count == 0:
- del provided[component]
- else:
- provided[component] = count
- return count > 0
-
- def _is_utility_subscribed(self, provided, component):
- try:
- return self._cache[provided][component] > 0
- except TypeError:
- # Not hashable and we're still using a dict
- return False
-
- def registerUtility(self, provided, name, component, info, factory):
- subscribed = self._is_utility_subscribed(provided, component)
-
- self._utility_registrations[(provided, name)] = component, info, factory
- self._utilities.register((), provided, name, component)
-
- if not subscribed:
- self._utilities.subscribe((), provided, component)
-
- self.__cache_utility(provided, component)
-
- def unregisterUtility(self, provided, name, component):
- del self._utility_registrations[(provided, name)]
- self._utilities.unregister((), provided, name)
-
- subscribed = self.__uncache_utility(provided, component)
-
- if not subscribed:
- self._utilities.unsubscribe((), provided, component)
-
-
- @implementer(IComponents)
- class Components:
-
- _v_utility_registrations_cache = None
-
- def __init__(self, name='', bases=()):
- # __init__ is used for test cleanup as well as initialization.
- # XXX add a separate API for test cleanup.
- assert isinstance(name, str)
- self.__name__ = name
- self._init_registries()
- self._init_registrations()
- self.__bases__ = tuple(bases)
- self._v_utility_registrations_cache = None
-
- def __repr__(self):
- return "<{} {}>".format(self.__class__.__name__, self.__name__)
-
- def __reduce__(self):
- # Mimic what a persistent.Persistent object does and elide
- # _v_ attributes so that they don't get saved in ZODB.
- # This allows us to store things that cannot be pickled in such
- # attributes.
- reduction = super().__reduce__()
- # (callable, args, state, listiter, dictiter)
- # We assume the state is always a dict; the last three items
- # are technically optional and can be missing or None.
- filtered_state = {k: v for k, v in reduction[2].items()
- if not k.startswith('_v_')}
- reduction = list(reduction)
- reduction[2] = filtered_state
- return tuple(reduction)
-
- def _init_registries(self):
- # Subclasses have never been required to call this method
- # if they override it, merely to fill in these two attributes.
- self.adapters = AdapterRegistry()
- self.utilities = AdapterRegistry()
-
- def _init_registrations(self):
- self._utility_registrations = {}
- self._adapter_registrations = {}
- self._subscription_registrations = []
- self._handler_registrations = []
-
- @property
- def _utility_registrations_cache(self):
- # We use a _v_ attribute internally so that data aren't saved in ZODB,
- # because this object cannot be pickled.
- cache = self._v_utility_registrations_cache
- if (cache is None
- or cache._utilities is not self.utilities
- or cache._utility_registrations is not self._utility_registrations):
- cache = self._v_utility_registrations_cache = _UtilityRegistrations(
- self.utilities,
- self._utility_registrations)
- return cache
-
- def _getBases(self):
- # Subclasses might override
- return self.__dict__.get('__bases__', ())
-
- def _setBases(self, bases):
- # Subclasses might override
- self.adapters.__bases__ = tuple([
- base.adapters for base in bases])
- self.utilities.__bases__ = tuple([
- base.utilities for base in bases])
- self.__dict__['__bases__'] = tuple(bases)
-
- __bases__ = property(
- lambda self: self._getBases(),
- lambda self, bases: self._setBases(bases),
- )
-
- def registerUtility(self, component=None, provided=None, name='',
- info='', event=True, factory=None):
- if factory:
- if component:
- raise TypeError("Can't specify factory and component.")
- component = factory()
-
- if provided is None:
- provided = _getUtilityProvided(component)
-
- if name == '':
- name = _getName(component)
-
- reg = self._utility_registrations.get((provided, name))
- if reg is not None:
- if reg[:2] == (component, info):
- # already registered
- return
- self.unregisterUtility(reg[0], provided, name)
-
- self._utility_registrations_cache.registerUtility(
- provided, name, component, info, factory)
-
- if event:
- notify(Registered(
- UtilityRegistration(self, provided, name, component, info,
- factory)
- ))
-
- def unregisterUtility(self, component=None, provided=None, name='',
- factory=None):
- if factory:
- if component:
- raise TypeError("Can't specify factory and component.")
- component = factory()
-
- if provided is None:
- if component is None:
- raise TypeError("Must specify one of component, factory and "
- "provided")
- provided = _getUtilityProvided(component)
-
- old = self._utility_registrations.get((provided, name))
- if (old is None) or ((component is not None) and
- (component != old[0])):
- return False
-
- if component is None:
- component = old[0]
-
- # Note that component is now the old thing registered
- self._utility_registrations_cache.unregisterUtility(
- provided, name, component)
-
- notify(Unregistered(
- UtilityRegistration(self, provided, name, component, *old[1:])
- ))
-
- return True
-
- def registeredUtilities(self):
- for ((provided, name), data
- ) in iter(self._utility_registrations.items()):
- yield UtilityRegistration(self, provided, name, *data)
-
- def queryUtility(self, provided, name='', default=None):
- return self.utilities.lookup((), provided, name, default)
-
- def getUtility(self, provided, name=''):
- utility = self.utilities.lookup((), provided, name)
- if utility is None:
- raise ComponentLookupError(provided, name)
- return utility
-
- def getUtilitiesFor(self, interface):
- yield from self.utilities.lookupAll((), interface)
-
- def getAllUtilitiesRegisteredFor(self, interface):
- return self.utilities.subscriptions((), interface)
-
- def registerAdapter(self, factory, required=None, provided=None,
- name='', info='', event=True):
- if provided is None:
- provided = _getAdapterProvided(factory)
- required = _getAdapterRequired(factory, required)
- if name == '':
- name = _getName(factory)
- self._adapter_registrations[(required, provided, name)
- ] = factory, info
- self.adapters.register(required, provided, name, factory)
-
- if event:
- notify(Registered(
- AdapterRegistration(self, required, provided, name,
- factory, info)
- ))
-
-
- def unregisterAdapter(self, factory=None,
- required=None, provided=None, name='',
- ):
- if provided is None:
- if factory is None:
- raise TypeError("Must specify one of factory and provided")
- provided = _getAdapterProvided(factory)
-
- if (required is None) and (factory is None):
- raise TypeError("Must specify one of factory and required")
-
- required = _getAdapterRequired(factory, required)
- old = self._adapter_registrations.get((required, provided, name))
- if (old is None) or ((factory is not None) and
- (factory != old[0])):
- return False
-
- del self._adapter_registrations[(required, provided, name)]
- self.adapters.unregister(required, provided, name)
-
- notify(Unregistered(
- AdapterRegistration(self, required, provided, name,
- *old)
- ))
-
- return True
-
- def registeredAdapters(self):
- for ((required, provided, name), (component, info)
- ) in iter(self._adapter_registrations.items()):
- yield AdapterRegistration(self, required, provided, name,
- component, info)
-
- def queryAdapter(self, object, interface, name='', default=None):
- return self.adapters.queryAdapter(object, interface, name, default)
-
- def getAdapter(self, object, interface, name=''):
- adapter = self.adapters.queryAdapter(object, interface, name)
- if adapter is None:
- raise ComponentLookupError(object, interface, name)
- return adapter
-
- def queryMultiAdapter(self, objects, interface, name='',
- default=None):
- return self.adapters.queryMultiAdapter(
- objects, interface, name, default)
-
- def getMultiAdapter(self, objects, interface, name=''):
- adapter = self.adapters.queryMultiAdapter(objects, interface, name)
- if adapter is None:
- raise ComponentLookupError(objects, interface, name)
- return adapter
-
- def getAdapters(self, objects, provided):
- for name, factory in self.adapters.lookupAll(
- list(map(providedBy, objects)),
- provided):
- adapter = factory(*objects)
- if adapter is not None:
- yield name, adapter
-
- def registerSubscriptionAdapter(self,
- factory, required=None, provided=None,
- name='', info='',
- event=True):
- if name:
- raise TypeError("Named subscribers are not yet supported")
- if provided is None:
- provided = _getAdapterProvided(factory)
- required = _getAdapterRequired(factory, required)
- self._subscription_registrations.append(
- (required, provided, name, factory, info)
- )
- self.adapters.subscribe(required, provided, factory)
-
- if event:
- notify(Registered(
- SubscriptionRegistration(self, required, provided, name,
- factory, info)
- ))
-
- def registeredSubscriptionAdapters(self):
- for data in self._subscription_registrations:
- yield SubscriptionRegistration(self, *data)
-
- def unregisterSubscriptionAdapter(self, factory=None,
- required=None, provided=None, name='',
- ):
- if name:
- raise TypeError("Named subscribers are not yet supported")
- if provided is None:
- if factory is None:
- raise TypeError("Must specify one of factory and provided")
- provided = _getAdapterProvided(factory)
-
- if (required is None) and (factory is None):
- raise TypeError("Must specify one of factory and required")
-
- required = _getAdapterRequired(factory, required)
-
- if factory is None:
- new = [(r, p, n, f, i)
- for (r, p, n, f, i)
- in self._subscription_registrations
- if not (r == required and p == provided)
- ]
- else:
- new = [(r, p, n, f, i)
- for (r, p, n, f, i)
- in self._subscription_registrations
- if not (r == required and p == provided and f == factory)
- ]
-
- if len(new) == len(self._subscription_registrations):
- return False
-
-
- self._subscription_registrations[:] = new
- self.adapters.unsubscribe(required, provided, factory)
-
- notify(Unregistered(
- SubscriptionRegistration(self, required, provided, name,
- factory, '')
- ))
-
- return True
-
- def subscribers(self, objects, provided):
- return self.adapters.subscribers(objects, provided)
-
- def registerHandler(self,
- factory, required=None,
- name='', info='',
- event=True):
- if name:
- raise TypeError("Named handlers are not yet supported")
- required = _getAdapterRequired(factory, required)
- self._handler_registrations.append(
- (required, name, factory, info)
- )
- self.adapters.subscribe(required, None, factory)
-
- if event:
- notify(Registered(
- HandlerRegistration(self, required, name, factory, info)
- ))
-
- def registeredHandlers(self):
- for data in self._handler_registrations:
- yield HandlerRegistration(self, *data)
-
- def unregisterHandler(self, factory=None, required=None, name=''):
- if name:
- raise TypeError("Named subscribers are not yet supported")
-
- if (required is None) and (factory is None):
- raise TypeError("Must specify one of factory and required")
-
- required = _getAdapterRequired(factory, required)
-
- if factory is None:
- new = [(r, n, f, i)
- for (r, n, f, i)
- in self._handler_registrations
- if r != required
- ]
- else:
- new = [(r, n, f, i)
- for (r, n, f, i)
- in self._handler_registrations
- if not (r == required and f == factory)
- ]
-
- if len(new) == len(self._handler_registrations):
- return False
-
- self._handler_registrations[:] = new
- self.adapters.unsubscribe(required, None, factory)
-
- notify(Unregistered(
- HandlerRegistration(self, required, name, factory, '')
- ))
-
- return True
-
- def handle(self, *objects):
- self.adapters.subscribers(objects, None)
-
- def rebuildUtilityRegistryFromLocalCache(self, rebuild=False):
- """
- Emergency maintenance method to rebuild the ``.utilities``
- registry from the local copy maintained in this object, or
- detect the need to do so.
-
- Most users will never need to call this, but it can be helpful
- in the event of suspected corruption.
-
- By default, this method only checks for corruption. To make it
- actually rebuild the registry, pass `True` for *rebuild*.
-
- :param bool rebuild: If set to `True` (not the default),
- this method will actually register and subscribe utilities
- in the registry as needed to synchronize with the local cache.
-
- :return: A dictionary that's meant as diagnostic data. The keys
- and values may change over time. When called with a false *rebuild*,
- the keys ``"needed_registered"`` and ``"needed_subscribed"`` will be
- non-zero if any corruption was detected, but that will not be corrected.
-
- .. versionadded:: 5.3.0
- """
- regs = dict(self._utility_registrations)
- utils = self.utilities
- needed_registered = 0
- did_not_register = 0
- needed_subscribed = 0
- did_not_subscribe = 0
-
-
- # Avoid the expensive change process during this; we'll call
- # it once at the end if needed.
- assert 'changed' not in utils.__dict__
- utils.changed = lambda _: None
-
- if rebuild:
- register = utils.register
- subscribe = utils.subscribe
- else:
- register = subscribe = lambda *args: None
-
- try:
- for (provided, name), (value, _info, _factory) in regs.items():
- if utils.registered((), provided, name) != value:
- register((), provided, name, value)
- needed_registered += 1
- else:
- did_not_register += 1
-
- if utils.subscribed((), provided, value) is None:
- needed_subscribed += 1
- subscribe((), provided, value)
- else:
- did_not_subscribe += 1
- finally:
- del utils.changed
- if rebuild and (needed_subscribed or needed_registered):
- utils.changed(utils)
-
- return {
- 'needed_registered': needed_registered,
- 'did_not_register': did_not_register,
- 'needed_subscribed': needed_subscribed,
- 'did_not_subscribe': did_not_subscribe
- }
-
- def _getName(component):
- try:
- return component.__component_name__
- except AttributeError:
- return ''
-
- def _getUtilityProvided(component):
- provided = list(providedBy(component))
- if len(provided) == 1:
- return provided[0]
- raise TypeError(
- "The utility doesn't provide a single interface "
- "and no provided interface was specified.")
-
- def _getAdapterProvided(factory):
- provided = list(implementedBy(factory))
- if len(provided) == 1:
- return provided[0]
- raise TypeError(
- "The adapter factory doesn't implement a single interface "
- "and no provided interface was specified.")
-
- def _getAdapterRequired(factory, required):
- if required is None:
- try:
- required = factory.__component_adapts__
- except AttributeError:
- raise TypeError(
- "The adapter factory doesn't have a __component_adapts__ "
- "attribute and no required specifications were specified"
- )
- elif ISpecification.providedBy(required):
- raise TypeError("the required argument should be a list of "
- "interfaces, not a single interface")
-
- result = []
- for r in required:
- if r is None:
- r = Interface
- elif not ISpecification.providedBy(r):
- if isinstance(r, type):
- r = implementedBy(r)
- else:
- raise TypeError("Required specification must be a "
- "specification or class, not %r" % type(r)
- )
- result.append(r)
- return tuple(result)
-
-
- @implementer(IUtilityRegistration)
- class UtilityRegistration:
-
- def __init__(self, registry, provided, name, component, doc, factory=None):
- (self.registry, self.provided, self.name, self.component, self.info,
- self.factory
- ) = registry, provided, name, component, doc, factory
-
- def __repr__(self):
- return '{}({!r}, {}, {!r}, {}, {!r}, {!r})'.format(
- self.__class__.__name__,
- self.registry,
- getattr(self.provided, '__name__', None), self.name,
- getattr(self.component, '__name__', repr(self.component)),
- self.factory, self.info,
- )
-
- def __hash__(self):
- return id(self)
-
- def __eq__(self, other):
- return repr(self) == repr(other)
-
- def __ne__(self, other):
- return repr(self) != repr(other)
-
- def __lt__(self, other):
- return repr(self) < repr(other)
-
- def __le__(self, other):
- return repr(self) <= repr(other)
-
- def __gt__(self, other):
- return repr(self) > repr(other)
-
- def __ge__(self, other):
- return repr(self) >= repr(other)
-
- @implementer(IAdapterRegistration)
- class AdapterRegistration:
-
- def __init__(self, registry, required, provided, name, component, doc):
- (self.registry, self.required, self.provided, self.name,
- self.factory, self.info
- ) = registry, required, provided, name, component, doc
-
- def __repr__(self):
- return '{}({!r}, {}, {}, {!r}, {}, {!r})'.format(
- self.__class__.__name__,
- self.registry,
- '[' + ", ".join([r.__name__ for r in self.required]) + ']',
- getattr(self.provided, '__name__', None), self.name,
- getattr(self.factory, '__name__', repr(self.factory)), self.info,
- )
-
- def __hash__(self):
- return id(self)
-
- def __eq__(self, other):
- return repr(self) == repr(other)
-
- def __ne__(self, other):
- return repr(self) != repr(other)
-
- def __lt__(self, other):
- return repr(self) < repr(other)
-
- def __le__(self, other):
- return repr(self) <= repr(other)
-
- def __gt__(self, other):
- return repr(self) > repr(other)
-
- def __ge__(self, other):
- return repr(self) >= repr(other)
-
- @implementer_only(ISubscriptionAdapterRegistration)
- class SubscriptionRegistration(AdapterRegistration):
- pass
-
-
- @implementer_only(IHandlerRegistration)
- class HandlerRegistration(AdapterRegistration):
-
- def __init__(self, registry, required, name, handler, doc):
- (self.registry, self.required, self.name, self.handler, self.info
- ) = registry, required, name, handler, doc
-
- @property
- def factory(self):
- return self.handler
-
- provided = None
-
- def __repr__(self):
- return '{}({!r}, {}, {!r}, {}, {!r})'.format(
- self.__class__.__name__,
- self.registry,
- '[' + ", ".join([r.__name__ for r in self.required]) + ']',
- self.name,
- getattr(self.factory, '__name__', repr(self.factory)), self.info,
- )
|