""" @asynccontextmanager code, copied from Python 3.7's contextlib. For usage in Python 3.6. """ import abc from functools import wraps import _collections_abc __all__ = ["asynccontextmanager"] class AbstractAsyncContextManager(abc.ABC): """An abstract base class for asynchronous context managers.""" async def __aenter__(self): """Return `self` upon entering the runtime context.""" return self @abc.abstractmethod async def __aexit__(self, exc_type, exc_value, traceback): """Raise any exception triggered within the runtime context.""" return None @classmethod def __subclasshook__(cls, C): if cls is AbstractAsyncContextManager: return _collections_abc._check_methods(C, "__aenter__", "__aexit__") return NotImplemented class _GeneratorContextManagerBase: """Shared functionality for @contextmanager and @asynccontextmanager.""" def __init__(self, func, args, kwds): self.gen = func(*args, **kwds) self.func, self.args, self.kwds = func, args, kwds # Issue 19330: ensure context manager instances have good docstrings doc = getattr(func, "__doc__", None) if doc is None: doc = type(self).__doc__ self.__doc__ = doc # Unfortunately, this still doesn't provide good help output when # inspecting the created context manager instances, since pydoc # currently bypasses the instance docstring and shows the docstring # for the class instead. # See http://bugs.python.org/issue19404 for more details. class _AsyncGeneratorContextManager( _GeneratorContextManagerBase, AbstractAsyncContextManager ): """Helper for @asynccontextmanager.""" async def __aenter__(self): try: return await self.gen.__anext__() except StopAsyncIteration: raise RuntimeError("generator didn't yield") from None async def __aexit__(self, typ, value, traceback): if typ is None: try: await self.gen.__anext__() except StopAsyncIteration: return else: raise RuntimeError("generator didn't stop") else: if value is None: value = typ() # See _GeneratorContextManager.__exit__ for comments on subtleties # in this implementation try: await self.gen.athrow(typ, value, traceback) raise RuntimeError("generator didn't stop after athrow()") except StopAsyncIteration as exc: return exc is not value except RuntimeError as exc: if exc is value: return False # Avoid suppressing if a StopIteration exception # was passed to throw() and later wrapped into a RuntimeError # (see PEP 479 for sync generators; async generators also # have this behavior). But do this only if the exception wrapped # by the RuntimeError is actully Stop(Async)Iteration (see # issue29692). if isinstance(value, (StopIteration, StopAsyncIteration)): if exc.__cause__ is value: return False raise except BaseException as exc: if exc is not value: raise def asynccontextmanager(func): """@asynccontextmanager decorator. Typical usage: @asynccontextmanager async def some_async_generator(): try: yield finally: This makes this: async with some_async_generator() as : equivalent to this: try: = finally: """ @wraps(func) def helper(*args, **kwds): return _AsyncGeneratorContextManager(func, args, kwds) return helper