backports_abc.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. """
  2. Patch recently added ABCs into the standard lib module
  3. ``collections.abc`` (Py3) or ``collections`` (Py2).
  4. Usage::
  5. import backports_abc
  6. backports_abc.patch()
  7. or::
  8. try:
  9. from collections.abc import Generator
  10. except ImportError:
  11. from backports_abc import Generator
  12. """
  13. try:
  14. import collections.abc as _collections_abc
  15. except ImportError:
  16. import collections as _collections_abc
  17. def get_mro(cls):
  18. try:
  19. return cls.__mro__
  20. except AttributeError:
  21. return old_style_mro(cls)
  22. def old_style_mro(cls):
  23. yield cls
  24. for base in cls.__bases__:
  25. for c in old_style_mro(base):
  26. yield c
  27. def mk_gen():
  28. from abc import abstractmethod
  29. required_methods = (
  30. '__iter__', '__next__' if hasattr(iter(()), '__next__') else 'next',
  31. 'send', 'throw', 'close')
  32. class Generator(_collections_abc.Iterator):
  33. __slots__ = ()
  34. if '__next__' in required_methods:
  35. def __next__(self):
  36. return self.send(None)
  37. else:
  38. def next(self):
  39. return self.send(None)
  40. @abstractmethod
  41. def send(self, value):
  42. raise StopIteration
  43. @abstractmethod
  44. def throw(self, typ, val=None, tb=None):
  45. if val is None:
  46. if tb is None:
  47. raise typ
  48. val = typ()
  49. if tb is not None:
  50. val = val.with_traceback(tb)
  51. raise val
  52. def close(self):
  53. try:
  54. self.throw(GeneratorExit)
  55. except (GeneratorExit, StopIteration):
  56. pass
  57. else:
  58. raise RuntimeError('generator ignored GeneratorExit')
  59. @classmethod
  60. def __subclasshook__(cls, C):
  61. if cls is Generator:
  62. mro = get_mro(C)
  63. for method in required_methods:
  64. for base in mro:
  65. if method in base.__dict__:
  66. break
  67. else:
  68. return NotImplemented
  69. return True
  70. return NotImplemented
  71. generator = type((lambda: (yield))())
  72. Generator.register(generator)
  73. return Generator
  74. def mk_awaitable():
  75. from abc import abstractmethod, ABCMeta
  76. @abstractmethod
  77. def __await__(self):
  78. yield
  79. @classmethod
  80. def __subclasshook__(cls, C):
  81. if cls is Awaitable:
  82. for B in get_mro(C):
  83. if '__await__' in B.__dict__:
  84. if B.__dict__['__await__']:
  85. return True
  86. break
  87. return NotImplemented
  88. # calling metaclass directly as syntax differs in Py2/Py3
  89. Awaitable = ABCMeta('Awaitable', (), {
  90. '__slots__': (),
  91. '__await__': __await__,
  92. '__subclasshook__': __subclasshook__,
  93. })
  94. return Awaitable
  95. def mk_coroutine():
  96. from abc import abstractmethod
  97. class Coroutine(Awaitable):
  98. __slots__ = ()
  99. @abstractmethod
  100. def send(self, value):
  101. """Send a value into the coroutine.
  102. Return next yielded value or raise StopIteration.
  103. """
  104. raise StopIteration
  105. @abstractmethod
  106. def throw(self, typ, val=None, tb=None):
  107. """Raise an exception in the coroutine.
  108. Return next yielded value or raise StopIteration.
  109. """
  110. if val is None:
  111. if tb is None:
  112. raise typ
  113. val = typ()
  114. if tb is not None:
  115. val = val.with_traceback(tb)
  116. raise val
  117. def close(self):
  118. """Raise GeneratorExit inside coroutine.
  119. """
  120. try:
  121. self.throw(GeneratorExit)
  122. except (GeneratorExit, StopIteration):
  123. pass
  124. else:
  125. raise RuntimeError('coroutine ignored GeneratorExit')
  126. @classmethod
  127. def __subclasshook__(cls, C):
  128. if cls is Coroutine:
  129. mro = get_mro(C)
  130. for method in ('__await__', 'send', 'throw', 'close'):
  131. for base in mro:
  132. if method in base.__dict__:
  133. break
  134. else:
  135. return NotImplemented
  136. return True
  137. return NotImplemented
  138. return Coroutine
  139. ###
  140. # make all ABCs available in this module
  141. try:
  142. Generator = _collections_abc.Generator
  143. except AttributeError:
  144. Generator = mk_gen()
  145. try:
  146. Awaitable = _collections_abc.Awaitable
  147. except AttributeError:
  148. Awaitable = mk_awaitable()
  149. try:
  150. Coroutine = _collections_abc.Coroutine
  151. except AttributeError:
  152. Coroutine = mk_coroutine()
  153. try:
  154. from inspect import isawaitable
  155. except ImportError:
  156. def isawaitable(obj):
  157. return isinstance(obj, Awaitable)
  158. ###
  159. # allow patching the stdlib
  160. PATCHED = {}
  161. def patch(patch_inspect=True):
  162. """
  163. Main entry point for patching the ``collections.abc`` and ``inspect``
  164. standard library modules.
  165. """
  166. PATCHED['collections.abc.Generator'] = _collections_abc.Generator = Generator
  167. PATCHED['collections.abc.Coroutine'] = _collections_abc.Coroutine = Coroutine
  168. PATCHED['collections.abc.Awaitable'] = _collections_abc.Awaitable = Awaitable
  169. if patch_inspect:
  170. import inspect
  171. PATCHED['inspect.isawaitable'] = inspect.isawaitable = isawaitable