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

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) 2004 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"""Adapter management
15"""
16import itertools
17import weakref
19from zope.interface import implementer
20from zope.interface import providedBy
21from zope.interface import Interface
22from zope.interface import ro
23from zope.interface.interfaces import IAdapterRegistry
25from zope.interface._compat import _normalize_name
26from zope.interface._compat import STRING_TYPES
27from zope.interface._compat import _use_c_impl
29__all__ = [
30 'AdapterRegistry',
31 'VerifyingAdapterRegistry',
32]
34# In the CPython implementation,
35# ``tuple`` and ``list`` cooperate so that ``tuple([some list])``
36# directly allocates and iterates at the C level without using a
37# Python iterator. That's not the case for
38# ``tuple(generator_expression)`` or ``tuple(map(func, it))``.
39##
40# 3.8
41# ``tuple([t for t in range(10)])`` -> 610ns
42# ``tuple(t for t in range(10))`` -> 696ns
43# ``tuple(map(lambda t: t, range(10)))`` -> 881ns
44##
45# 2.7
46# ``tuple([t fon t in range(10)])`` -> 625ns
47# ``tuple(t for t in range(10))`` -> 665ns
48# ``tuple(map(lambda t: t, range(10)))`` -> 958ns
49#
50# All three have substantial variance.
51##
52# On PyPy, this is also the best option.
53##
54# PyPy 2.7.18-7.3.3
55# ``tuple([t fon t in range(10)])`` -> 128ns
56# ``tuple(t for t in range(10))`` -> 175ns
57# ``tuple(map(lambda t: t, range(10)))`` -> 153ns
58##
59# PyPy 3.7.9 7.3.3-beta
60# ``tuple([t fon t in range(10)])`` -> 82ns
61# ``tuple(t for t in range(10))`` -> 177ns
62# ``tuple(map(lambda t: t, range(10)))`` -> 168ns
63#
65class BaseAdapterRegistry(object):
66 """
67 A basic implementation of the data storage and algorithms required
68 for a :class:`zope.interface.interfaces.IAdapterRegistry`.
70 Subclasses can set the following attributes to control how the data
71 is stored; in particular, these hooks can be helpful for ZODB
72 persistence. They can be class attributes that are the named (or similar) type, or
73 they can be methods that act as a constructor for an object that behaves
74 like the types defined here; this object will not assume that they are type
75 objects, but subclasses are free to do so:
77 _sequenceType = list
78 This is the type used for our two mutable top-level "byorder" sequences.
79 Must support mutation operations like ``append()`` and ``del seq[index]``.
80 These are usually small (< 10). Although at least one of them is
81 accessed when performing lookups or queries on this object, the other
82 is untouched. In many common scenarios, both are only required when
83 mutating registrations and subscriptions (like what
84 :meth:`zope.interface.interfaces.IComponents.registerUtility` does).
85 This use pattern makes it an ideal candidate to be a
86 :class:`~persistent.list.PersistentList`.
87 _leafSequenceType = tuple
88 This is the type used for the leaf sequences of subscribers.
89 It could be set to a ``PersistentList`` to avoid many unnecessary data
90 loads when subscribers aren't being used. Mutation operations are directed
91 through :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`; if you use
92 a mutable type, you'll need to override those.
93 _mappingType = dict
94 This is the mutable mapping type used for the keyed mappings.
95 A :class:`~persistent.mapping.PersistentMapping`
96 could be used to help reduce the number of data loads when the registry is large
97 and parts of it are rarely used. Further reductions in data loads can come from
98 using a :class:`~BTrees.OOBTree.OOBTree`, but care is required
99 to be sure that all required/provided
100 values are fully ordered (e.g., no required or provided values that are classes
101 can be used).
102 _providedType = dict
103 This is the mutable mapping type used for the ``_provided`` mapping.
104 This is separate from the generic mapping type because the values
105 are always integers, so one might choose to use a more optimized data
106 structure such as a :class:`~BTrees.OIBTree.OIBTree`.
107 The same caveats regarding key types
108 apply as for ``_mappingType``.
110 It is possible to also set these on an instance, but because of the need to
111 potentially also override :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`,
112 this may be less useful in a persistent scenario; using a subclass is recommended.
114 .. versionchanged:: 5.3.0
115 Add support for customizing the way internal data
116 structures are created.
117 .. versionchanged:: 5.3.0
118 Add methods :meth:`rebuild`, :meth:`allRegistrations`
119 and :meth:`allSubscriptions`.
120 """
122 # List of methods copied from lookup sub-objects:
123 _delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter',
124 'adapter_hook', 'lookupAll', 'names',
125 'subscriptions', 'subscribers')
127 # All registries maintain a generation that can be used by verifying
128 # registries
129 _generation = 0
131 def __init__(self, bases=()):
133 # The comments here could be improved. Possibly this bit needs
134 # explaining in a separate document, as the comments here can
135 # be quite confusing. /regebro
137 # {order -> {required -> {provided -> {name -> value}}}}
138 # Here "order" is actually an index in a list, "required" and
139 # "provided" are interfaces, and "required" is really a nested
140 # key. So, for example:
141 # for order == 0 (that is, self._adapters[0]), we have:
142 # {provided -> {name -> value}}
143 # but for order == 2 (that is, self._adapters[2]), we have:
144 # {r1 -> {r2 -> {provided -> {name -> value}}}}
145 #
146 self._adapters = self._sequenceType()
148 # {order -> {required -> {provided -> {name -> [value]}}}}
149 # where the remarks about adapters above apply
150 self._subscribers = self._sequenceType()
152 # Set, with a reference count, keeping track of the interfaces
153 # for which we have provided components:
154 self._provided = self._providedType()
156 # Create ``_v_lookup`` object to perform lookup. We make this a
157 # separate object to to make it easier to implement just the
158 # lookup functionality in C. This object keeps track of cache
159 # invalidation data in two kinds of registries.
161 # Invalidating registries have caches that are invalidated
162 # when they or their base registies change. An invalidating
163 # registry can only have invalidating registries as bases.
164 # See LookupBaseFallback below for the pertinent logic.
166 # Verifying registies can't rely on getting invalidation messages,
167 # so have to check the generations of base registries to determine
168 # if their cache data are current. See VerifyingBasePy below
169 # for the pertinent object.
170 self._createLookup()
172 # Setting the bases causes the registries described above
173 # to be initialized (self._setBases -> self.changed ->
174 # self._v_lookup.changed).
176 self.__bases__ = bases
178 def _setBases(self, bases):
179 """
180 If subclasses need to track when ``__bases__`` changes, they
181 can override this method.
183 Subclasses must still call this method.
184 """
185 self.__dict__['__bases__'] = bases
186 self.ro = ro.ro(self)
187 self.changed(self)
189 __bases__ = property(lambda self: self.__dict__['__bases__'],
190 lambda self, bases: self._setBases(bases),
191 )
193 def _createLookup(self):
194 self._v_lookup = self.LookupClass(self)
195 for name in self._delegated:
196 self.__dict__[name] = getattr(self._v_lookup, name)
198 # Hooks for subclasses to define the types of objects used in
199 # our data structures.
200 # These have to be documented in the docstring, instead of local
201 # comments, because Sphinx autodoc ignores the comment and just writes
202 # "alias of list"
203 _sequenceType = list
204 _leafSequenceType = tuple
205 _mappingType = dict
206 _providedType = dict
208 def _addValueToLeaf(self, existing_leaf_sequence, new_item):
209 """
210 Add the value *new_item* to the *existing_leaf_sequence*, which may
211 be ``None``.
213 Subclasses that redefine `_leafSequenceType` should override this method.
215 :param existing_leaf_sequence:
216 If *existing_leaf_sequence* is not *None*, it will be an instance
217 of `_leafSequenceType`. (Unless the object has been unpickled
218 from an old pickle and the class definition has changed, in which case
219 it may be an instance of a previous definition, commonly a `tuple`.)
221 :return:
222 This method returns the new value to be stored. It may mutate the
223 sequence in place if it was not ``None`` and the type is mutable, but
224 it must also return it.
226 .. versionadded:: 5.3.0
227 """
228 if existing_leaf_sequence is None:
229 return (new_item,)
230 return existing_leaf_sequence + (new_item,)
232 def _removeValueFromLeaf(self, existing_leaf_sequence, to_remove):
233 """
234 Remove the item *to_remove* from the (non-``None``, non-empty)
235 *existing_leaf_sequence* and return the mutated sequence.
237 If there is more than one item that is equal to *to_remove*
238 they must all be removed.
240 Subclasses that redefine `_leafSequenceType` should override
241 this method. Note that they can call this method to help
242 in their implementation; this implementation will always
243 return a new tuple constructed by iterating across
244 the *existing_leaf_sequence* and omitting items equal to *to_remove*.
246 :param existing_leaf_sequence:
247 As for `_addValueToLeaf`, probably an instance of
248 `_leafSequenceType` but possibly an older type; never `None`.
249 :return:
250 A version of *existing_leaf_sequence* with all items equal to
251 *to_remove* removed. Must not return `None`. However,
252 returning an empty
253 object, even of another type such as the empty tuple, ``()`` is
254 explicitly allowed; such an object will never be stored.
256 .. versionadded:: 5.3.0
257 """
258 return tuple([v for v in existing_leaf_sequence if v != to_remove])
260 def changed(self, originally_changed):
261 self._generation += 1
262 self._v_lookup.changed(originally_changed)
264 def register(self, required, provided, name, value):
265 if not isinstance(name, STRING_TYPES):
266 raise ValueError('name is not a string')
267 if value is None:
268 self.unregister(required, provided, name, value)
269 return
271 required = tuple([_convert_None_to_Interface(r) for r in required])
272 name = _normalize_name(name)
273 order = len(required)
274 byorder = self._adapters
275 while len(byorder) <= order:
276 byorder.append(self._mappingType())
277 components = byorder[order]
278 key = required + (provided,)
280 for k in key:
281 d = components.get(k)
282 if d is None:
283 d = self._mappingType()
284 components[k] = d
285 components = d
287 if components.get(name) is value:
288 return
290 components[name] = value
292 n = self._provided.get(provided, 0) + 1
293 self._provided[provided] = n
294 if n == 1:
295 self._v_lookup.add_extendor(provided)
297 self.changed(self)
299 def _find_leaf(self, byorder, required, provided, name):
300 # Find the leaf value, if any, in the *byorder* list
301 # for the interface sequence *required* and the interface
302 # *provided*, given the already normalized *name*.
303 #
304 # If no such leaf value exists, returns ``None``
305 required = tuple([_convert_None_to_Interface(r) for r in required])
306 order = len(required)
307 if len(byorder) <= order:
308 return None
310 components = byorder[order]
311 key = required + (provided,)
313 for k in key:
314 d = components.get(k)
315 if d is None:
316 return None
317 components = d
319 return components.get(name)
321 def registered(self, required, provided, name=u''):
322 return self._find_leaf(
323 self._adapters,
324 required,
325 provided,
326 _normalize_name(name)
327 )
329 @classmethod
330 def _allKeys(cls, components, i, parent_k=()):
331 if i == 0:
332 for k, v in components.items():
333 yield parent_k + (k,), v
334 else:
335 for k, v in components.items():
336 new_parent_k = parent_k + (k,)
337 for x, y in cls._allKeys(v, i - 1, new_parent_k):
338 yield x, y
340 def _all_entries(self, byorder):
341 # Recurse through the mapping levels of the `byorder` sequence,
342 # reconstructing a flattened sequence of ``(required, provided, name, value)``
343 # tuples that can be used to reconstruct the sequence with the appropriate
344 # registration methods.
345 #
346 # Locally reference the `byorder` data; it might be replaced while
347 # this method is running (see ``rebuild``).
348 for i, components in enumerate(byorder):
349 # We will have *i* levels of dictionaries to go before
350 # we get to the leaf.
351 for key, value in self._allKeys(components, i + 1):
352 assert len(key) == i + 2
353 required = key[:i]
354 provided = key[-2]
355 name = key[-1]
356 yield (required, provided, name, value)
358 def allRegistrations(self):
359 """
360 Yields tuples ``(required, provided, name, value)`` for all
361 the registrations that this object holds.
363 These tuples could be passed as the arguments to the
364 :meth:`register` method on another adapter registry to
365 duplicate the registrations this object holds.
367 .. versionadded:: 5.3.0
368 """
369 for t in self._all_entries(self._adapters):
370 yield t
372 def unregister(self, required, provided, name, value=None):
373 required = tuple([_convert_None_to_Interface(r) for r in required])
374 order = len(required)
375 byorder = self._adapters
376 if order >= len(byorder):
377 return False
378 components = byorder[order]
379 key = required + (provided,)
381 # Keep track of how we got to `components`:
382 lookups = []
383 for k in key:
384 d = components.get(k)
385 if d is None:
386 return
387 lookups.append((components, k))
388 components = d
390 old = components.get(name)
391 if old is None:
392 return
393 if (value is not None) and (old is not value):
394 return
396 del components[name]
397 if not components:
398 # Clean out empty containers, since we don't want our keys
399 # to reference global objects (interfaces) unnecessarily.
400 # This is often a problem when an interface is slated for
401 # removal; a hold-over entry in the registry can make it
402 # difficult to remove such interfaces.
403 for comp, k in reversed(lookups):
404 d = comp[k]
405 if d:
406 break
407 else:
408 del comp[k]
409 while byorder and not byorder[-1]:
410 del byorder[-1]
411 n = self._provided[provided] - 1
412 if n == 0:
413 del self._provided[provided]
414 self._v_lookup.remove_extendor(provided)
415 else:
416 self._provided[provided] = n
418 self.changed(self)
420 def subscribe(self, required, provided, value):
421 required = tuple([_convert_None_to_Interface(r) for r in required])
422 name = u''
423 order = len(required)
424 byorder = self._subscribers
425 while len(byorder) <= order:
426 byorder.append(self._mappingType())
427 components = byorder[order]
428 key = required + (provided,)
430 for k in key:
431 d = components.get(k)
432 if d is None:
433 d = self._mappingType()
434 components[k] = d
435 components = d
437 components[name] = self._addValueToLeaf(components.get(name), value)
439 if provided is not None:
440 n = self._provided.get(provided, 0) + 1
441 self._provided[provided] = n
442 if n == 1:
443 self._v_lookup.add_extendor(provided)
445 self.changed(self)
447 def subscribed(self, required, provided, subscriber):
448 subscribers = self._find_leaf(
449 self._subscribers,
450 required,
451 provided,
452 u''
453 ) or ()
454 return subscriber if subscriber in subscribers else None
456 def allSubscriptions(self):
457 """
458 Yields tuples ``(required, provided, value)`` for all the
459 subscribers that this object holds.
461 These tuples could be passed as the arguments to the
462 :meth:`subscribe` method on another adapter registry to
463 duplicate the registrations this object holds.
465 .. versionadded:: 5.3.0
466 """
467 for required, provided, _name, value in self._all_entries(self._subscribers):
468 for v in value:
469 yield (required, provided, v)
471 def unsubscribe(self, required, provided, value=None):
472 required = tuple([_convert_None_to_Interface(r) for r in required])
473 order = len(required)
474 byorder = self._subscribers
475 if order >= len(byorder):
476 return
477 components = byorder[order]
478 key = required + (provided,)
480 # Keep track of how we got to `components`:
481 lookups = []
482 for k in key:
483 d = components.get(k)
484 if d is None:
485 return
486 lookups.append((components, k))
487 components = d
489 old = components.get(u'')
490 if not old:
491 # this is belt-and-suspenders against the failure of cleanup below
492 return # pragma: no cover
493 len_old = len(old)
494 if value is None:
495 # Removing everything; note that the type of ``new`` won't
496 # necessarily match the ``_leafSequenceType``, but that's
497 # OK because we're about to delete the entire entry
498 # anyway.
499 new = ()
500 else:
501 new = self._removeValueFromLeaf(old, value)
502 # ``new`` may be the same object as ``old``, just mutated in place,
503 # so we cannot compare it to ``old`` to check for changes. Remove
504 # our reference to it now to avoid trying to do so below.
505 del old
507 if len(new) == len_old:
508 # No changes, so nothing could have been removed.
509 return
511 if new:
512 components[u''] = new
513 else:
514 # Instead of setting components[u''] = new, we clean out
515 # empty containers, since we don't want our keys to
516 # reference global objects (interfaces) unnecessarily. This
517 # is often a problem when an interface is slated for
518 # removal; a hold-over entry in the registry can make it
519 # difficult to remove such interfaces.
520 del components[u'']
521 for comp, k in reversed(lookups):
522 d = comp[k]
523 if d:
524 break
525 else:
526 del comp[k]
527 while byorder and not byorder[-1]:
528 del byorder[-1]
530 if provided is not None:
531 n = self._provided[provided] + len(new) - len_old
532 if n == 0:
533 del self._provided[provided]
534 self._v_lookup.remove_extendor(provided)
535 else:
536 self._provided[provided] = n
538 self.changed(self)
540 def rebuild(self):
541 """
542 Rebuild (and replace) all the internal data structures of this
543 object.
545 This is useful, especially for persistent implementations, if
546 you suspect an issue with reference counts keeping interfaces
547 alive even though they are no longer used.
549 It is also useful if you or a subclass change the data types
550 (``_mappingType`` and friends) that are to be used.
552 This method replaces all internal data structures with new objects;
553 it specifically does not re-use any storage.
555 .. versionadded:: 5.3.0
556 """
558 # Grab the iterators, we're about to discard their data.
559 registrations = self.allRegistrations()
560 subscriptions = self.allSubscriptions()
562 def buffer(it):
563 # The generator doesn't actually start running until we
564 # ask for its next(), by which time the attributes will change
565 # unless we do so before calling __init__.
566 try:
567 first = next(it)
568 except StopIteration:
569 return iter(())
571 return itertools.chain((first,), it)
573 registrations = buffer(registrations)
574 subscriptions = buffer(subscriptions)
577 # Replace the base data structures as well as _v_lookup.
578 self.__init__(self.__bases__)
579 # Re-register everything previously registered and subscribed.
580 #
581 # XXX: This is going to call ``self.changed()`` a lot, all of
582 # which is unnecessary (because ``self.__init__`` just
583 # re-created those dependent objects and also called
584 # ``self.changed()``). Is this a bottleneck that needs fixed?
585 # (We could do ``self.changed = lambda _: None`` before
586 # beginning and remove it after to disable the presumably expensive
587 # part of passing that notification to the change of objects.)
588 for args in registrations:
589 self.register(*args)
590 for args in subscriptions:
591 self.subscribe(*args)
593 # XXX hack to fake out twisted's use of a private api. We need to get them
594 # to use the new registed method.
595 def get(self, _): # pragma: no cover
596 class XXXTwistedFakeOut:
597 selfImplied = {}
598 return XXXTwistedFakeOut
601_not_in_mapping = object()
603@_use_c_impl
604class LookupBase(object):
606 def __init__(self):
607 self._cache = {}
608 self._mcache = {}
609 self._scache = {}
611 def changed(self, ignored=None):
612 self._cache.clear()
613 self._mcache.clear()
614 self._scache.clear()
616 def _getcache(self, provided, name):
617 cache = self._cache.get(provided)
618 if cache is None:
619 cache = {}
620 self._cache[provided] = cache
621 if name:
622 c = cache.get(name)
623 if c is None:
624 c = {}
625 cache[name] = c
626 cache = c
627 return cache
629 def lookup(self, required, provided, name=u'', default=None):
630 if not isinstance(name, STRING_TYPES):
631 raise ValueError('name is not a string')
632 cache = self._getcache(provided, name)
633 required = tuple(required)
634 if len(required) == 1:
635 result = cache.get(required[0], _not_in_mapping)
636 else:
637 result = cache.get(tuple(required), _not_in_mapping)
639 if result is _not_in_mapping:
640 result = self._uncached_lookup(required, provided, name)
641 if len(required) == 1:
642 cache[required[0]] = result
643 else:
644 cache[tuple(required)] = result
646 if result is None:
647 return default
649 return result
651 def lookup1(self, required, provided, name=u'', default=None):
652 if not isinstance(name, STRING_TYPES):
653 raise ValueError('name is not a string')
654 cache = self._getcache(provided, name)
655 result = cache.get(required, _not_in_mapping)
656 if result is _not_in_mapping:
657 return self.lookup((required, ), provided, name, default)
659 if result is None:
660 return default
662 return result
664 def queryAdapter(self, object, provided, name=u'', default=None):
665 return self.adapter_hook(provided, object, name, default)
667 def adapter_hook(self, provided, object, name=u'', default=None):
668 if not isinstance(name, STRING_TYPES):
669 raise ValueError('name is not a string')
670 required = providedBy(object)
671 cache = self._getcache(provided, name)
672 factory = cache.get(required, _not_in_mapping)
673 if factory is _not_in_mapping:
674 factory = self.lookup((required, ), provided, name)
676 if factory is not None:
677 if isinstance(object, super):
678 object = object.__self__
679 result = factory(object)
680 if result is not None:
681 return result
683 return default
685 def lookupAll(self, required, provided):
686 cache = self._mcache.get(provided)
687 if cache is None:
688 cache = {}
689 self._mcache[provided] = cache
691 required = tuple(required)
692 result = cache.get(required, _not_in_mapping)
693 if result is _not_in_mapping:
694 result = self._uncached_lookupAll(required, provided)
695 cache[required] = result
697 return result
700 def subscriptions(self, required, provided):
701 cache = self._scache.get(provided)
702 if cache is None:
703 cache = {}
704 self._scache[provided] = cache
706 required = tuple(required)
707 result = cache.get(required, _not_in_mapping)
708 if result is _not_in_mapping:
709 result = self._uncached_subscriptions(required, provided)
710 cache[required] = result
712 return result
715@_use_c_impl
716class VerifyingBase(LookupBaseFallback):
717 # Mixin for lookups against registries which "chain" upwards, and
718 # whose lookups invalidate their own caches whenever a parent registry
719 # bumps its own '_generation' counter. E.g., used by
720 # zope.component.persistentregistry
722 def changed(self, originally_changed):
723 LookupBaseFallback.changed(self, originally_changed)
724 self._verify_ro = self._registry.ro[1:]
725 self._verify_generations = [r._generation for r in self._verify_ro]
727 def _verify(self):
728 if ([r._generation for r in self._verify_ro]
729 != self._verify_generations):
730 self.changed(None)
732 def _getcache(self, provided, name):
733 self._verify()
734 return LookupBaseFallback._getcache(self, provided, name)
736 def lookupAll(self, required, provided):
737 self._verify()
738 return LookupBaseFallback.lookupAll(self, required, provided)
740 def subscriptions(self, required, provided):
741 self._verify()
742 return LookupBaseFallback.subscriptions(self, required, provided)
745class AdapterLookupBase(object):
747 def __init__(self, registry):
748 self._registry = registry
749 self._required = {}
750 self.init_extendors()
751 super(AdapterLookupBase, self).__init__()
753 def changed(self, ignored=None):
754 super(AdapterLookupBase, self).changed(None)
755 for r in self._required.keys():
756 r = r()
757 if r is not None:
758 r.unsubscribe(self)
759 self._required.clear()
762 # Extendors
763 # ---------
765 # When given an target interface for an adapter lookup, we need to consider
766 # adapters for interfaces that extend the target interface. This is
767 # what the extendors dictionary is about. It tells us all of the
768 # interfaces that extend an interface for which there are adapters
769 # registered.
771 # We could separate this by order and name, thus reducing the
772 # number of provided interfaces to search at run time. The tradeoff,
773 # however, is that we have to store more information. For example,
774 # if the same interface is provided for multiple names and if the
775 # interface extends many interfaces, we'll have to keep track of
776 # a fair bit of information for each name. It's better to
777 # be space efficient here and be time efficient in the cache
778 # implementation.
780 # TODO: add invalidation when a provided interface changes, in case
781 # the interface's __iro__ has changed. This is unlikely enough that
782 # we'll take our chances for now.
784 def init_extendors(self):
785 self._extendors = {}
786 for p in self._registry._provided:
787 self.add_extendor(p)
789 def add_extendor(self, provided):
790 _extendors = self._extendors
791 for i in provided.__iro__:
792 extendors = _extendors.get(i, ())
793 _extendors[i] = (
794 [e for e in extendors if provided.isOrExtends(e)]
795 +
796 [provided]
797 +
798 [e for e in extendors if not provided.isOrExtends(e)]
799 )
801 def remove_extendor(self, provided):
802 _extendors = self._extendors
803 for i in provided.__iro__:
804 _extendors[i] = [e for e in _extendors.get(i, ())
805 if e != provided]
808 def _subscribe(self, *required):
809 _refs = self._required
810 for r in required:
811 ref = r.weakref()
812 if ref not in _refs:
813 r.subscribe(self)
814 _refs[ref] = 1
816 def _uncached_lookup(self, required, provided, name=u''):
817 required = tuple(required)
818 result = None
819 order = len(required)
820 for registry in self._registry.ro:
821 byorder = registry._adapters
822 if order >= len(byorder):
823 continue
825 extendors = registry._v_lookup._extendors.get(provided)
826 if not extendors:
827 continue
829 components = byorder[order]
830 result = _lookup(components, required, extendors, name, 0,
831 order)
832 if result is not None:
833 break
835 self._subscribe(*required)
837 return result
839 def queryMultiAdapter(self, objects, provided, name=u'', default=None):
840 factory = self.lookup([providedBy(o) for o in objects], provided, name)
841 if factory is None:
842 return default
844 result = factory(*[o.__self__ if isinstance(o, super) else o for o in objects])
845 if result is None:
846 return default
848 return result
850 def _uncached_lookupAll(self, required, provided):
851 required = tuple(required)
852 order = len(required)
853 result = {}
854 for registry in reversed(self._registry.ro):
855 byorder = registry._adapters
856 if order >= len(byorder):
857 continue
858 extendors = registry._v_lookup._extendors.get(provided)
859 if not extendors:
860 continue
861 components = byorder[order]
862 _lookupAll(components, required, extendors, result, 0, order)
864 self._subscribe(*required)
866 return tuple(result.items())
868 def names(self, required, provided):
869 return [c[0] for c in self.lookupAll(required, provided)]
871 def _uncached_subscriptions(self, required, provided):
872 required = tuple(required)
873 order = len(required)
874 result = []
875 for registry in reversed(self._registry.ro):
876 byorder = registry._subscribers
877 if order >= len(byorder):
878 continue
880 if provided is None:
881 extendors = (provided, )
882 else:
883 extendors = registry._v_lookup._extendors.get(provided)
884 if extendors is None:
885 continue
887 _subscriptions(byorder[order], required, extendors, u'',
888 result, 0, order)
890 self._subscribe(*required)
892 return result
894 def subscribers(self, objects, provided):
895 subscriptions = self.subscriptions([providedBy(o) for o in objects], provided)
896 if provided is None:
897 result = ()
898 for subscription in subscriptions:
899 subscription(*objects)
900 else:
901 result = []
902 for subscription in subscriptions:
903 subscriber = subscription(*objects)
904 if subscriber is not None:
905 result.append(subscriber)
906 return result
908class AdapterLookup(AdapterLookupBase, LookupBase):
909 pass
911@implementer(IAdapterRegistry)
912class AdapterRegistry(BaseAdapterRegistry):
913 """
914 A full implementation of ``IAdapterRegistry`` that adds support for
915 sub-registries.
916 """
918 LookupClass = AdapterLookup
920 def __init__(self, bases=()):
921 # AdapterRegisties are invalidating registries, so
922 # we need to keep track of our invalidating subregistries.
923 self._v_subregistries = weakref.WeakKeyDictionary()
925 super(AdapterRegistry, self).__init__(bases)
927 def _addSubregistry(self, r):
928 self._v_subregistries[r] = 1
930 def _removeSubregistry(self, r):
931 if r in self._v_subregistries:
932 del self._v_subregistries[r]
934 def _setBases(self, bases):
935 old = self.__dict__.get('__bases__', ())
936 for r in old:
937 if r not in bases:
938 r._removeSubregistry(self)
939 for r in bases:
940 if r not in old:
941 r._addSubregistry(self)
943 super(AdapterRegistry, self)._setBases(bases)
945 def changed(self, originally_changed):
946 super(AdapterRegistry, self).changed(originally_changed)
948 for sub in self._v_subregistries.keys():
949 sub.changed(originally_changed)
952class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase):
953 pass
955@implementer(IAdapterRegistry)
956class VerifyingAdapterRegistry(BaseAdapterRegistry):
957 """
958 The most commonly-used adapter registry.
959 """
961 LookupClass = VerifyingAdapterLookup
963def _convert_None_to_Interface(x):
964 if x is None:
965 return Interface
966 else:
967 return x
969def _lookup(components, specs, provided, name, i, l):
970 # this function is called very often.
971 # The components.get in loops is executed 100 of 1000s times.
972 # by loading get into a local variable the bytecode
973 # "LOAD_FAST 0 (components)" in the loop can be eliminated.
974 components_get = components.get
975 if i < l:
976 for spec in specs[i].__sro__:
977 comps = components_get(spec)
978 if comps:
979 r = _lookup(comps, specs, provided, name, i+1, l)
980 if r is not None:
981 return r
982 else:
983 for iface in provided:
984 comps = components_get(iface)
985 if comps:
986 r = comps.get(name)
987 if r is not None:
988 return r
990 return None
992def _lookupAll(components, specs, provided, result, i, l):
993 components_get = components.get # see _lookup above
994 if i < l:
995 for spec in reversed(specs[i].__sro__):
996 comps = components_get(spec)
997 if comps:
998 _lookupAll(comps, specs, provided, result, i+1, l)
999 else:
1000 for iface in reversed(provided):
1001 comps = components_get(iface)
1002 if comps:
1003 result.update(comps)
1005def _subscriptions(components, specs, provided, name, result, i, l):
1006 components_get = components.get # see _lookup above
1007 if i < l:
1008 for spec in reversed(specs[i].__sro__):
1009 comps = components_get(spec)
1010 if comps:
1011 _subscriptions(comps, specs, provided, name, result, i+1, l)
1012 else:
1013 for iface in reversed(provided):
1014 comps = components_get(iface)
1015 if comps:
1016 comps = comps.get(name)
1017 if comps:
1018 result.extend(comps)