diff --git a/Lib/contextlib.py b/Lib/contextlib.py index 1ff8cdf1c..345a04297 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -31,25 +31,26 @@ class AbstractContextManager(abc.ABC): return NotImplemented -class AbstractAsyncContextManager(abc.ABC): +# XXX +# class AbstractAsyncContextManager(abc.ABC): - """An abstract base class for asynchronous context managers.""" +# """An abstract base class for asynchronous context managers.""" - async def __aenter__(self): - """Return `self` upon entering the runtime context.""" - return self +# 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 +# @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 +# @classmethod +# def __subclasshook__(cls, C): +# if cls is AbstractAsyncContextManager: +# return _collections_abc._check_methods(C, "__aenter__", +# "__aexit__") +# return NotImplemented class ContextDecorator(object): @@ -161,50 +162,51 @@ class _GeneratorContextManager(_GeneratorContextManagerBase, raise RuntimeError("generator didn't stop after throw()") -class _AsyncGeneratorContextManager(_GeneratorContextManagerBase, - AbstractAsyncContextManager): - """Helper for @asynccontextmanager.""" +# XXX +# 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 __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 throw()") - 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 +# 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 throw()") +# 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 contextmanager(func): @@ -267,10 +269,12 @@ def asynccontextmanager(func): finally: """ - @wraps(func) - def helper(*args, **kwds): - return _AsyncGeneratorContextManager(func, args, kwds) - return helper + # XXX + # @wraps(func) + # def helper(*args, **kwds): + # return _AsyncGeneratorContextManager(func, args, kwds) + # return helper + raise RuntimeError("async not supported") class closing(AbstractContextManager): @@ -519,141 +523,142 @@ class ExitStack(_BaseExitStack, AbstractContextManager): self.__exit__(None, None, None) -# Inspired by discussions on https://bugs.python.org/issue29302 -class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager): - """Async context manager for dynamic management of a stack of exit - callbacks. +# XXX +# # Inspired by discussions on https://bugs.python.org/issue29302 +# class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager): +# """Async context manager for dynamic management of a stack of exit +# callbacks. - For example: - async with AsyncExitStack() as stack: - connections = [await stack.enter_async_context(get_connection()) - for i in range(5)] - # All opened connections will automatically be released at the - # end of the async with statement, even if attempts to open a - # connection later in the list raise an exception. - """ +# For example: +# async with AsyncExitStack() as stack: +# connections = [await stack.enter_async_context(get_connection()) +# for i in range(5)] +# # All opened connections will automatically be released at the +# # end of the async with statement, even if attempts to open a +# # connection later in the list raise an exception. +# """ - @staticmethod - def _create_async_exit_wrapper(cm, cm_exit): - async def _exit_wrapper(exc_type, exc, tb): - return await cm_exit(cm, exc_type, exc, tb) - return _exit_wrapper +# @staticmethod +# def _create_async_exit_wrapper(cm, cm_exit): +# async def _exit_wrapper(exc_type, exc, tb): +# return await cm_exit(cm, exc_type, exc, tb) +# return _exit_wrapper - @staticmethod - def _create_async_cb_wrapper(callback, *args, **kwds): - async def _exit_wrapper(exc_type, exc, tb): - await callback(*args, **kwds) - return _exit_wrapper +# @staticmethod +# def _create_async_cb_wrapper(callback, *args, **kwds): +# async def _exit_wrapper(exc_type, exc, tb): +# await callback(*args, **kwds) +# return _exit_wrapper - async def enter_async_context(self, cm): - """Enters the supplied async context manager. +# async def enter_async_context(self, cm): +# """Enters the supplied async context manager. - If successful, also pushes its __aexit__ method as a callback and - returns the result of the __aenter__ method. - """ - _cm_type = type(cm) - _exit = _cm_type.__aexit__ - result = await _cm_type.__aenter__(cm) - self._push_async_cm_exit(cm, _exit) - return result +# If successful, also pushes its __aexit__ method as a callback and +# returns the result of the __aenter__ method. +# """ +# _cm_type = type(cm) +# _exit = _cm_type.__aexit__ +# result = await _cm_type.__aenter__(cm) +# self._push_async_cm_exit(cm, _exit) +# return result - def push_async_exit(self, exit): - """Registers a coroutine function with the standard __aexit__ method - signature. +# def push_async_exit(self, exit): +# """Registers a coroutine function with the standard __aexit__ method +# signature. - Can suppress exceptions the same way __aexit__ method can. - Also accepts any object with an __aexit__ method (registering a call - to the method instead of the object itself). - """ - _cb_type = type(exit) - try: - exit_method = _cb_type.__aexit__ - except AttributeError: - # Not an async context manager, so assume it's a coroutine function - self._push_exit_callback(exit, False) - else: - self._push_async_cm_exit(exit, exit_method) - return exit # Allow use as a decorator +# Can suppress exceptions the same way __aexit__ method can. +# Also accepts any object with an __aexit__ method (registering a call +# to the method instead of the object itself). +# """ +# _cb_type = type(exit) +# try: +# exit_method = _cb_type.__aexit__ +# except AttributeError: +# # Not an async context manager, so assume it's a coroutine function +# self._push_exit_callback(exit, False) +# else: +# self._push_async_cm_exit(exit, exit_method) +# return exit # Allow use as a decorator - def push_async_callback(self, callback, *args, **kwds): - """Registers an arbitrary coroutine function and arguments. +# def push_async_callback(self, callback, *args, **kwds): +# """Registers an arbitrary coroutine function and arguments. - Cannot suppress exceptions. - """ - _exit_wrapper = self._create_async_cb_wrapper(callback, *args, **kwds) +# Cannot suppress exceptions. +# """ +# _exit_wrapper = self._create_async_cb_wrapper(callback, *args, **kwds) - # We changed the signature, so using @wraps is not appropriate, but - # setting __wrapped__ may still help with introspection. - _exit_wrapper.__wrapped__ = callback - self._push_exit_callback(_exit_wrapper, False) - return callback # Allow use as a decorator +# # We changed the signature, so using @wraps is not appropriate, but +# # setting __wrapped__ may still help with introspection. +# _exit_wrapper.__wrapped__ = callback +# self._push_exit_callback(_exit_wrapper, False) +# return callback # Allow use as a decorator - async def aclose(self): - """Immediately unwind the context stack.""" - await self.__aexit__(None, None, None) +# async def aclose(self): +# """Immediately unwind the context stack.""" +# await self.__aexit__(None, None, None) - def _push_async_cm_exit(self, cm, cm_exit): - """Helper to correctly register coroutine function to __aexit__ - method.""" - _exit_wrapper = self._create_async_exit_wrapper(cm, cm_exit) - _exit_wrapper.__self__ = cm - self._push_exit_callback(_exit_wrapper, False) +# def _push_async_cm_exit(self, cm, cm_exit): +# """Helper to correctly register coroutine function to __aexit__ +# method.""" +# _exit_wrapper = self._create_async_exit_wrapper(cm, cm_exit) +# _exit_wrapper.__self__ = cm +# self._push_exit_callback(_exit_wrapper, False) - async def __aenter__(self): - return self +# async def __aenter__(self): +# return self - async def __aexit__(self, *exc_details): - received_exc = exc_details[0] is not None +# async def __aexit__(self, *exc_details): +# received_exc = exc_details[0] is not None - # We manipulate the exception state so it behaves as though - # we were actually nesting multiple with statements - frame_exc = sys.exc_info()[1] - def _fix_exception_context(new_exc, old_exc): - # Context may not be correct, so find the end of the chain - while 1: - exc_context = new_exc.__context__ - if exc_context is old_exc: - # Context is already set correctly (see issue 20317) - return - if exc_context is None or exc_context is frame_exc: - break - new_exc = exc_context - # Change the end of the chain to point to the exception - # we expect it to reference - new_exc.__context__ = old_exc +# # We manipulate the exception state so it behaves as though +# # we were actually nesting multiple with statements +# frame_exc = sys.exc_info()[1] +# def _fix_exception_context(new_exc, old_exc): +# # Context may not be correct, so find the end of the chain +# while 1: +# exc_context = new_exc.__context__ +# if exc_context is old_exc: +# # Context is already set correctly (see issue 20317) +# return +# if exc_context is None or exc_context is frame_exc: +# break +# new_exc = exc_context +# # Change the end of the chain to point to the exception +# # we expect it to reference +# new_exc.__context__ = old_exc - # Callbacks are invoked in LIFO order to match the behaviour of - # nested context managers - suppressed_exc = False - pending_raise = False - while self._exit_callbacks: - is_sync, cb = self._exit_callbacks.pop() - try: - if is_sync: - cb_suppress = cb(*exc_details) - else: - cb_suppress = await cb(*exc_details) +# # Callbacks are invoked in LIFO order to match the behaviour of +# # nested context managers +# suppressed_exc = False +# pending_raise = False +# while self._exit_callbacks: +# is_sync, cb = self._exit_callbacks.pop() +# try: +# if is_sync: +# cb_suppress = cb(*exc_details) +# else: +# cb_suppress = await cb(*exc_details) - if cb_suppress: - suppressed_exc = True - pending_raise = False - exc_details = (None, None, None) - except: - new_exc_details = sys.exc_info() - # simulate the stack of exceptions by setting the context - _fix_exception_context(new_exc_details[1], exc_details[1]) - pending_raise = True - exc_details = new_exc_details - if pending_raise: - try: - # bare "raise exc_details[1]" replaces our carefully - # set-up context - fixed_ctx = exc_details[1].__context__ - raise exc_details[1] - except BaseException: - exc_details[1].__context__ = fixed_ctx - raise - return received_exc and suppressed_exc +# if cb_suppress: +# suppressed_exc = True +# pending_raise = False +# exc_details = (None, None, None) +# except: +# new_exc_details = sys.exc_info() +# # simulate the stack of exceptions by setting the context +# _fix_exception_context(new_exc_details[1], exc_details[1]) +# pending_raise = True +# exc_details = new_exc_details +# if pending_raise: +# try: +# # bare "raise exc_details[1]" replaces our carefully +# # set-up context +# fixed_ctx = exc_details[1].__context__ +# raise exc_details[1] +# except BaseException: +# exc_details[1].__context__ = fixed_ctx +# raise +# return received_exc and suppressed_exc class nullcontext(AbstractContextManager):