__init__.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. ##############################################################################
  2. # Copyright (c) 2020 Zope Foundation and Contributors.
  3. # All Rights Reserved.
  4. #
  5. # This software is subject to the provisions of the Zope Public License,
  6. # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
  7. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  8. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  9. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  10. # FOR A PARTICULAR PURPOSE.
  11. ##############################################################################
  12. import itertools
  13. from types import FunctionType
  14. from zope.interface import Interface
  15. from zope.interface import classImplements
  16. from zope.interface.interface import InterfaceClass
  17. from zope.interface.interface import _decorator_non_return
  18. from zope.interface.interface import fromFunction
  19. __all__ = [
  20. # Nothing public here.
  21. ]
  22. # pylint:disable=inherit-non-class,
  23. # pylint:disable=no-self-argument,no-method-argument
  24. # pylint:disable=unexpected-special-method-signature
  25. class optional:
  26. # Apply this decorator to a method definition to make it
  27. # optional (remove it from the list of required names), overriding
  28. # the definition inherited from the ABC.
  29. def __init__(self, method):
  30. self.__doc__ = method.__doc__
  31. class ABCInterfaceClass(InterfaceClass):
  32. """
  33. An interface that is automatically derived from a
  34. :class:`abc.ABCMeta` type.
  35. Internal use only.
  36. The body of the interface definition *must* define
  37. a property ``abc`` that is the ABC to base the interface on.
  38. If ``abc`` is *not* in the interface definition, a regular
  39. interface will be defined instead (but ``extra_classes`` is still
  40. respected).
  41. Use the ``@optional`` decorator on method definitions if
  42. the ABC defines methods that are not actually required in all cases
  43. because the Python language has multiple ways to implement a protocol.
  44. For example, the ``iter()`` protocol can be implemented with
  45. ``__iter__`` or the pair ``__len__`` and ``__getitem__``.
  46. When created, any existing classes that are registered to conform
  47. to the ABC are declared to implement this interface. This is *not*
  48. automatically updated as the ABC registry changes. If the body of the
  49. interface definition defines ``extra_classes``, it should be a
  50. tuple giving additional classes to declare implement the interface.
  51. Note that this is not fully symmetric. For example, it is usually
  52. the case that a subclass relationship carries the interface
  53. declarations over::
  54. >>> from zope.interface import Interface
  55. >>> class I1(Interface):
  56. ... pass
  57. ...
  58. >>> from zope.interface import implementer
  59. >>> @implementer(I1)
  60. ... class Root(object):
  61. ... pass
  62. ...
  63. >>> class Child(Root):
  64. ... pass
  65. ...
  66. >>> child = Child()
  67. >>> isinstance(child, Root)
  68. True
  69. >>> from zope.interface import providedBy
  70. >>> list(providedBy(child))
  71. [<InterfaceClass __main__.I1>]
  72. However, that's not the case with ABCs and ABC interfaces. Just
  73. because ``isinstance(A(), AnABC)`` and ``isinstance(B(), AnABC)``
  74. are both true, that doesn't mean there's any class hierarchy
  75. relationship between ``A`` and ``B``, or between either of them
  76. and ``AnABC``. Thus, if ``AnABC`` implemented ``IAnABC``, it would
  77. not follow that either ``A`` or ``B`` implements ``IAnABC`` (nor
  78. their instances provide it)::
  79. >>> class SizedClass(object):
  80. ... def __len__(self): return 1
  81. ...
  82. >>> from collections.abc import Sized
  83. >>> isinstance(SizedClass(), Sized)
  84. True
  85. >>> from zope.interface import classImplements
  86. >>> classImplements(Sized, I1)
  87. None
  88. >>> list(providedBy(SizedClass()))
  89. []
  90. Thus, to avoid conflicting assumptions, ABCs should not be
  91. declared to implement their parallel ABC interface. Only concrete
  92. classes specifically registered with the ABC should be declared to
  93. do so.
  94. .. versionadded:: 5.0.0
  95. """
  96. # If we could figure out invalidation, and used some special
  97. # Specification/Declaration instances, and override the method
  98. # ``providedBy`` here, perhaps we could more closely integrate with ABC
  99. # virtual inheritance?
  100. def __init__(self, name, bases, attrs):
  101. # go ahead and give us a name to ease debugging.
  102. self.__name__ = name
  103. extra_classes = attrs.pop('extra_classes', ())
  104. ignored_classes = attrs.pop('ignored_classes', ())
  105. if 'abc' not in attrs:
  106. # Something like ``IList(ISequence)``: We're extending
  107. # abc interfaces but not an ABC interface ourself.
  108. InterfaceClass.__init__(self, name, bases, attrs)
  109. ABCInterfaceClass.__register_classes(
  110. self, extra_classes, ignored_classes,
  111. )
  112. self.__class__ = InterfaceClass
  113. return
  114. based_on = attrs.pop('abc')
  115. self.__abc = based_on
  116. self.__extra_classes = tuple(extra_classes)
  117. self.__ignored_classes = tuple(ignored_classes)
  118. assert name[1:] == based_on.__name__, (name, based_on)
  119. methods = {
  120. # Passing the name is important in case of aliases,
  121. # e.g., ``__ror__ = __or__``.
  122. k: self.__method_from_function(v, k)
  123. for k, v in vars(based_on).items()
  124. if isinstance(v, FunctionType) and
  125. not self.__is_private_name(k) and
  126. not self.__is_reverse_protocol_name(k)
  127. }
  128. methods['__doc__'] = self.__create_class_doc(attrs)
  129. # Anything specified in the body takes precedence.
  130. methods.update(attrs)
  131. InterfaceClass.__init__(self, name, bases, methods)
  132. self.__register_classes()
  133. @staticmethod
  134. def __optional_methods_to_docs(attrs):
  135. optionals = {k: v for k, v in attrs.items() if isinstance(v, optional)}
  136. for k in optionals:
  137. attrs[k] = _decorator_non_return
  138. if not optionals:
  139. return ''
  140. docs = "\n\nThe following methods are optional:\n - " + "\n-".join(
  141. f"{k}\n{v.__doc__}" for k, v in optionals.items()
  142. )
  143. return docs
  144. def __create_class_doc(self, attrs):
  145. based_on = self.__abc
  146. def ref(c):
  147. mod = c.__module__
  148. name = c.__name__
  149. if mod == str.__module__:
  150. return "`%s`" % name
  151. if mod == '_io':
  152. mod = 'io'
  153. return f"`{mod}.{name}`"
  154. implementations_doc = "\n - ".join(
  155. ref(c)
  156. for c in sorted(self.getRegisteredConformers(), key=ref)
  157. )
  158. if implementations_doc:
  159. implementations_doc = (
  160. "\n\nKnown implementations are:\n\n - " + implementations_doc
  161. )
  162. based_on_doc = (based_on.__doc__ or '')
  163. based_on_doc = based_on_doc.splitlines()
  164. based_on_doc = based_on_doc[0] if based_on_doc else ''
  165. doc = """Interface for the ABC `{}.{}`.\n\n{}{}{}""".format(
  166. based_on.__module__, based_on.__name__,
  167. attrs.get('__doc__', based_on_doc),
  168. self.__optional_methods_to_docs(attrs),
  169. implementations_doc
  170. )
  171. return doc
  172. @staticmethod
  173. def __is_private_name(name):
  174. if name.startswith('__') and name.endswith('__'):
  175. return False
  176. return name.startswith('_')
  177. @staticmethod
  178. def __is_reverse_protocol_name(name):
  179. # The reverse names, like __rand__,
  180. # aren't really part of the protocol. The interpreter has
  181. # very complex behaviour around invoking those. PyPy
  182. # doesn't always even expose them as attributes.
  183. return name.startswith('__r') and name.endswith('__')
  184. def __method_from_function(self, function, name):
  185. method = fromFunction(function, self, name=name)
  186. # Eliminate the leading *self*, which is implied in
  187. # an interface, but explicit in an ABC.
  188. method.positional = method.positional[1:]
  189. return method
  190. def __register_classes(self, conformers=None, ignored_classes=None):
  191. # Make the concrete classes already present in our ABC's registry
  192. # declare that they implement this interface.
  193. conformers = (
  194. conformers if conformers is not None
  195. else self.getRegisteredConformers()
  196. )
  197. ignored = (
  198. ignored_classes if ignored_classes is not None
  199. else self.__ignored_classes
  200. )
  201. for cls in conformers:
  202. if cls in ignored:
  203. continue
  204. classImplements(cls, self)
  205. def getABC(self):
  206. """
  207. Return the ABC this interface represents.
  208. """
  209. return self.__abc
  210. def getRegisteredConformers(self):
  211. """
  212. Return an iterable of the classes that are known to conform to
  213. the ABC this interface parallels.
  214. """
  215. based_on = self.__abc
  216. # The registry only contains things that aren't already
  217. # known to be subclasses of the ABC. But the ABC is in charge
  218. # of checking that, so its quite possible that registrations
  219. # are in fact ignored, winding up just in the _abc_cache.
  220. try:
  221. registered = (
  222. list(based_on._abc_registry) + list(based_on._abc_cache)
  223. )
  224. except AttributeError:
  225. # Rewritten in C in CPython 3.7.
  226. # These expose the underlying weakref.
  227. from abc import _get_dump
  228. data = _get_dump(based_on)
  229. registry = data[0]
  230. cache = data[1]
  231. registered = [x() for x in itertools.chain(registry, cache)]
  232. registered = [x for x in registered if x is not None]
  233. return set(itertools.chain(registered, self.__extra_classes))
  234. def _create_ABCInterface():
  235. # It's a two-step process to create the root ABCInterface, because without
  236. # specifying a corresponding ABC, using the normal constructor gets us a
  237. # plain InterfaceClass object, and there is no ABC to associate with the
  238. # root.
  239. abc_name_bases_attrs = ('ABCInterface', (Interface,), {})
  240. instance = ABCInterfaceClass.__new__(
  241. ABCInterfaceClass, *abc_name_bases_attrs,
  242. )
  243. InterfaceClass.__init__(instance, *abc_name_bases_attrs)
  244. return instance
  245. ABCInterface = _create_ABCInterface()