comparison mercurial/thirdparty/zope/interface/interface.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) 2001, 2002 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 """Interface object implementation
15 """
16 from __future__ import generators
17
18 import sys
19 from types import MethodType
20 from types import FunctionType
21 import warnings
22 import weakref
23
24 from zope.interface.exceptions import Invalid
25 from zope.interface.ro import ro
26
27
28 CO_VARARGS = 4
29 CO_VARKEYWORDS = 8
30 TAGGED_DATA = '__interface_tagged_values__'
31
32 _decorator_non_return = object()
33
34 def invariant(call):
35 f_locals = sys._getframe(1).f_locals
36 tags = f_locals.setdefault(TAGGED_DATA, {})
37 invariants = tags.setdefault('invariants', [])
38 invariants.append(call)
39 return _decorator_non_return
40
41
42 def taggedValue(key, value):
43 """Attaches a tagged value to an interface at definition time."""
44 f_locals = sys._getframe(1).f_locals
45 tagged_values = f_locals.setdefault(TAGGED_DATA, {})
46 tagged_values[key] = value
47 return _decorator_non_return
48
49
50 class Element(object):
51
52 # We can't say this yet because we don't have enough
53 # infrastructure in place.
54 #
55 #implements(IElement)
56
57 def __init__(self, __name__, __doc__=''):
58 """Create an 'attribute' description
59 """
60 if not __doc__ and __name__.find(' ') >= 0:
61 __doc__ = __name__
62 __name__ = None
63
64 self.__name__=__name__
65 self.__doc__=__doc__
66 self.__tagged_values = {}
67
68 def getName(self):
69 """ Returns the name of the object. """
70 return self.__name__
71
72 def getDoc(self):
73 """ Returns the documentation for the object. """
74 return self.__doc__
75
76 def getTaggedValue(self, tag):
77 """ Returns the value associated with 'tag'. """
78 return self.__tagged_values[tag]
79
80 def queryTaggedValue(self, tag, default=None):
81 """ Returns the value associated with 'tag'. """
82 return self.__tagged_values.get(tag, default)
83
84 def getTaggedValueTags(self):
85 """ Returns a list of all tags. """
86 return self.__tagged_values.keys()
87
88 def setTaggedValue(self, tag, value):
89 """ Associates 'value' with 'key'. """
90 self.__tagged_values[tag] = value
91
92 class SpecificationBasePy(object):
93
94 def providedBy(self, ob):
95 """Is the interface implemented by an object
96 """
97 spec = providedBy(ob)
98 return self in spec._implied
99
100 def implementedBy(self, cls):
101 """Test whether the specification is implemented by a class or factory.
102
103 Raise TypeError if argument is neither a class nor a callable.
104 """
105 spec = implementedBy(cls)
106 return self in spec._implied
107
108 def isOrExtends(self, interface):
109 """Is the interface the same as or extend the given interface
110 """
111 return interface in self._implied
112
113 __call__ = isOrExtends
114
115 SpecificationBase = SpecificationBasePy
116 try:
117 from zope.interface._zope_interface_coptimizations import SpecificationBase
118 except ImportError:
119 pass
120
121 _marker = object()
122 class InterfaceBasePy(object):
123 """Base class that wants to be replaced with a C base :)
124 """
125
126 def __call__(self, obj, alternate=_marker):
127 """Adapt an object to the interface
128 """
129 conform = getattr(obj, '__conform__', None)
130 if conform is not None:
131 adapter = self._call_conform(conform)
132 if adapter is not None:
133 return adapter
134
135 adapter = self.__adapt__(obj)
136
137 if adapter is not None:
138 return adapter
139 elif alternate is not _marker:
140 return alternate
141 else:
142 raise TypeError("Could not adapt", obj, self)
143
144 def __adapt__(self, obj):
145 """Adapt an object to the reciever
146 """
147 if self.providedBy(obj):
148 return obj
149
150 for hook in adapter_hooks:
151 adapter = hook(self, obj)
152 if adapter is not None:
153 return adapter
154
155
156 InterfaceBase = InterfaceBasePy
157 try:
158 from zope.interface._zope_interface_coptimizations import InterfaceBase
159 except ImportError:
160 pass
161
162
163 adapter_hooks = []
164 try:
165 from zope.interface._zope_interface_coptimizations import adapter_hooks
166 except ImportError:
167 pass
168
169
170 class Specification(SpecificationBase):
171 """Specifications
172
173 An interface specification is used to track interface declarations
174 and component registrations.
175
176 This class is a base class for both interfaces themselves and for
177 interface specifications (declarations).
178
179 Specifications are mutable. If you reassign their bases, their
180 relations with other specifications are adjusted accordingly.
181 """
182
183 # Copy some base class methods for speed
184 isOrExtends = SpecificationBase.isOrExtends
185 providedBy = SpecificationBase.providedBy
186
187 def __init__(self, bases=()):
188 self._implied = {}
189 self.dependents = weakref.WeakKeyDictionary()
190 self.__bases__ = tuple(bases)
191
192 def subscribe(self, dependent):
193 self.dependents[dependent] = self.dependents.get(dependent, 0) + 1
194
195 def unsubscribe(self, dependent):
196 n = self.dependents.get(dependent, 0) - 1
197 if not n:
198 del self.dependents[dependent]
199 elif n > 0:
200 self.dependents[dependent] = n
201 else:
202 raise KeyError(dependent)
203
204 def __setBases(self, bases):
205 # Register ourselves as a dependent of our old bases
206 for b in self.__bases__:
207 b.unsubscribe(self)
208
209 # Register ourselves as a dependent of our bases
210 self.__dict__['__bases__'] = bases
211 for b in bases:
212 b.subscribe(self)
213
214 self.changed(self)
215
216 __bases__ = property(
217
218 lambda self: self.__dict__.get('__bases__', ()),
219 __setBases,
220 )
221
222 def changed(self, originally_changed):
223 """We, or something we depend on, have changed
224 """
225 try:
226 del self._v_attrs
227 except AttributeError:
228 pass
229
230 implied = self._implied
231 implied.clear()
232
233 ancestors = ro(self)
234
235 try:
236 if Interface not in ancestors:
237 ancestors.append(Interface)
238 except NameError:
239 pass # defining Interface itself
240
241 self.__sro__ = tuple(ancestors)
242 self.__iro__ = tuple([ancestor for ancestor in ancestors
243 if isinstance(ancestor, InterfaceClass)
244 ])
245
246 for ancestor in ancestors:
247 # We directly imply our ancestors:
248 implied[ancestor] = ()
249
250 # Now, advise our dependents of change:
251 for dependent in tuple(self.dependents.keys()):
252 dependent.changed(originally_changed)
253
254
255 def interfaces(self):
256 """Return an iterator for the interfaces in the specification.
257 """
258 seen = {}
259 for base in self.__bases__:
260 for interface in base.interfaces():
261 if interface not in seen:
262 seen[interface] = 1
263 yield interface
264
265
266 def extends(self, interface, strict=True):
267 """Does the specification extend the given interface?
268
269 Test whether an interface in the specification extends the
270 given interface
271 """
272 return ((interface in self._implied)
273 and
274 ((not strict) or (self != interface))
275 )
276
277 def weakref(self, callback=None):
278 return weakref.ref(self, callback)
279
280 def get(self, name, default=None):
281 """Query for an attribute description
282 """
283 try:
284 attrs = self._v_attrs
285 except AttributeError:
286 attrs = self._v_attrs = {}
287 attr = attrs.get(name)
288 if attr is None:
289 for iface in self.__iro__:
290 attr = iface.direct(name)
291 if attr is not None:
292 attrs[name] = attr
293 break
294
295 if attr is None:
296 return default
297 else:
298 return attr
299
300 class InterfaceClass(Element, InterfaceBase, Specification):
301 """Prototype (scarecrow) Interfaces Implementation."""
302
303 # We can't say this yet because we don't have enough
304 # infrastructure in place.
305 #
306 #implements(IInterface)
307
308 def __init__(self, name, bases=(), attrs=None, __doc__=None,
309 __module__=None):
310
311 if attrs is None:
312 attrs = {}
313
314 if __module__ is None:
315 __module__ = attrs.get('__module__')
316 if isinstance(__module__, str):
317 del attrs['__module__']
318 else:
319 try:
320 # Figure out what module defined the interface.
321 # This is how cPython figures out the module of
322 # a class, but of course it does it in C. :-/
323 __module__ = sys._getframe(1).f_globals['__name__']
324 except (AttributeError, KeyError): # pragma: no cover
325 pass
326
327 self.__module__ = __module__
328
329 d = attrs.get('__doc__')
330 if d is not None:
331 if not isinstance(d, Attribute):
332 if __doc__ is None:
333 __doc__ = d
334 del attrs['__doc__']
335
336 if __doc__ is None:
337 __doc__ = ''
338
339 Element.__init__(self, name, __doc__)
340
341 tagged_data = attrs.pop(TAGGED_DATA, None)
342 if tagged_data is not None:
343 for key, val in tagged_data.items():
344 self.setTaggedValue(key, val)
345
346 for base in bases:
347 if not isinstance(base, InterfaceClass):
348 raise TypeError('Expected base interfaces')
349
350 Specification.__init__(self, bases)
351
352 # Make sure that all recorded attributes (and methods) are of type
353 # `Attribute` and `Method`
354 for name, attr in list(attrs.items()):
355 if name in ('__locals__', '__qualname__', '__annotations__'):
356 # __locals__: Python 3 sometimes adds this.
357 # __qualname__: PEP 3155 (Python 3.3+)
358 # __annotations__: PEP 3107 (Python 3.0+)
359 del attrs[name]
360 continue
361 if isinstance(attr, Attribute):
362 attr.interface = self
363 if not attr.__name__:
364 attr.__name__ = name
365 elif isinstance(attr, FunctionType):
366 attrs[name] = fromFunction(attr, self, name=name)
367 elif attr is _decorator_non_return:
368 del attrs[name]
369 else:
370 raise InvalidInterface("Concrete attribute, " + name)
371
372 self.__attrs = attrs
373
374 self.__identifier__ = "%s.%s" % (self.__module__, self.__name__)
375
376 def interfaces(self):
377 """Return an iterator for the interfaces in the specification.
378 """
379 yield self
380
381 def getBases(self):
382 return self.__bases__
383
384 def isEqualOrExtendedBy(self, other):
385 """Same interface or extends?"""
386 return self == other or other.extends(self)
387
388 def names(self, all=False):
389 """Return the attribute names defined by the interface."""
390 if not all:
391 return self.__attrs.keys()
392
393 r = self.__attrs.copy()
394
395 for base in self.__bases__:
396 r.update(dict.fromkeys(base.names(all)))
397
398 return r.keys()
399
400 def __iter__(self):
401 return iter(self.names(all=True))
402
403 def namesAndDescriptions(self, all=False):
404 """Return attribute names and descriptions defined by interface."""
405 if not all:
406 return self.__attrs.items()
407
408 r = {}
409 for base in self.__bases__[::-1]:
410 r.update(dict(base.namesAndDescriptions(all)))
411
412 r.update(self.__attrs)
413
414 return r.items()
415
416 def getDescriptionFor(self, name):
417 """Return the attribute description for the given name."""
418 r = self.get(name)
419 if r is not None:
420 return r
421
422 raise KeyError(name)
423
424 __getitem__ = getDescriptionFor
425
426 def __contains__(self, name):
427 return self.get(name) is not None
428
429 def direct(self, name):
430 return self.__attrs.get(name)
431
432 def queryDescriptionFor(self, name, default=None):
433 return self.get(name, default)
434
435 def validateInvariants(self, obj, errors=None):
436 """validate object to defined invariants."""
437 for call in self.queryTaggedValue('invariants', []):
438 try:
439 call(obj)
440 except Invalid as e:
441 if errors is None:
442 raise
443 else:
444 errors.append(e)
445 for base in self.__bases__:
446 try:
447 base.validateInvariants(obj, errors)
448 except Invalid:
449 if errors is None:
450 raise
451 if errors:
452 raise Invalid(errors)
453
454 def __repr__(self): # pragma: no cover
455 try:
456 return self._v_repr
457 except AttributeError:
458 name = self.__name__
459 m = self.__module__
460 if m:
461 name = '%s.%s' % (m, name)
462 r = "<%s %s>" % (self.__class__.__name__, name)
463 self._v_repr = r
464 return r
465
466 def _call_conform(self, conform):
467 try:
468 return conform(self)
469 except TypeError: # pragma: no cover
470 # We got a TypeError. It might be an error raised by
471 # the __conform__ implementation, or *we* may have
472 # made the TypeError by calling an unbound method
473 # (object is a class). In the later case, we behave
474 # as though there is no __conform__ method. We can
475 # detect this case by checking whether there is more
476 # than one traceback object in the traceback chain:
477 if sys.exc_info()[2].tb_next is not None:
478 # There is more than one entry in the chain, so
479 # reraise the error:
480 raise
481 # This clever trick is from Phillip Eby
482
483 return None # pragma: no cover
484
485 def __reduce__(self):
486 return self.__name__
487
488 def __cmp(self, other):
489 # Yes, I did mean to name this __cmp, rather than __cmp__.
490 # It is a private method used by __lt__ and __gt__.
491 # I don't want to override __eq__ because I want the default
492 # __eq__, which is really fast.
493 """Make interfaces sortable
494
495 TODO: It would ne nice if:
496
497 More specific interfaces should sort before less specific ones.
498 Otherwise, sort on name and module.
499
500 But this is too complicated, and we're going to punt on it
501 for now.
502
503 For now, sort on interface and module name.
504
505 None is treated as a pseudo interface that implies the loosest
506 contact possible, no contract. For that reason, all interfaces
507 sort before None.
508
509 """
510 if other is None:
511 return -1
512
513 n1 = (getattr(self, '__name__', ''), getattr(self, '__module__', ''))
514 n2 = (getattr(other, '__name__', ''), getattr(other, '__module__', ''))
515
516 # This spelling works under Python3, which doesn't have cmp().
517 return (n1 > n2) - (n1 < n2)
518
519 def __hash__(self):
520 d = self.__dict__
521 if '__module__' not in d or '__name__' not in d: # pragma: no cover
522 warnings.warn('Hashing uninitialized InterfaceClass instance')
523 return 1
524 return hash((self.__name__, self.__module__))
525
526 def __eq__(self, other):
527 c = self.__cmp(other)
528 return c == 0
529
530 def __ne__(self, other):
531 c = self.__cmp(other)
532 return c != 0
533
534 def __lt__(self, other):
535 c = self.__cmp(other)
536 return c < 0
537
538 def __le__(self, other):
539 c = self.__cmp(other)
540 return c <= 0
541
542 def __gt__(self, other):
543 c = self.__cmp(other)
544 return c > 0
545
546 def __ge__(self, other):
547 c = self.__cmp(other)
548 return c >= 0
549
550
551 Interface = InterfaceClass("Interface", __module__ = 'zope.interface')
552
553 class Attribute(Element):
554 """Attribute descriptions
555 """
556
557 # We can't say this yet because we don't have enough
558 # infrastructure in place.
559 #
560 # implements(IAttribute)
561
562 interface = None
563
564
565 class Method(Attribute):
566 """Method interfaces
567
568 The idea here is that you have objects that describe methods.
569 This provides an opportunity for rich meta-data.
570 """
571
572 # We can't say this yet because we don't have enough
573 # infrastructure in place.
574 #
575 # implements(IMethod)
576
577 positional = required = ()
578 _optional = varargs = kwargs = None
579 def _get_optional(self):
580 if self._optional is None:
581 return {}
582 return self._optional
583 def _set_optional(self, opt):
584 self._optional = opt
585 def _del_optional(self):
586 self._optional = None
587 optional = property(_get_optional, _set_optional, _del_optional)
588
589 def __call__(self, *args, **kw):
590 raise BrokenImplementation(self.interface, self.__name__)
591
592 def getSignatureInfo(self):
593 return {'positional': self.positional,
594 'required': self.required,
595 'optional': self.optional,
596 'varargs': self.varargs,
597 'kwargs': self.kwargs,
598 }
599
600 def getSignatureString(self):
601 sig = []
602 for v in self.positional:
603 sig.append(v)
604 if v in self.optional.keys():
605 sig[-1] += "=" + repr(self.optional[v])
606 if self.varargs:
607 sig.append("*" + self.varargs)
608 if self.kwargs:
609 sig.append("**" + self.kwargs)
610
611 return "(%s)" % ", ".join(sig)
612
613 def fromFunction(func, interface=None, imlevel=0, name=None):
614 name = name or func.__name__
615 method = Method(name, func.__doc__)
616 defaults = getattr(func, '__defaults__', None) or ()
617 code = func.__code__
618 # Number of positional arguments
619 na = code.co_argcount-imlevel
620 names = code.co_varnames[imlevel:]
621 opt = {}
622 # Number of required arguments
623 nr = na-len(defaults)
624 if nr < 0:
625 defaults=defaults[-nr:]
626 nr = 0
627
628 # Determine the optional arguments.
629 opt.update(dict(zip(names[nr:], defaults)))
630
631 method.positional = names[:na]
632 method.required = names[:nr]
633 method.optional = opt
634
635 argno = na
636
637 # Determine the function's variable argument's name (i.e. *args)
638 if code.co_flags & CO_VARARGS:
639 method.varargs = names[argno]
640 argno = argno + 1
641 else:
642 method.varargs = None
643
644 # Determine the function's keyword argument's name (i.e. **kw)
645 if code.co_flags & CO_VARKEYWORDS:
646 method.kwargs = names[argno]
647 else:
648 method.kwargs = None
649
650 method.interface = interface
651
652 for key, value in func.__dict__.items():
653 method.setTaggedValue(key, value)
654
655 return method
656
657
658 def fromMethod(meth, interface=None, name=None):
659 if isinstance(meth, MethodType):
660 func = meth.__func__
661 else:
662 func = meth
663 return fromFunction(func, interface, imlevel=1, name=name)
664
665
666 # Now we can create the interesting interfaces and wire them up:
667 def _wire():
668 from zope.interface.declarations import classImplements
669
670 from zope.interface.interfaces import IAttribute
671 classImplements(Attribute, IAttribute)
672
673 from zope.interface.interfaces import IMethod
674 classImplements(Method, IMethod)
675
676 from zope.interface.interfaces import IInterface
677 classImplements(InterfaceClass, IInterface)
678
679 from zope.interface.interfaces import ISpecification
680 classImplements(Specification, ISpecification)
681
682 # We import this here to deal with module dependencies.
683 from zope.interface.declarations import implementedBy
684 from zope.interface.declarations import providedBy
685 from zope.interface.exceptions import InvalidInterface
686 from zope.interface.exceptions import BrokenImplementation