Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/zope/interface/registry.py : 48%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1##############################################################################
2#
3# Copyright (c) 2006 Zope Foundation and Contributors.
4# All Rights Reserved.
5#
6# This software is subject to the provisions of the Zope Public License,
7# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11# FOR A PARTICULAR PURPOSE.
12#
13##############################################################################
14"""Basic components support
15"""
16from collections import defaultdict
18try:
19 from zope.event import notify
20except ImportError: # pragma: no cover
21 def notify(*arg, **kw): pass
23from zope.interface.interfaces import ISpecification
24from zope.interface.interfaces import ComponentLookupError
25from zope.interface.interfaces import IAdapterRegistration
26from zope.interface.interfaces import IComponents
27from zope.interface.interfaces import IHandlerRegistration
28from zope.interface.interfaces import ISubscriptionAdapterRegistration
29from zope.interface.interfaces import IUtilityRegistration
30from zope.interface.interfaces import Registered
31from zope.interface.interfaces import Unregistered
33from zope.interface.interface import Interface
34from zope.interface.declarations import implementedBy
35from zope.interface.declarations import implementer
36from zope.interface.declarations import implementer_only
37from zope.interface.declarations import providedBy
38from zope.interface.adapter import AdapterRegistry
39from zope.interface._compat import CLASS_TYPES
40from zope.interface._compat import STRING_TYPES
42__all__ = [
43 # Components is public API, but
44 # the *Registration classes are just implementations
45 # of public interfaces.
46 'Components',
47]
49class _UnhashableComponentCounter(object):
50 # defaultdict(int)-like object for unhashable components
52 def __init__(self, otherdict):
53 # [(component, count)]
54 self._data = [item for item in otherdict.items()]
56 def __getitem__(self, key):
57 for component, count in self._data:
58 if component == key:
59 return count
60 return 0
62 def __setitem__(self, component, count):
63 for i, data in enumerate(self._data):
64 if data[0] == component:
65 self._data[i] = component, count
66 return
67 self._data.append((component, count))
69 def __delitem__(self, component):
70 for i, data in enumerate(self._data):
71 if data[0] == component:
72 del self._data[i]
73 return
74 raise KeyError(component) # pragma: no cover
76def _defaultdict_int():
77 return defaultdict(int)
79class _UtilityRegistrations(object):
81 def __init__(self, utilities, utility_registrations):
82 # {provided -> {component: count}}
83 self._cache = defaultdict(_defaultdict_int)
84 self._utilities = utilities
85 self._utility_registrations = utility_registrations
87 self.__populate_cache()
89 def __populate_cache(self):
90 for ((p, _), data) in iter(self._utility_registrations.items()):
91 component = data[0]
92 self.__cache_utility(p, component)
94 def __cache_utility(self, provided, component):
95 try:
96 self._cache[provided][component] += 1
97 except TypeError:
98 # The component is not hashable, and we have a dict. Switch to a strategy
99 # that doesn't use hashing.
100 prov = self._cache[provided] = _UnhashableComponentCounter(self._cache[provided])
101 prov[component] += 1
103 def __uncache_utility(self, provided, component):
104 provided = self._cache[provided]
105 # It seems like this line could raise a TypeError if component isn't
106 # hashable and we haven't yet switched to _UnhashableComponentCounter. However,
107 # we can't actually get in that situation. In order to get here, we would
108 # have had to cache the utility already which would have switched
109 # the datastructure if needed.
110 count = provided[component]
111 count -= 1
112 if count == 0:
113 del provided[component]
114 else:
115 provided[component] = count
116 return count > 0
118 def _is_utility_subscribed(self, provided, component):
119 try:
120 return self._cache[provided][component] > 0
121 except TypeError:
122 # Not hashable and we're still using a dict
123 return False
125 def registerUtility(self, provided, name, component, info, factory):
126 subscribed = self._is_utility_subscribed(provided, component)
128 self._utility_registrations[(provided, name)] = component, info, factory
129 self._utilities.register((), provided, name, component)
131 if not subscribed:
132 self._utilities.subscribe((), provided, component)
134 self.__cache_utility(provided, component)
136 def unregisterUtility(self, provided, name, component):
137 del self._utility_registrations[(provided, name)]
138 self._utilities.unregister((), provided, name)
140 subscribed = self.__uncache_utility(provided, component)
142 if not subscribed:
143 self._utilities.unsubscribe((), provided, component)
146@implementer(IComponents)
147class Components(object):
149 _v_utility_registrations_cache = None
151 def __init__(self, name='', bases=()):
152 # __init__ is used for test cleanup as well as initialization.
153 # XXX add a separate API for test cleanup.
154 assert isinstance(name, STRING_TYPES)
155 self.__name__ = name
156 self._init_registries()
157 self._init_registrations()
158 self.__bases__ = tuple(bases)
159 self._v_utility_registrations_cache = None
161 def __repr__(self):
162 return "<%s %s>" % (self.__class__.__name__, self.__name__)
164 def __reduce__(self):
165 # Mimic what a persistent.Persistent object does and elide
166 # _v_ attributes so that they don't get saved in ZODB.
167 # This allows us to store things that cannot be pickled in such
168 # attributes.
169 reduction = super(Components, self).__reduce__()
170 # (callable, args, state, listiter, dictiter)
171 # We assume the state is always a dict; the last three items
172 # are technically optional and can be missing or None.
173 filtered_state = {k: v for k, v in reduction[2].items()
174 if not k.startswith('_v_')}
175 reduction = list(reduction)
176 reduction[2] = filtered_state
177 return tuple(reduction)
179 def _init_registries(self):
180 # Subclasses have never been required to call this method
181 # if they override it, merely to fill in these two attributes.
182 self.adapters = AdapterRegistry()
183 self.utilities = AdapterRegistry()
185 def _init_registrations(self):
186 self._utility_registrations = {}
187 self._adapter_registrations = {}
188 self._subscription_registrations = []
189 self._handler_registrations = []
191 @property
192 def _utility_registrations_cache(self):
193 # We use a _v_ attribute internally so that data aren't saved in ZODB,
194 # because this object cannot be pickled.
195 cache = self._v_utility_registrations_cache
196 if (cache is None
197 or cache._utilities is not self.utilities
198 or cache._utility_registrations is not self._utility_registrations):
199 cache = self._v_utility_registrations_cache = _UtilityRegistrations(
200 self.utilities,
201 self._utility_registrations)
202 return cache
204 def _getBases(self):
205 # Subclasses might override
206 return self.__dict__.get('__bases__', ())
208 def _setBases(self, bases):
209 # Subclasses might override
210 self.adapters.__bases__ = tuple([
211 base.adapters for base in bases])
212 self.utilities.__bases__ = tuple([
213 base.utilities for base in bases])
214 self.__dict__['__bases__'] = tuple(bases)
216 __bases__ = property(
217 lambda self: self._getBases(),
218 lambda self, bases: self._setBases(bases),
219 )
221 def registerUtility(self, component=None, provided=None, name=u'',
222 info=u'', event=True, factory=None):
223 if factory:
224 if component:
225 raise TypeError("Can't specify factory and component.")
226 component = factory()
228 if provided is None:
229 provided = _getUtilityProvided(component)
231 if name == u'':
232 name = _getName(component)
234 reg = self._utility_registrations.get((provided, name))
235 if reg is not None:
236 if reg[:2] == (component, info):
237 # already registered
238 return
239 self.unregisterUtility(reg[0], provided, name)
241 self._utility_registrations_cache.registerUtility(
242 provided, name, component, info, factory)
244 if event:
245 notify(Registered(
246 UtilityRegistration(self, provided, name, component, info,
247 factory)
248 ))
250 def unregisterUtility(self, component=None, provided=None, name=u'',
251 factory=None):
252 if factory:
253 if component:
254 raise TypeError("Can't specify factory and component.")
255 component = factory()
257 if provided is None:
258 if component is None:
259 raise TypeError("Must specify one of component, factory and "
260 "provided")
261 provided = _getUtilityProvided(component)
263 old = self._utility_registrations.get((provided, name))
264 if (old is None) or ((component is not None) and
265 (component != old[0])):
266 return False
268 if component is None:
269 component = old[0]
271 # Note that component is now the old thing registered
272 self._utility_registrations_cache.unregisterUtility(
273 provided, name, component)
275 notify(Unregistered(
276 UtilityRegistration(self, provided, name, component, *old[1:])
277 ))
279 return True
281 def registeredUtilities(self):
282 for ((provided, name), data
283 ) in iter(self._utility_registrations.items()):
284 yield UtilityRegistration(self, provided, name, *data)
286 def queryUtility(self, provided, name=u'', default=None):
287 return self.utilities.lookup((), provided, name, default)
289 def getUtility(self, provided, name=u''):
290 utility = self.utilities.lookup((), provided, name)
291 if utility is None:
292 raise ComponentLookupError(provided, name)
293 return utility
295 def getUtilitiesFor(self, interface):
296 for name, utility in self.utilities.lookupAll((), interface):
297 yield name, utility
299 def getAllUtilitiesRegisteredFor(self, interface):
300 return self.utilities.subscriptions((), interface)
302 def registerAdapter(self, factory, required=None, provided=None,
303 name=u'', info=u'', event=True):
304 if provided is None:
305 provided = _getAdapterProvided(factory)
306 required = _getAdapterRequired(factory, required)
307 if name == u'':
308 name = _getName(factory)
309 self._adapter_registrations[(required, provided, name)
310 ] = factory, info
311 self.adapters.register(required, provided, name, factory)
313 if event:
314 notify(Registered(
315 AdapterRegistration(self, required, provided, name,
316 factory, info)
317 ))
320 def unregisterAdapter(self, factory=None,
321 required=None, provided=None, name=u'',
322 ):
323 if provided is None:
324 if factory is None:
325 raise TypeError("Must specify one of factory and provided")
326 provided = _getAdapterProvided(factory)
328 if (required is None) and (factory is None):
329 raise TypeError("Must specify one of factory and required")
331 required = _getAdapterRequired(factory, required)
332 old = self._adapter_registrations.get((required, provided, name))
333 if (old is None) or ((factory is not None) and
334 (factory != old[0])):
335 return False
337 del self._adapter_registrations[(required, provided, name)]
338 self.adapters.unregister(required, provided, name)
340 notify(Unregistered(
341 AdapterRegistration(self, required, provided, name,
342 *old)
343 ))
345 return True
347 def registeredAdapters(self):
348 for ((required, provided, name), (component, info)
349 ) in iter(self._adapter_registrations.items()):
350 yield AdapterRegistration(self, required, provided, name,
351 component, info)
353 def queryAdapter(self, object, interface, name=u'', default=None):
354 return self.adapters.queryAdapter(object, interface, name, default)
356 def getAdapter(self, object, interface, name=u''):
357 adapter = self.adapters.queryAdapter(object, interface, name)
358 if adapter is None:
359 raise ComponentLookupError(object, interface, name)
360 return adapter
362 def queryMultiAdapter(self, objects, interface, name=u'',
363 default=None):
364 return self.adapters.queryMultiAdapter(
365 objects, interface, name, default)
367 def getMultiAdapter(self, objects, interface, name=u''):
368 adapter = self.adapters.queryMultiAdapter(objects, interface, name)
369 if adapter is None:
370 raise ComponentLookupError(objects, interface, name)
371 return adapter
373 def getAdapters(self, objects, provided):
374 for name, factory in self.adapters.lookupAll(
375 list(map(providedBy, objects)),
376 provided):
377 adapter = factory(*objects)
378 if adapter is not None:
379 yield name, adapter
381 def registerSubscriptionAdapter(self,
382 factory, required=None, provided=None,
383 name=u'', info=u'',
384 event=True):
385 if name:
386 raise TypeError("Named subscribers are not yet supported")
387 if provided is None:
388 provided = _getAdapterProvided(factory)
389 required = _getAdapterRequired(factory, required)
390 self._subscription_registrations.append(
391 (required, provided, name, factory, info)
392 )
393 self.adapters.subscribe(required, provided, factory)
395 if event:
396 notify(Registered(
397 SubscriptionRegistration(self, required, provided, name,
398 factory, info)
399 ))
401 def registeredSubscriptionAdapters(self):
402 for data in self._subscription_registrations:
403 yield SubscriptionRegistration(self, *data)
405 def unregisterSubscriptionAdapter(self, factory=None,
406 required=None, provided=None, name=u'',
407 ):
408 if name:
409 raise TypeError("Named subscribers are not yet supported")
410 if provided is None:
411 if factory is None:
412 raise TypeError("Must specify one of factory and provided")
413 provided = _getAdapterProvided(factory)
415 if (required is None) and (factory is None):
416 raise TypeError("Must specify one of factory and required")
418 required = _getAdapterRequired(factory, required)
420 if factory is None:
421 new = [(r, p, n, f, i)
422 for (r, p, n, f, i)
423 in self._subscription_registrations
424 if not (r == required and p == provided)
425 ]
426 else:
427 new = [(r, p, n, f, i)
428 for (r, p, n, f, i)
429 in self._subscription_registrations
430 if not (r == required and p == provided and f == factory)
431 ]
433 if len(new) == len(self._subscription_registrations):
434 return False
437 self._subscription_registrations[:] = new
438 self.adapters.unsubscribe(required, provided, factory)
440 notify(Unregistered(
441 SubscriptionRegistration(self, required, provided, name,
442 factory, '')
443 ))
445 return True
447 def subscribers(self, objects, provided):
448 return self.adapters.subscribers(objects, provided)
450 def registerHandler(self,
451 factory, required=None,
452 name=u'', info=u'',
453 event=True):
454 if name:
455 raise TypeError("Named handlers are not yet supported")
456 required = _getAdapterRequired(factory, required)
457 self._handler_registrations.append(
458 (required, name, factory, info)
459 )
460 self.adapters.subscribe(required, None, factory)
462 if event:
463 notify(Registered(
464 HandlerRegistration(self, required, name, factory, info)
465 ))
467 def registeredHandlers(self):
468 for data in self._handler_registrations:
469 yield HandlerRegistration(self, *data)
471 def unregisterHandler(self, factory=None, required=None, name=u''):
472 if name:
473 raise TypeError("Named subscribers are not yet supported")
475 if (required is None) and (factory is None):
476 raise TypeError("Must specify one of factory and required")
478 required = _getAdapterRequired(factory, required)
480 if factory is None:
481 new = [(r, n, f, i)
482 for (r, n, f, i)
483 in self._handler_registrations
484 if r != required
485 ]
486 else:
487 new = [(r, n, f, i)
488 for (r, n, f, i)
489 in self._handler_registrations
490 if not (r == required and f == factory)
491 ]
493 if len(new) == len(self._handler_registrations):
494 return False
496 self._handler_registrations[:] = new
497 self.adapters.unsubscribe(required, None, factory)
499 notify(Unregistered(
500 HandlerRegistration(self, required, name, factory, '')
501 ))
503 return True
505 def handle(self, *objects):
506 self.adapters.subscribers(objects, None)
508 def rebuildUtilityRegistryFromLocalCache(self, rebuild=False):
509 """
510 Emergency maintenance method to rebuild the ``.utilities``
511 registry from the local copy maintained in this object, or
512 detect the need to do so.
514 Most users will never need to call this, but it can be helpful
515 in the event of suspected corruption.
517 By default, this method only checks for corruption. To make it
518 actually rebuild the registry, pass `True` for *rebuild*.
520 :param bool rebuild: If set to `True` (not the default),
521 this method will actually register and subscribe utilities
522 in the registry as needed to synchronize with the local cache.
524 :return: A dictionary that's meant as diagnostic data. The keys
525 and values may change over time. When called with a false *rebuild*,
526 the keys ``"needed_registered"`` and ``"needed_subscribed"`` will be
527 non-zero if any corruption was detected, but that will not be corrected.
529 .. versionadded:: 5.3.0
530 """
531 regs = dict(self._utility_registrations)
532 utils = self.utilities
533 needed_registered = 0
534 did_not_register = 0
535 needed_subscribed = 0
536 did_not_subscribe = 0
539 # Avoid the expensive change process during this; we'll call
540 # it once at the end if needed.
541 assert 'changed' not in utils.__dict__
542 utils.changed = lambda _: None
544 if rebuild:
545 register = utils.register
546 subscribe = utils.subscribe
547 else:
548 register = subscribe = lambda *args: None
550 try:
551 for (provided, name), (value, _info, _factory) in regs.items():
552 if utils.registered((), provided, name) != value:
553 register((), provided, name, value)
554 needed_registered += 1
555 else:
556 did_not_register += 1
558 if utils.subscribed((), provided, value) is None:
559 needed_subscribed += 1
560 subscribe((), provided, value)
561 else:
562 did_not_subscribe += 1
563 finally:
564 del utils.changed
565 if rebuild and (needed_subscribed or needed_registered):
566 utils.changed(utils)
568 return {
569 'needed_registered': needed_registered,
570 'did_not_register': did_not_register,
571 'needed_subscribed': needed_subscribed,
572 'did_not_subscribe': did_not_subscribe
573 }
575def _getName(component):
576 try:
577 return component.__component_name__
578 except AttributeError:
579 return u''
581def _getUtilityProvided(component):
582 provided = list(providedBy(component))
583 if len(provided) == 1:
584 return provided[0]
585 raise TypeError(
586 "The utility doesn't provide a single interface "
587 "and no provided interface was specified.")
589def _getAdapterProvided(factory):
590 provided = list(implementedBy(factory))
591 if len(provided) == 1:
592 return provided[0]
593 raise TypeError(
594 "The adapter factory doesn't implement a single interface "
595 "and no provided interface was specified.")
597def _getAdapterRequired(factory, required):
598 if required is None:
599 try:
600 required = factory.__component_adapts__
601 except AttributeError:
602 raise TypeError(
603 "The adapter factory doesn't have a __component_adapts__ "
604 "attribute and no required specifications were specified"
605 )
606 elif ISpecification.providedBy(required):
607 raise TypeError("the required argument should be a list of "
608 "interfaces, not a single interface")
610 result = []
611 for r in required:
612 if r is None:
613 r = Interface
614 elif not ISpecification.providedBy(r):
615 if isinstance(r, CLASS_TYPES):
616 r = implementedBy(r)
617 else:
618 raise TypeError("Required specification must be a "
619 "specification or class, not %r" % type(r)
620 )
621 result.append(r)
622 return tuple(result)
625@implementer(IUtilityRegistration)
626class UtilityRegistration(object):
628 def __init__(self, registry, provided, name, component, doc, factory=None):
629 (self.registry, self.provided, self.name, self.component, self.info,
630 self.factory
631 ) = registry, provided, name, component, doc, factory
633 def __repr__(self):
634 return '%s(%r, %s, %r, %s, %r, %r)' % (
635 self.__class__.__name__,
636 self.registry,
637 getattr(self.provided, '__name__', None), self.name,
638 getattr(self.component, '__name__', repr(self.component)),
639 self.factory, self.info,
640 )
642 def __hash__(self):
643 return id(self)
645 def __eq__(self, other):
646 return repr(self) == repr(other)
648 def __ne__(self, other):
649 return repr(self) != repr(other)
651 def __lt__(self, other):
652 return repr(self) < repr(other)
654 def __le__(self, other):
655 return repr(self) <= repr(other)
657 def __gt__(self, other):
658 return repr(self) > repr(other)
660 def __ge__(self, other):
661 return repr(self) >= repr(other)
663@implementer(IAdapterRegistration)
664class AdapterRegistration(object):
666 def __init__(self, registry, required, provided, name, component, doc):
667 (self.registry, self.required, self.provided, self.name,
668 self.factory, self.info
669 ) = registry, required, provided, name, component, doc
671 def __repr__(self):
672 return '%s(%r, %s, %s, %r, %s, %r)' % (
673 self.__class__.__name__,
674 self.registry,
675 '[' + ", ".join([r.__name__ for r in self.required]) + ']',
676 getattr(self.provided, '__name__', None), self.name,
677 getattr(self.factory, '__name__', repr(self.factory)), self.info,
678 )
680 def __hash__(self):
681 return id(self)
683 def __eq__(self, other):
684 return repr(self) == repr(other)
686 def __ne__(self, other):
687 return repr(self) != repr(other)
689 def __lt__(self, other):
690 return repr(self) < repr(other)
692 def __le__(self, other):
693 return repr(self) <= repr(other)
695 def __gt__(self, other):
696 return repr(self) > repr(other)
698 def __ge__(self, other):
699 return repr(self) >= repr(other)
701@implementer_only(ISubscriptionAdapterRegistration)
702class SubscriptionRegistration(AdapterRegistration):
703 pass
706@implementer_only(IHandlerRegistration)
707class HandlerRegistration(AdapterRegistration):
709 def __init__(self, registry, required, name, handler, doc):
710 (self.registry, self.required, self.name, self.handler, self.info
711 ) = registry, required, name, handler, doc
713 @property
714 def factory(self):
715 return self.handler
717 provided = None
719 def __repr__(self):
720 return '%s(%r, %s, %r, %s, %r)' % (
721 self.__class__.__name__,
722 self.registry,
723 '[' + ", ".join([r.__name__ for r in self.required]) + ']',
724 self.name,
725 getattr(self.factory, '__name__', repr(self.factory)), self.info,
726 )