8000 Add AsyncContextManager generic class by ilevkivskyi · Pull Request #438 · python/typing · GitHub
[go: up one dir, main page]

Skip to content

Add AsyncContextManager generic class #438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion src/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,12 @@ def __anext__(self) -> T_a:
return data
else:
raise StopAsyncIteration

class ACM:
async def __aenter__(self) -> int:
return 42
async def __aexit__(self, etype, eval, tb):
return None
"""

if ASYNCIO:
Expand All @@ -1562,12 +1568,13 @@ def __anext__(self) -> T_a:
else:
# fake names for the sake of static analysis
asyncio = None
AwaitableWrapper = AsyncIteratorWrapper = object
AwaitableWrapper = AsyncIteratorWrapper = ACM = object

PY36 = sys.version_info[:2] >= (3, 6)

PY36_TESTS = """
from test import ann_module, ann_module2, ann_module3
from typing import AsyncContextManager

class A:
y: float
Expand Down Expand Up @@ -1604,6 +1611,16 @@ def __str__(self):
return f'{self.x} -> {self.y}'
def __add__(self, other):
return 0

async def g_with(am: AsyncContextManager[int]):
x: int
async with am as x:
return x

try:
g_with(ACM()).send(None)
except StopIteration as e:
assert e.args[0] == 42
"""

if PY36:
Expand Down Expand Up @@ -2165,6 +2182,24 @@ def manager():
self.assertIsInstance(cm, typing.ContextManager)
self.assertNotIsInstance(42, typing.ContextManager)

@skipUnless(ASYNCIO, 'Python 3.5 required')
def test_async_contextmanager(self):
class NotACM:
pass
self.assertIsInstance(ACM(), typing.AsyncContextManager)
self.assertNotIsInstance(NotACM(), typing.AsyncContextManager)
@contextlib.contextmanager
def manager():
yield 42

cm = manager()
self.assertNotIsInstance(cm, typing.AsyncContextManager)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add a test that also serves as an example of how to use AsyncContextManager (presumably in async with ...?

self.assertEqual(typing.AsyncContextManager[int].__args__, (int,))
with self.assertRaises(TypeError):
isinstance(42, typing.AsyncContextManager[int])
with self.assertRaises(TypeError):
typing.AsyncContextManager[int, str]


class TypeTests(BaseTestCase):

Expand Down
35 changes: 35 additions & 0 deletions src/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import collections.abc as collections_abc
except ImportError:
import collections as collections_abc # Fallback for PY3.2.
if sys.version_info[:2] >= (3, 6):
import _collections_abc # Needed for private function _check_methods # noqa
try:
from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType
except ImportError:
Expand Down Expand Up @@ -59,6 +61,7 @@
# Coroutine,
# Collection,
# AsyncGenerator,
# AsyncContextManager

# Structural checks, a.k.a. protocols.
'Reversible',
Expand Down Expand Up @@ -1974,6 +1977,38 @@ def __subclasshook__(cls, C):
return NotImplemented


if hasattr(contextlib, 'AbstractAsyncContextManager'):
class AsyncContextManager(Generic[T_co],
extra=contextlib.AbstractAsyncContextManager):
__slots__ = ()

__all__.append('AsyncContextManager')
elif sys.version_info[:2] >= (3, 5):
exec("""
class AsyncContextManager(Generic[T_co]):
__slots__ = ()

async def __aenter__(self):
return self

@abc.abstractmethod
async def __aexit__(self, exc_type, exc_value, traceback):
return None

@classmethod
def __subclasshook__(cls, C):
if cls is AsyncContextManager:
if sys.version_info[:2] >= (3, 6):
return _collections_abc._check_methods(C, "__aenter__", "__aexit__")
if (any("__aenter__" in B.__dict__ for B in C.__mro__) and
any("__aexit__" in B.__dict__ for B in C.__mro__)):
return True
return NotImplemented

__all__.append('AsyncContextManager')
""")


class Dict(dict, MutableMapping[KT, VT], extra=dict):

__slots__ = ()
Expand Down
0