comparison mercurial/thirdparty/zope/interface/adapter.py @ 37176:943d77fc07a3

thirdparty: vendor zope.interface 4.4.3 I've been trying to formalize interfaces for various components of Mercurial. So far, we've been using the "abc" package. This package is "good enough" for a lot of tasks. But it quickly falls over. For example, if you declare an @abc.abstractproperty, you must implement that attribute with a @property or the class compile time checking performed by abc will complain. This often forces you to implement dumb @property wrappers to return a _ prefixed attribute of the sane name. That's ugly. I've also wanted to implement automated checking that classes conform to various interfaces and don't expose other "public" attributes. After doing a bit of research and asking around, the general consensus seems to be that zope.interface is the best package for doing interface-based programming in Python. It has built-in support for verifying classes and objects conform to interfaces. It allows an interface's properties to be defined during __init__. There's even an "adapter registry" that allow you to register interfaces and look up which classes implement them. That could potentially be useful for places where our custom registry.py modules currently facilitates central registrations, but at a type level. Imagine extensions providing alternate implementations of things like the local repository interface to allow opening repositories with custom requirements. Anyway, this commit vendors zope.interface 4.4.3. The contents of the source tarball have been copied into mercurial/thirdparty/zope/ without modifications. Test modules have been removed because they are not interesting to us. The LICENSE.txt file has been copied so it lives next to the source. The Python modules don't use relative imports. zope/__init__.py defines a namespace package. So we'll need to modify the source code before this package is usable inside Mercurial. This will be done in subsequent commits. # no-check-commit for various style failures Differential Revision: https://phab.mercurial-scm.org/D2928
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 21 Mar 2018 19:48:50 -0700
parents
children 68ee61822182
comparison
equal deleted inserted replaced
37175:fbe34945220d 37176:943d77fc07a3
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 """
16 import weakref
17
18 from zope.interface import implementer
19 from zope.interface import providedBy
20 from zope.interface import Interface
21 from zope.interface import ro
22 from zope.interface.interfaces import IAdapterRegistry
23
24 from zope.interface._compat import _normalize_name
25 from zope.interface._compat import STRING_TYPES
26
27 _BLANK = u''
28
29 class BaseAdapterRegistry(object):
30
31 # List of methods copied from lookup sub-objects:
32 _delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter',
33 'adapter_hook', 'lookupAll', 'names',
34 'subscriptions', 'subscribers')
35
36 # All registries maintain a generation that can be used by verifying
37 # registries
38 _generation = 0
39
40 def __init__(self, bases=()):
41
42 # The comments here could be improved. Possibly this bit needs
43 # explaining in a separate document, as the comments here can
44 # be quite confusing. /regebro
45
46 # {order -> {required -> {provided -> {name -> value}}}}
47 # Here "order" is actually an index in a list, "required" and
48 # "provided" are interfaces, and "required" is really a nested
49 # key. So, for example:
50 # for order == 0 (that is, self._adapters[0]), we have:
51 # {provided -> {name -> value}}
52 # but for order == 2 (that is, self._adapters[2]), we have:
53 # {r1 -> {r2 -> {provided -> {name -> value}}}}
54 #
55 self._adapters = []
56
57 # {order -> {required -> {provided -> {name -> [value]}}}}
58 # where the remarks about adapters above apply
59 self._subscribers = []
60
61 # Set, with a reference count, keeping track of the interfaces
62 # for which we have provided components:
63 self._provided = {}
64
65 # Create ``_v_lookup`` object to perform lookup. We make this a
66 # separate object to to make it easier to implement just the
67 # lookup functionality in C. This object keeps track of cache
68 # invalidation data in two kinds of registries.
69
70 # Invalidating registries have caches that are invalidated
71 # when they or their base registies change. An invalidating
72 # registry can only have invalidating registries as bases.
73 # See LookupBaseFallback below for the pertinent logic.
74
75 # Verifying registies can't rely on getting invalidation messages,
76 # so have to check the generations of base registries to determine
77 # if their cache data are current. See VerifyingBasePy below
78 # for the pertinent object.
79 self._createLookup()
80
81 # Setting the bases causes the registries described above
82 # to be initialized (self._setBases -> self.changed ->
83 # self._v_lookup.changed).
84
85 self.__bases__ = bases
86
87 def _setBases(self, bases):
88 self.__dict__['__bases__'] = bases
89 self.ro = ro.ro(self)
90 self.changed(self)
91
92 __bases__ = property(lambda self: self.__dict__['__bases__'],
93 lambda self, bases: self._setBases(bases),
94 )
95
96 def _createLookup(self):
97 self._v_lookup = self.LookupClass(self)
98 for name in self._delegated:
99 self.__dict__[name] = getattr(self._v_lookup, name)
100
101 def changed(self, originally_changed):
102 self._generation += 1
103 self._v_lookup.changed(originally_changed)
104
105 def register(self, required, provided, name, value):
106 if not isinstance(name, STRING_TYPES):
107 raise ValueError('name is not a string')
108 if value is None:
109 self.unregister(required, provided, name, value)
110 return
111
112 required = tuple(map(_convert_None_to_Interface, required))
113 name = _normalize_name(name)
114 order = len(required)
115 byorder = self._adapters
116 while len(byorder) <= order:
117 byorder.append({})
118 components = byorder[order]
119 key = required + (provided,)
120
121 for k in key:
122 d = components.get(k)
123 if d is None:
124 d = {}
125 components[k] = d
126 components = d
127
128 if components.get(name) is value:
129 return
130
131 components[name] = value
132
133 n = self._provided.get(provided, 0) + 1
134 self._provided[provided] = n
135 if n == 1:
136 self._v_lookup.add_extendor(provided)
137
138 self.changed(self)
139
140 def registered(self, required, provided, name=_BLANK):
141 required = tuple(map(_convert_None_to_Interface, required))
142 name = _normalize_name(name)
143 order = len(required)
144 byorder = self._adapters
145 if len(byorder) <= order:
146 return None
147
148 components = byorder[order]
149 key = required + (provided,)
150
151 for k in key:
152 d = components.get(k)
153 if d is None:
154 return None
155 components = d
156
157 return components.get(name)
158
159 def unregister(self, required, provided, name, value=None):
160 required = tuple(map(_convert_None_to_Interface, required))
161 order = len(required)
162 byorder = self._adapters
163 if order >= len(byorder):
164 return False
165 components = byorder[order]
166 key = required + (provided,)
167
168 # Keep track of how we got to `components`:
169 lookups = []
170 for k in key:
171 d = components.get(k)
172 if d is None:
173 return
174 lookups.append((components, k))
175 components = d
176
177 old = components.get(name)
178 if old is None:
179 return
180 if (value is not None) and (old is not value):
181 return
182
183 del components[name]
184 if not components:
185 # Clean out empty containers, since we don't want our keys
186 # to reference global objects (interfaces) unnecessarily.
187 # This is often a problem when an interface is slated for
188 # removal; a hold-over entry in the registry can make it
189 # difficult to remove such interfaces.
190 for comp, k in reversed(lookups):
191 d = comp[k]
192 if d:
193 break
194 else:
195 del comp[k]
196 while byorder and not byorder[-1]:
197 del byorder[-1]
198 n = self._provided[provided] - 1
199 if n == 0:
200 del self._provided[provided]
201 self._v_lookup.remove_extendor(provided)
202 else:
203 self._provided[provided] = n
204
205 self.changed(self)
206
207 def subscribe(self, required, provided, value):
208 required = tuple(map(_convert_None_to_Interface, required))
209 name = _BLANK
210 order = len(required)
211 byorder = self._subscribers
212 while len(byorder) <= order:
213 byorder.append({})
214 components = byorder[order]
215 key = required + (provided,)
216
217 for k in key:
218 d = components.get(k)
219 if d is None:
220 d = {}
221 components[k] = d
222 components = d
223
224 components[name] = components.get(name, ()) + (value, )
225
226 if provided is not None:
227 n = self._provided.get(provided, 0) + 1
228 self._provided[provided] = n
229 if n == 1:
230 self._v_lookup.add_extendor(provided)
231
232 self.changed(self)
233
234 def unsubscribe(self, required, provided, value=None):
235 required = tuple(map(_convert_None_to_Interface, required))
236 order = len(required)
237 byorder = self._subscribers
238 if order >= len(byorder):
239 return
240 components = byorder[order]
241 key = required + (provided,)
242
243 # Keep track of how we got to `components`:
244 lookups = []
245 for k in key:
246 d = components.get(k)
247 if d is None:
248 return
249 lookups.append((components, k))
250 components = d
251
252 old = components.get(_BLANK)
253 if not old:
254 # this is belt-and-suspenders against the failure of cleanup below
255 return # pragma: no cover
256
257 if value is None:
258 new = ()
259 else:
260 new = tuple([v for v in old if v is not value])
261
262 if new == old:
263 return
264
265 if new:
266 components[_BLANK] = new
267 else:
268 # Instead of setting components[_BLANK] = new, we clean out
269 # empty containers, since we don't want our keys to
270 # reference global objects (interfaces) unnecessarily. This
271 # is often a problem when an interface is slated for
272 # removal; a hold-over entry in the registry can make it
273 # difficult to remove such interfaces.
274 del components[_BLANK]
275 for comp, k in reversed(lookups):
276 d = comp[k]
277 if d:
278 break
279 else:
280 del comp[k]
281 while byorder and not byorder[-1]:
282 del byorder[-1]
283
284 if provided is not None:
285 n = self._provided[provided] + len(new) - len(old)
286 if n == 0:
287 del self._provided[provided]
288 self._v_lookup.remove_extendor(provided)
289
290 self.changed(self)
291
292 # XXX hack to fake out twisted's use of a private api. We need to get them
293 # to use the new registed method.
294 def get(self, _): # pragma: no cover
295 class XXXTwistedFakeOut:
296 selfImplied = {}
297 return XXXTwistedFakeOut
298
299
300 _not_in_mapping = object()
301 class LookupBaseFallback(object):
302
303 def __init__(self):
304 self._cache = {}
305 self._mcache = {}
306 self._scache = {}
307
308 def changed(self, ignored=None):
309 self._cache.clear()
310 self._mcache.clear()
311 self._scache.clear()
312
313 def _getcache(self, provided, name):
314 cache = self._cache.get(provided)
315 if cache is None:
316 cache = {}
317 self._cache[provided] = cache
318 if name:
319 c = cache.get(name)
320 if c is None:
321 c = {}
322 cache[name] = c
323 cache = c
324 return cache
325
326 def lookup(self, required, provided, name=_BLANK, default=None):
327 if not isinstance(name, STRING_TYPES):
328 raise ValueError('name is not a string')
329 cache = self._getcache(provided, name)
330 required = tuple(required)
331 if len(required) == 1:
332 result = cache.get(required[0], _not_in_mapping)
333 else:
334 result = cache.get(tuple(required), _not_in_mapping)
335
336 if result is _not_in_mapping:
337 result = self._uncached_lookup(required, provided, name)
338 if len(required) == 1:
339 cache[required[0]] = result
340 else:
341 cache[tuple(required)] = result
342
343 if result is None:
344 return default
345
346 return result
347
348 def lookup1(self, required, provided, name=_BLANK, default=None):
349 if not isinstance(name, STRING_TYPES):
350 raise ValueError('name is not a string')
351 cache = self._getcache(provided, name)
352 result = cache.get(required, _not_in_mapping)
353 if result is _not_in_mapping:
354 return self.lookup((required, ), provided, name, default)
355
356 if result is None:
357 return default
358
359 return result
360
361 def queryAdapter(self, object, provided, name=_BLANK, default=None):
362 return self.adapter_hook(provided, object, name, default)
363
364 def adapter_hook(self, provided, object, name=_BLANK, default=None):
365 if not isinstance(name, STRING_TYPES):
366 raise ValueError('name is not a string')
367 required = providedBy(object)
368 cache = self._getcache(provided, name)
369 factory = cache.get(required, _not_in_mapping)
370 if factory is _not_in_mapping:
371 factory = self.lookup((required, ), provided, name)
372
373 if factory is not None:
374 result = factory(object)
375 if result is not None:
376 return result
377
378 return default
379
380 def lookupAll(self, required, provided):
381 cache = self._mcache.get(provided)
382 if cache is None:
383 cache = {}
384 self._mcache[provided] = cache
385
386 required = tuple(required)
387 result = cache.get(required, _not_in_mapping)
388 if result is _not_in_mapping:
389 result = self._uncached_lookupAll(required, provided)
390 cache[required] = result
391
392 return result
393
394
395 def subscriptions(self, required, provided):
396 cache = self._scache.get(provided)
397 if cache is None:
398 cache = {}
399 self._scache[provided] = cache
400
401 required = tuple(required)
402 result = cache.get(required, _not_in_mapping)
403 if result is _not_in_mapping:
404 result = self._uncached_subscriptions(required, provided)
405 cache[required] = result
406
407 return result
408
409 LookupBasePy = LookupBaseFallback # BBB
410
411 try:
412 from zope.interface._zope_interface_coptimizations import LookupBase
413 except ImportError:
414 LookupBase = LookupBaseFallback
415
416
417 class VerifyingBaseFallback(LookupBaseFallback):
418 # Mixin for lookups against registries which "chain" upwards, and
419 # whose lookups invalidate their own caches whenever a parent registry
420 # bumps its own '_generation' counter. E.g., used by
421 # zope.component.persistentregistry
422
423 def changed(self, originally_changed):
424 LookupBaseFallback.changed(self, originally_changed)
425 self._verify_ro = self._registry.ro[1:]
426 self._verify_generations = [r._generation for r in self._verify_ro]
427
428 def _verify(self):
429 if ([r._generation for r in self._verify_ro]
430 != self._verify_generations):
431 self.changed(None)
432
433 def _getcache(self, provided, name):
434 self._verify()
435 return LookupBaseFallback._getcache(self, provided, name)
436
437 def lookupAll(self, required, provided):
438 self._verify()
439 return LookupBaseFallback.lookupAll(self, required, provided)
440
441 def subscriptions(self, required, provided):
442 self._verify()
443 return LookupBaseFallback.subscriptions(self, required, provided)
444
445 VerifyingBasePy = VerifyingBaseFallback #BBB
446
447 try:
448 from zope.interface._zope_interface_coptimizations import VerifyingBase
449 except ImportError:
450 VerifyingBase = VerifyingBaseFallback
451
452
453 class AdapterLookupBase(object):
454
455 def __init__(self, registry):
456 self._registry = registry
457 self._required = {}
458 self.init_extendors()
459 super(AdapterLookupBase, self).__init__()
460
461 def changed(self, ignored=None):
462 super(AdapterLookupBase, self).changed(None)
463 for r in self._required.keys():
464 r = r()
465 if r is not None:
466 r.unsubscribe(self)
467 self._required.clear()
468
469
470 # Extendors
471 # ---------
472
473 # When given an target interface for an adapter lookup, we need to consider
474 # adapters for interfaces that extend the target interface. This is
475 # what the extendors dictionary is about. It tells us all of the
476 # interfaces that extend an interface for which there are adapters
477 # registered.
478
479 # We could separate this by order and name, thus reducing the
480 # number of provided interfaces to search at run time. The tradeoff,
481 # however, is that we have to store more information. For example,
482 # if the same interface is provided for multiple names and if the
483 # interface extends many interfaces, we'll have to keep track of
484 # a fair bit of information for each name. It's better to
485 # be space efficient here and be time efficient in the cache
486 # implementation.
487
488 # TODO: add invalidation when a provided interface changes, in case
489 # the interface's __iro__ has changed. This is unlikely enough that
490 # we'll take our chances for now.
491
492 def init_extendors(self):
493 self._extendors = {}
494 for p in self._registry._provided:
495 self.add_extendor(p)
496
497 def add_extendor(self, provided):
498 _extendors = self._extendors
499 for i in provided.__iro__:
500 extendors = _extendors.get(i, ())
501 _extendors[i] = (
502 [e for e in extendors if provided.isOrExtends(e)]
503 +
504 [provided]
505 +
506 [e for e in extendors if not provided.isOrExtends(e)]
507 )
508
509 def remove_extendor(self, provided):
510 _extendors = self._extendors
511 for i in provided.__iro__:
512 _extendors[i] = [e for e in _extendors.get(i, ())
513 if e != provided]
514
515
516 def _subscribe(self, *required):
517 _refs = self._required
518 for r in required:
519 ref = r.weakref()
520 if ref not in _refs:
521 r.subscribe(self)
522 _refs[ref] = 1
523
524 def _uncached_lookup(self, required, provided, name=_BLANK):
525 required = tuple(required)
526 result = None
527 order = len(required)
528 for registry in self._registry.ro:
529 byorder = registry._adapters
530 if order >= len(byorder):
531 continue
532
533 extendors = registry._v_lookup._extendors.get(provided)
534 if not extendors:
535 continue
536
537 components = byorder[order]
538 result = _lookup(components, required, extendors, name, 0,
539 order)
540 if result is not None:
541 break
542
543 self._subscribe(*required)
544
545 return result
546
547 def queryMultiAdapter(self, objects, provided, name=_BLANK, default=None):
548 factory = self.lookup(map(providedBy, objects), provided, name)
549 if factory is None:
550 return default
551
552 result = factory(*objects)
553 if result is None:
554 return default
555
556 return result
557
558 def _uncached_lookupAll(self, required, provided):
559 required = tuple(required)
560 order = len(required)
561 result = {}
562 for registry in reversed(self._registry.ro):
563 byorder = registry._adapters
564 if order >= len(byorder):
565 continue
566 extendors = registry._v_lookup._extendors.get(provided)
567 if not extendors:
568 continue
569 components = byorder[order]
570 _lookupAll(components, required, extendors, result, 0, order)
571
572 self._subscribe(*required)
573
574 return tuple(result.items())
575
576 def names(self, required, provided):
577 return [c[0] for c in self.lookupAll(required, provided)]
578
579 def _uncached_subscriptions(self, required, provided):
580 required = tuple(required)
581 order = len(required)
582 result = []
583 for registry in reversed(self._registry.ro):
584 byorder = registry._subscribers
585 if order >= len(byorder):
586 continue
587
588 if provided is None:
589 extendors = (provided, )
590 else:
591 extendors = registry._v_lookup._extendors.get(provided)
592 if extendors is None:
593 continue
594
595 _subscriptions(byorder[order], required, extendors, _BLANK,
596 result, 0, order)
597
598 self._subscribe(*required)
599
600 return result
601
602 def subscribers(self, objects, provided):
603 subscriptions = self.subscriptions(map(providedBy, objects), provided)
604 if provided is None:
605 result = ()
606 for subscription in subscriptions:
607 subscription(*objects)
608 else:
609 result = []
610 for subscription in subscriptions:
611 subscriber = subscription(*objects)
612 if subscriber is not None:
613 result.append(subscriber)
614 return result
615
616 class AdapterLookup(AdapterLookupBase, LookupBase):
617 pass
618
619 @implementer(IAdapterRegistry)
620 class AdapterRegistry(BaseAdapterRegistry):
621
622 LookupClass = AdapterLookup
623
624 def __init__(self, bases=()):
625 # AdapterRegisties are invalidating registries, so
626 # we need to keep track of out invalidating subregistries.
627 self._v_subregistries = weakref.WeakKeyDictionary()
628
629 super(AdapterRegistry, self).__init__(bases)
630
631 def _addSubregistry(self, r):
632 self._v_subregistries[r] = 1
633
634 def _removeSubregistry(self, r):
635 if r in self._v_subregistries:
636 del self._v_subregistries[r]
637
638 def _setBases(self, bases):
639 old = self.__dict__.get('__bases__', ())
640 for r in old:
641 if r not in bases:
642 r._removeSubregistry(self)
643 for r in bases:
644 if r not in old:
645 r._addSubregistry(self)
646
647 super(AdapterRegistry, self)._setBases(bases)
648
649 def changed(self, originally_changed):
650 super(AdapterRegistry, self).changed(originally_changed)
651
652 for sub in self._v_subregistries.keys():
653 sub.changed(originally_changed)
654
655
656 class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase):
657 pass
658
659 @implementer(IAdapterRegistry)
660 class VerifyingAdapterRegistry(BaseAdapterRegistry):
661
662 LookupClass = VerifyingAdapterLookup
663
664 def _convert_None_to_Interface(x):
665 if x is None:
666 return Interface
667 else:
668 return x
669
670 def _lookup(components, specs, provided, name, i, l):
671 if i < l:
672 for spec in specs[i].__sro__:
673 comps = components.get(spec)
674 if comps:
675 r = _lookup(comps, specs, provided, name, i+1, l)
676 if r is not None:
677 return r
678 else:
679 for iface in provided:
680 comps = components.get(iface)
681 if comps:
682 r = comps.get(name)
683 if r is not None:
684 return r
685
686 return None
687
688 def _lookupAll(components, specs, provided, result, i, l):
689 if i < l:
690 for spec in reversed(specs[i].__sro__):
691 comps = components.get(spec)
692 if comps:
693 _lookupAll(comps, specs, provided, result, i+1, l)
694 else:
695 for iface in reversed(provided):
696 comps = components.get(iface)
697 if comps:
698 result.update(comps)
699
700 def _subscriptions(components, specs, provided, name, result, i, l):
701 if i < l:
702 for spec in reversed(specs[i].__sro__):
703 comps = components.get(spec)
704 if comps:
705 _subscriptions(comps, specs, provided, name, result, i+1, l)
706 else:
707 for iface in reversed(provided):
708 comps = components.get(iface)
709 if comps:
710 comps = comps.get(name)
711 if comps:
712 result.extend(comps)