123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- """
- Patch recently added ABCs into the standard lib module
- ``collections.abc`` (Py3) or ``collections`` (Py2).
- Usage::
- import backports_abc
- backports_abc.patch()
- or::
- try:
- from collections.abc import Generator
- except ImportError:
- from backports_abc import Generator
- """
- try:
- import collections.abc as _collections_abc
- except ImportError:
- import collections as _collections_abc
- def get_mro(cls):
- try:
- return cls.__mro__
- except AttributeError:
- return old_style_mro(cls)
- def old_style_mro(cls):
- yield cls
- for base in cls.__bases__:
- for c in old_style_mro(base):
- yield c
- def mk_gen():
- from abc import abstractmethod
- required_methods = (
- '__iter__', '__next__' if hasattr(iter(()), '__next__') else 'next',
- 'send', 'throw', 'close')
- class Generator(_collections_abc.Iterator):
- __slots__ = ()
- if '__next__' in required_methods:
- def __next__(self):
- return self.send(None)
- else:
- def next(self):
- return self.send(None)
- @abstractmethod
- def send(self, value):
- raise StopIteration
- @abstractmethod
- def throw(self, typ, val=None, tb=None):
- if val is None:
- if tb is None:
- raise typ
- val = typ()
- if tb is not None:
- val = val.with_traceback(tb)
- raise val
- def close(self):
- try:
- self.throw(GeneratorExit)
- except (GeneratorExit, StopIteration):
- pass
- else:
- raise RuntimeError('generator ignored GeneratorExit')
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Generator:
- mro = get_mro(C)
- for method in required_methods:
- for base in mro:
- if method in base.__dict__:
- break
- else:
- return NotImplemented
- return True
- return NotImplemented
- generator = type((lambda: (yield))())
- Generator.register(generator)
- return Generator
- def mk_awaitable():
- from abc import abstractmethod, ABCMeta
- @abstractmethod
- def __await__(self):
- yield
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Awaitable:
- for B in get_mro(C):
- if '__await__' in B.__dict__:
- if B.__dict__['__await__']:
- return True
- break
- return NotImplemented
- # calling metaclass directly as syntax differs in Py2/Py3
- Awaitable = ABCMeta('Awaitable', (), {
- '__slots__': (),
- '__await__': __await__,
- '__subclasshook__': __subclasshook__,
- })
- return Awaitable
- def mk_coroutine():
- from abc import abstractmethod
- class Coroutine(Awaitable):
- __slots__ = ()
- @abstractmethod
- def send(self, value):
- """Send a value into the coroutine.
- Return next yielded value or raise StopIteration.
- """
- raise StopIteration
- @abstractmethod
- def throw(self, typ, val=None, tb=None):
- """Raise an exception in the coroutine.
- Return next yielded value or raise StopIteration.
- """
- if val is None:
- if tb is None:
- raise typ
- val = typ()
- if tb is not None:
- val = val.with_traceback(tb)
- raise val
- def close(self):
- """Raise GeneratorExit inside coroutine.
- """
- try:
- self.throw(GeneratorExit)
- except (GeneratorExit, StopIteration):
- pass
- else:
- raise RuntimeError('coroutine ignored GeneratorExit')
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Coroutine:
- mro = get_mro(C)
- for method in ('__await__', 'send', 'throw', 'close'):
- for base in mro:
- if method in base.__dict__:
- break
- else:
- return NotImplemented
- return True
- return NotImplemented
- return Coroutine
- ###
- # make all ABCs available in this module
- try:
- Generator = _collections_abc.Generator
- except AttributeError:
- Generator = mk_gen()
- try:
- Awaitable = _collections_abc.Awaitable
- except AttributeError:
- Awaitable = mk_awaitable()
- try:
- Coroutine = _collections_abc.Coroutine
- except AttributeError:
- Coroutine = mk_coroutine()
- try:
- from inspect import isawaitable
- except ImportError:
- def isawaitable(obj):
- return isinstance(obj, Awaitable)
- ###
- # allow patching the stdlib
- PATCHED = {}
- def patch(patch_inspect=True):
- """
- Main entry point for patching the ``collections.abc`` and ``inspect``
- standard library modules.
- """
- PATCHED['collections.abc.Generator'] = _collections_abc.Generator = Generator
- PATCHED['collections.abc.Coroutine'] = _collections_abc.Coroutine = Coroutine
- PATCHED['collections.abc.Awaitable'] = _collections_abc.Awaitable = Awaitable
- if patch_inspect:
- import inspect
- PATCHED['inspect.isawaitable'] = inspect.isawaitable = isawaitable
|