From fbb3258fbb75204288556c3381726853639c1235 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 6 May 2025 13:11:14 +0200 Subject: [PATCH 1/3] Abstract(Async)ContextManager.__enter__ return type defaults to Self --- stdlib/contextlib.pyi | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/stdlib/contextlib.pyi b/stdlib/contextlib.pyi index 4663b448c79c..d6d993265fce 100644 --- a/stdlib/contextlib.pyi +++ b/stdlib/contextlib.pyi @@ -31,6 +31,7 @@ if sys.version_info >= (3, 11): _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _T_io = TypeVar("_T_io", bound=IO[str] | None) +_EnterT_co = TypeVar("_EnterT_co", covariant=True, default=Self) _ExitT_co = TypeVar("_ExitT_co", covariant=True, bound=bool | None, default=bool | None) _F = TypeVar("_F", bound=Callable[..., Any]) _G_co = TypeVar("_G_co", bound=Generator[Any, Any, Any] | AsyncGenerator[Any, Any], covariant=True) @@ -46,8 +47,8 @@ _CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any, Any] | _ExitFunc) # At runtime it inherits from ABC and is not a Protocol, but it is on the # allowlist for use as a Protocol. @runtime_checkable -class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] - def __enter__(self) -> _T_co: ... +class AbstractContextManager(ABC, Protocol[_EnterT_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __enter__(self) -> _EnterT_co: ... @abstractmethod def __exit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / @@ -57,8 +58,8 @@ class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[m # At runtime it inherits from ABC and is not a Protocol, but it is on the # allowlist for use as a Protocol. @runtime_checkable -class AbstractAsyncContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] - async def __aenter__(self) -> _T_co: ... +class AbstractAsyncContextManager(ABC, Protocol[_EnterT_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + async def __aenter__(self) -> _EnterT_co: ... @abstractmethod async def __aexit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / From 9a360458244031867dd9a3a48017439f9834b556 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 6 May 2025 13:16:02 +0200 Subject: [PATCH 2/3] Add a test case --- stdlib/@tests/test_cases/check_contextlib.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/stdlib/@tests/test_cases/check_contextlib.py b/stdlib/@tests/test_cases/check_contextlib.py index 648661bca856..651503cec116 100644 --- a/stdlib/@tests/test_cases/check_contextlib.py +++ b/stdlib/@tests/test_cases/check_contextlib.py @@ -1,9 +1,17 @@ from __future__ import annotations -from contextlib import ExitStack +from contextlib import AbstractContextManager, ExitStack from typing_extensions import assert_type +class CM1(AbstractContextManager): + def __exit__(self, *args) -> None: + return None + +with CM1() as cm1: + assert_type(cm1, CM1) + + # See issue #7961 class Thing(ExitStack): pass From 683ba60ecc901f7454d036caf490fb0e835555e5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 11:17:53 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stdlib/@tests/test_cases/check_contextlib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/@tests/test_cases/check_contextlib.py b/stdlib/@tests/test_cases/check_contextlib.py index 651503cec116..63f8b27e8454 100644 --- a/stdlib/@tests/test_cases/check_contextlib.py +++ b/stdlib/@tests/test_cases/check_contextlib.py @@ -8,6 +8,7 @@ class CM1(AbstractContextManager): def __exit__(self, *args) -> None: return None + with CM1() as cm1: assert_type(cm1, CM1)