Skip to content

Commit 7c8ca7f

Browse files
committed
Merge remote-tracking branch 'upstream/main' into nopastebreak
2 parents b060efa + e5413ec commit 7c8ca7f

File tree

6 files changed

+58
-10
lines changed

6 files changed

+58
-10
lines changed

Doc/library/typing.rst

+18-2
Original file line numberDiff line numberDiff line change
@@ -3819,28 +3819,44 @@ Aliases to other ABCs in :mod:`collections.abc`
38193819
Aliases to :mod:`contextlib` ABCs
38203820
"""""""""""""""""""""""""""""""""
38213821

3822-
.. class:: ContextManager(Generic[T_co])
3822+
.. class:: ContextManager(Generic[T_co, ExitT_co])
38233823

38243824
Deprecated alias to :class:`contextlib.AbstractContextManager`.
38253825

3826+
The first type parameter, ``T_co``, represents the type returned by
3827+
the :meth:`~object.__enter__` method. The optional second type parameter, ``ExitT_co``,
3828+
which defaults to ``bool | None``, represents the type returned by the
3829+
:meth:`~object.__exit__` method.
3830+
38263831
.. versionadded:: 3.5.4
38273832

38283833
.. deprecated:: 3.9
38293834
:class:`contextlib.AbstractContextManager`
38303835
now supports subscripting (``[]``).
38313836
See :pep:`585` and :ref:`types-genericalias`.
38323837

3833-
.. class:: AsyncContextManager(Generic[T_co])
3838+
.. versionchanged:: 3.13
3839+
Added the optional second type parameter, ``ExitT_co``.
3840+
3841+
.. class:: AsyncContextManager(Generic[T_co, AExitT_co])
38343842

38353843
Deprecated alias to :class:`contextlib.AbstractAsyncContextManager`.
38363844

3845+
The first type parameter, ``T_co``, represents the type returned by
3846+
the :meth:`~object.__aenter__` method. The optional second type parameter, ``AExitT_co``,
3847+
which defaults to ``bool | None``, represents the type returned by the
3848+
:meth:`~object.__aexit__` method.
3849+
38373850
.. versionadded:: 3.6.2
38383851

38393852
.. deprecated:: 3.9
38403853
:class:`contextlib.AbstractAsyncContextManager`
38413854
now supports subscripting (``[]``).
38423855
See :pep:`585` and :ref:`types-genericalias`.
38433856

3857+
.. versionchanged:: 3.13
3858+
Added the optional second type parameter, ``AExitT_co``.
3859+
38443860
Deprecation Timeline of Major Features
38453861
======================================
38463862

Lib/_pyrepl/reader.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -569,12 +569,16 @@ def do_cmd(self, cmd: tuple[str, list[str]]) -> None:
569569
"""`cmd` is a tuple of "event_name" and "event", which in the current
570570
implementation is always just the "buffer" which happens to be a list
571571
of single-character strings."""
572-
assert isinstance(cmd[0], str)
573572

574573
trace("received command {cmd}", cmd=cmd)
575-
command_type = self.commands.get(cmd[0], commands.invalid_command)
576-
command = command_type(self, *cmd) # type: ignore[arg-type]
574+
if isinstance(cmd[0], str):
575+
command_type = self.commands.get(cmd[0], commands.invalid_command)
576+
elif isinstance(cmd[0], type):
577+
command_type = cmd[0]
578+
else:
579+
return # nothing to do
577580

581+
command = command_type(self, *cmd) # type: ignore[arg-type]
578582
command.do()
579583

580584
self.after_command(command)

Lib/test/test_pyrepl.py

+9
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,15 @@ def test_setpos_fromxy_in_wrapped_line(self):
977977
reader.setpos_from_xy(0, 1)
978978
self.assertEqual(reader.pos, 9)
979979

980+
def test_up_arrow_after_ctrl_r(self):
981+
events = iter([
982+
Event(evt='key', data='\x12', raw=bytearray(b'\x12')),
983+
Event(evt='key', data='up', raw=bytearray(b'\x1bOA')),
984+
])
985+
986+
reader, _ = handle_all_events(events)
987+
self.assert_screen_equals(reader, "")
988+
980989

981990
if __name__ == '__main__':
982991
unittest.main()

Lib/test/test_typing.py

+19-4
Original file line numberDiff line numberDiff line change
@@ -7511,6 +7511,15 @@ def manager():
75117511
self.assertIsInstance(cm, typing.ContextManager)
75127512
self.assertNotIsInstance(42, typing.ContextManager)
75137513

7514+
def test_contextmanager_type_params(self):
7515+
cm1 = typing.ContextManager[int]
7516+
self.assertEqual(get_args(cm1), (int, bool | None))
7517+
cm2 = typing.ContextManager[int, None]
7518+
self.assertEqual(get_args(cm2), (int, types.NoneType))
7519+
7520+
type gen_cm[T1, T2] = typing.ContextManager[T1, T2]
7521+
self.assertEqual(get_args(gen_cm.__value__[int, None]), (int, types.NoneType))
7522+
75147523
def test_async_contextmanager(self):
75157524
class NotACM:
75167525
pass
@@ -7522,11 +7531,17 @@ def manager():
75227531

75237532
cm = manager()
75247533
self.assertNotIsInstance(cm, typing.AsyncContextManager)
7525-
self.assertEqual(typing.AsyncContextManager[int].__args__, (int,))
7534+
self.assertEqual(typing.AsyncContextManager[int].__args__, (int, bool | None))
75267535
with self.assertRaises(TypeError):
75277536
isinstance(42, typing.AsyncContextManager[int])
75287537
with self.assertRaises(TypeError):
7529-
typing.AsyncContextManager[int, str]
7538+
typing.AsyncContextManager[int, str, float]
7539+
7540+
def test_asynccontextmanager_type_params(self):
7541+
cm1 = typing.AsyncContextManager[int]
7542+
self.assertEqual(get_args(cm1), (int, bool | None))
7543+
cm2 = typing.AsyncContextManager[int, None]
7544+
self.assertEqual(get_args(cm2), (int, types.NoneType))
75307545

75317546

75327547
class TypeTests(BaseTestCase):
@@ -9953,7 +9968,7 @@ def test_special_attrs(self):
99539968
typing.ValuesView: 'ValuesView',
99549969
# Subscribed ABC classes
99559970
typing.AbstractSet[Any]: 'AbstractSet',
9956-
typing.AsyncContextManager[Any]: 'AsyncContextManager',
9971+
typing.AsyncContextManager[Any, Any]: 'AsyncContextManager',
99579972
typing.AsyncGenerator[Any, Any]: 'AsyncGenerator',
99589973
typing.AsyncIterable[Any]: 'AsyncIterable',
99599974
typing.AsyncIterator[Any]: 'AsyncIterator',
@@ -9963,7 +9978,7 @@ def test_special_attrs(self):
99639978
typing.ChainMap[Any, Any]: 'ChainMap',
99649979
typing.Collection[Any]: 'Collection',
99659980
typing.Container[Any]: 'Container',
9966-
typing.ContextManager[Any]: 'ContextManager',
9981+
typing.ContextManager[Any, Any]: 'ContextManager',
99679982
typing.Coroutine[Any, Any, Any]: 'Coroutine',
99689983
typing.Counter[Any]: 'Counter',
99699984
typing.DefaultDict[Any, Any]: 'DefaultDict',

Lib/typing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3783,7 +3783,7 @@ def __getattr__(attr):
37833783
obj = _alias(getattr(re, attr), 1)
37843784
elif attr in {"ContextManager", "AsyncContextManager"}:
37853785
import contextlib
3786-
obj = _alias(getattr(contextlib, f"Abstract{attr}"), 1, name=attr)
3786+
obj = _alias(getattr(contextlib, f"Abstract{attr}"), 2, name=attr, defaults=(bool | None,))
37873787
else:
37883788
raise AttributeError(f"module {__name__!r} has no attribute {attr!r}")
37893789
globals()[attr] = obj
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add an optional second type parameter to :class:`typing.ContextManager` and
2+
:class:`typing.AsyncContextManager`, representing the return types of
3+
:meth:`~object.__exit__` and :meth:`~object.__aexit__` respectively.
4+
This parameter defaults to ``bool | None``.

0 commit comments

Comments
 (0)