gh-118660: Add second type parameter to (Async)ContextManager (#118681)

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Jelle Zijlstra 2024-05-07 07:16:05 -07:00 committed by GitHub
parent a855f824a2
commit 71080b8a0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 42 additions and 7 deletions

View file

@ -3819,10 +3819,15 @@ Aliases to other ABCs in :mod:`collections.abc`
Aliases to :mod:`contextlib` ABCs
"""""""""""""""""""""""""""""""""
.. class:: ContextManager(Generic[T_co])
.. class:: ContextManager(Generic[T_co, ExitT_co])
Deprecated alias to :class:`contextlib.AbstractContextManager`.
The first type parameter, ``T_co``, represents the type returned by
the :meth:`~object.__enter__` method. The optional second type parameter, ``ExitT_co``,
which defaults to ``bool | None``, represents the type returned by the
:meth:`~object.__exit__` method.
.. versionadded:: 3.5.4
.. deprecated:: 3.9
@ -3830,10 +3835,18 @@ Aliases to :mod:`contextlib` ABCs
now supports subscripting (``[]``).
See :pep:`585` and :ref:`types-genericalias`.
.. class:: AsyncContextManager(Generic[T_co])
.. versionchanged:: 3.13
Added the optional second type parameter, ``ExitT_co``.
.. class:: AsyncContextManager(Generic[T_co, AExitT_co])
Deprecated alias to :class:`contextlib.AbstractAsyncContextManager`.
The first type parameter, ``T_co``, represents the type returned by
the :meth:`~object.__aenter__` method. The optional second type parameter, ``AExitT_co``,
which defaults to ``bool | None``, represents the type returned by the
:meth:`~object.__aexit__` method.
.. versionadded:: 3.6.2
.. deprecated:: 3.9
@ -3841,6 +3854,9 @@ Aliases to :mod:`contextlib` ABCs
now supports subscripting (``[]``).
See :pep:`585` and :ref:`types-genericalias`.
.. versionchanged:: 3.13
Added the optional second type parameter, ``AExitT_co``.
Deprecation Timeline of Major Features
======================================

View file

@ -7511,6 +7511,15 @@ class OtherABCTests(BaseTestCase):
self.assertIsInstance(cm, typing.ContextManager)
self.assertNotIsInstance(42, typing.ContextManager)
def test_contextmanager_type_params(self):
cm1 = typing.ContextManager[int]
self.assertEqual(get_args(cm1), (int, bool | None))
cm2 = typing.ContextManager[int, None]
self.assertEqual(get_args(cm2), (int, types.NoneType))
type gen_cm[T1, T2] = typing.ContextManager[T1, T2]
self.assertEqual(get_args(gen_cm.__value__[int, None]), (int, types.NoneType))
def test_async_contextmanager(self):
class NotACM:
pass
@ -7522,11 +7531,17 @@ class OtherABCTests(BaseTestCase):
cm = manager()
self.assertNotIsInstance(cm, typing.AsyncContextManager)
self.assertEqual(typing.AsyncContextManager[int].__args__, (int,))
self.assertEqual(typing.AsyncContextManager[int].__args__, (int, bool | None))
with self.assertRaises(TypeError):
isinstance(42, typing.AsyncContextManager[int])
with self.assertRaises(TypeError):
typing.AsyncContextManager[int, str]
typing.AsyncContextManager[int, str, float]
def test_asynccontextmanager_type_params(self):
cm1 = typing.AsyncContextManager[int]
self.assertEqual(get_args(cm1), (int, bool | None))
cm2 = typing.AsyncContextManager[int, None]
self.assertEqual(get_args(cm2), (int, types.NoneType))
class TypeTests(BaseTestCase):
@ -9953,7 +9968,7 @@ class SpecialAttrsTests(BaseTestCase):
typing.ValuesView: 'ValuesView',
# Subscribed ABC classes
typing.AbstractSet[Any]: 'AbstractSet',
typing.AsyncContextManager[Any]: 'AsyncContextManager',
typing.AsyncContextManager[Any, Any]: 'AsyncContextManager',
typing.AsyncGenerator[Any, Any]: 'AsyncGenerator',
typing.AsyncIterable[Any]: 'AsyncIterable',
typing.AsyncIterator[Any]: 'AsyncIterator',
@ -9963,7 +9978,7 @@ class SpecialAttrsTests(BaseTestCase):
typing.ChainMap[Any, Any]: 'ChainMap',
typing.Collection[Any]: 'Collection',
typing.Container[Any]: 'Container',
typing.ContextManager[Any]: 'ContextManager',
typing.ContextManager[Any, Any]: 'ContextManager',
typing.Coroutine[Any, Any, Any]: 'Coroutine',
typing.Counter[Any]: 'Counter',
typing.DefaultDict[Any, Any]: 'DefaultDict',

View file

@ -3783,7 +3783,7 @@ def __getattr__(attr):
obj = _alias(getattr(re, attr), 1)
elif attr in {"ContextManager", "AsyncContextManager"}:
import contextlib
obj = _alias(getattr(contextlib, f"Abstract{attr}"), 1, name=attr)
obj = _alias(getattr(contextlib, f"Abstract{attr}"), 2, name=attr, defaults=(bool | None,))
else:
raise AttributeError(f"module {__name__!r} has no attribute {attr!r}")
globals()[attr] = obj

View file

@ -0,0 +1,4 @@
Add an optional second type parameter to :class:`typing.ContextManager` and
:class:`typing.AsyncContextManager`, representing the return types of
:meth:`~object.__exit__` and :meth:`~object.__aexit__` respectively.
This parameter defaults to ``bool | None``.