bpo-30241: implement contextlib.AbstractAsyncContextManager (#1412)
This commit is contained in:
parent
bfbf04ef18
commit
176baa326b
|
@ -29,6 +29,17 @@ Functions and classes provided:
|
|||
.. versionadded:: 3.6
|
||||
|
||||
|
||||
.. class:: AbstractAsyncContextManager
|
||||
|
||||
An :term:`abstract base class` for classes that implement
|
||||
:meth:`object.__aenter__` and :meth:`object.__aexit__`. A default
|
||||
implementation for :meth:`object.__aenter__` is provided which returns
|
||||
``self`` while :meth:`object.__aexit__` is an abstract method which by default
|
||||
returns ``None``. See also the definition of
|
||||
:ref:`async-context-managers`.
|
||||
|
||||
.. versionadded:: 3.7
|
||||
|
||||
|
||||
.. decorator:: contextmanager
|
||||
|
||||
|
|
|
@ -306,8 +306,9 @@ is a list of strings, not bytes.
|
|||
contextlib
|
||||
----------
|
||||
|
||||
:func:`contextlib.asynccontextmanager` has been added. (Contributed by
|
||||
Jelle Zijlstra in :issue:`29679`.)
|
||||
:func:`~contextlib.asynccontextmanager` and
|
||||
:class:`~contextlib.AbstractAsyncContextManager` have been added. (Contributed
|
||||
by Jelle Zijlstra in :issue:`29679` and :issue:`30241`.)
|
||||
|
||||
cProfile
|
||||
--------
|
||||
|
|
|
@ -6,7 +6,8 @@ from collections import deque
|
|||
from functools import wraps
|
||||
|
||||
__all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext",
|
||||
"AbstractContextManager", "ContextDecorator", "ExitStack",
|
||||
"AbstractContextManager", "AbstractAsyncContextManager",
|
||||
"ContextDecorator", "ExitStack",
|
||||
"redirect_stdout", "redirect_stderr", "suppress"]
|
||||
|
||||
|
||||
|
@ -30,6 +31,27 @@ class AbstractContextManager(abc.ABC):
|
|||
return NotImplemented
|
||||
|
||||
|
||||
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 ContextDecorator(object):
|
||||
"A base class or mixin that enables context managers to work as decorators."
|
||||
|
||||
|
@ -136,7 +158,8 @@ class _GeneratorContextManager(_GeneratorContextManagerBase,
|
|||
raise RuntimeError("generator didn't stop after throw()")
|
||||
|
||||
|
||||
class _AsyncGeneratorContextManager(_GeneratorContextManagerBase):
|
||||
class _AsyncGeneratorContextManager(_GeneratorContextManagerBase,
|
||||
AbstractAsyncContextManager):
|
||||
"""Helper for @asynccontextmanager."""
|
||||
|
||||
async def __aenter__(self):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import asyncio
|
||||
from contextlib import asynccontextmanager
|
||||
from contextlib import asynccontextmanager, AbstractAsyncContextManager
|
||||
import functools
|
||||
from test import support
|
||||
import unittest
|
||||
|
@ -20,6 +20,53 @@ def _async_test(func):
|
|||
return wrapper
|
||||
|
||||
|
||||
class TestAbstractAsyncContextManager(unittest.TestCase):
|
||||
|
||||
@_async_test
|
||||
async def test_enter(self):
|
||||
class DefaultEnter(AbstractAsyncContextManager):
|
||||
async def __aexit__(self, *args):
|
||||
await super().__aexit__(*args)
|
||||
|
||||
manager = DefaultEnter()
|
||||
self.assertIs(await manager.__aenter__(), manager)
|
||||
|
||||
async with manager as context:
|
||||
self.assertIs(manager, context)
|
||||
|
||||
def test_exit_is_abstract(self):
|
||||
class MissingAexit(AbstractAsyncContextManager):
|
||||
pass
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
MissingAexit()
|
||||
|
||||
def test_structural_subclassing(self):
|
||||
class ManagerFromScratch:
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
async def __aexit__(self, exc_type, exc_value, traceback):
|
||||
return None
|
||||
|
||||
self.assertTrue(issubclass(ManagerFromScratch, AbstractAsyncContextManager))
|
||||
|
||||
class DefaultEnter(AbstractAsyncContextManager):
|
||||
async def __aexit__(self, *args):
|
||||
await super().__aexit__(*args)
|
||||
|
||||
self.assertTrue(issubclass(DefaultEnter, AbstractAsyncContextManager))
|
||||
|
||||
class NoneAenter(ManagerFromScratch):
|
||||
__aenter__ = None
|
||||
|
||||
self.assertFalse(issubclass(NoneAenter, AbstractAsyncContextManager))
|
||||
|
||||
class NoneAexit(ManagerFromScratch):
|
||||
__aexit__ = None
|
||||
|
||||
self.assertFalse(issubclass(NoneAexit, AbstractAsyncContextManager))
|
||||
|
||||
|
||||
class AsyncContextManagerTestCase(unittest.TestCase):
|
||||
|
||||
@_async_test
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Add contextlib.AbstractAsyncContextManager. Patch by Jelle Zijlstra.
|
Loading…
Reference in New Issue