From 302358a623ec228bae7b36ff65001eed5731e4fb Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 00:04:41 -0500 Subject: [PATCH 01/12] Asyncio future/graph updates --- stdlib/@tests/stubtest_allowlists/py314.txt | 11 ---- stdlib/VERSIONS | 1 + stdlib/asyncio/__init__.pyi | 9 ++++ stdlib/asyncio/futures.pyi | 10 +++- stdlib/asyncio/graph.pyi | 56 +++++++++++++++++++++ 5 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 stdlib/asyncio/graph.pyi diff --git a/stdlib/@tests/stubtest_allowlists/py314.txt b/stdlib/@tests/stubtest_allowlists/py314.txt index 9f79c767bf7d..d3218304670c 100644 --- a/stdlib/@tests/stubtest_allowlists/py314.txt +++ b/stdlib/@tests/stubtest_allowlists/py314.txt @@ -27,28 +27,17 @@ argparse.HelpFormatter.__init__ ast.Interpolation ast.TemplateStr asyncio.__all__ -asyncio.FrameCallGraphEntry -asyncio.FutureCallGraph asyncio._AbstractEventLoopPolicy asyncio._DefaultEventLoopPolicy asyncio._get_event_loop_policy asyncio._set_event_loop_policy -asyncio.capture_call_graph asyncio.eager_task_factory -asyncio.format_call_graph -asyncio.future_add_to_awaited_by -asyncio.future_discard_from_awaited_by -asyncio.print_call_graph asyncio.events.__all__ asyncio.events.AbstractEventLoopPolicy asyncio.events.BaseDefaultEventLoopPolicy asyncio.events._AbstractEventLoopPolicy asyncio.events._get_event_loop_policy asyncio.events._set_event_loop_policy -asyncio.futures.__all__ -asyncio.futures.future_add_to_awaited_by -asyncio.futures.future_discard_from_awaited_by -asyncio.graph asyncio.tasks.eager_task_factory bdb.Bdb.__init__ bdb.Bdb.disable_current_event diff --git a/stdlib/VERSIONS b/stdlib/VERSIONS index 717cf7b4d71a..4d1a920c4965 100644 --- a/stdlib/VERSIONS +++ b/stdlib/VERSIONS @@ -85,6 +85,7 @@ array: 3.0- ast: 3.0- asynchat: 3.0-3.11 asyncio: 3.4- +asyncio.graph: 3.14- asyncio.exceptions: 3.8- asyncio.format_helpers: 3.7- asyncio.mixins: 3.10- diff --git a/stdlib/asyncio/__init__.pyi b/stdlib/asyncio/__init__.pyi index c314acbea1ca..c414c059a964 100644 --- a/stdlib/asyncio/__init__.pyi +++ b/stdlib/asyncio/__init__.pyi @@ -32,6 +32,8 @@ else: if sys.platform == "win32": if sys.version_info >= (3, 14): + from .graph import * + __all__ = ( "BaseEventLoop", # from base_events "Server", # from base_events @@ -60,6 +62,13 @@ if sys.platform == "win32": "Future", # from futures "wrap_future", # from futures "isfuture", # from futures + "future_discard_from_awaited_by", # from futures + "future_add_to_awaited_by", # from futures + "capture_call_graph", # from graph + "format_call_graph", # from graph + "print_call_graph", # from graph + "FrameCallGraphEntry", # from graph + "FutureCallGraph", # from graph "Lock", # from locks "Event", # from locks "Condition", # from locks diff --git a/stdlib/asyncio/futures.pyi b/stdlib/asyncio/futures.pyi index cb2785012fb2..a63de66f02e6 100644 --- a/stdlib/asyncio/futures.pyi +++ b/stdlib/asyncio/futures.pyi @@ -1,3 +1,4 @@ +import sys from _asyncio import Future as Future from concurrent.futures._base import Future as _ConcurrentFuture from typing import Any, TypeVar @@ -6,7 +7,10 @@ from typing_extensions import TypeIs from .events import AbstractEventLoop # Keep asyncio.__all__ updated with any changes to __all__ here -__all__ = ("Future", "wrap_future", "isfuture") +if sys.version_info >= (3, 14): + __all__ = ("Future", "wrap_future", "isfuture", "future_discard_from_awaited_by", "future_add_to_awaited_by") +else: + __all__ = ("Future", "wrap_future", "isfuture") _T = TypeVar("_T") @@ -15,3 +19,7 @@ _T = TypeVar("_T") # That's why the import order is reversed. def isfuture(obj: object) -> TypeIs[Future[Any]]: ... def wrap_future(future: _ConcurrentFuture[_T] | Future[_T], *, loop: AbstractEventLoop | None = None) -> Future[_T]: ... + +if sys.version_info >= (3, 14): + def future_discard_from_awaited_by(future: Future[Any], waiter: Future[Any], /) -> None: ... + def future_add_to_awaited_by(future: Future[Any], waiter: Future[Any], /) -> None: ... diff --git a/stdlib/asyncio/graph.pyi b/stdlib/asyncio/graph.pyi new file mode 100644 index 000000000000..f43e50a14df8 --- /dev/null +++ b/stdlib/asyncio/graph.pyi @@ -0,0 +1,56 @@ +from asyncio import Future +from dataclasses import dataclass +from types import FrameType +from typing import Any, TextIO, TypeVar, overload + +_T = TypeVar("_T") + +__all__ = ( + "capture_call_graph", + "format_call_graph", + "print_call_graph", + "FrameCallGraphEntry", + "FutureCallGraph", +) + +@dataclass(frozen=True, slots=True) +class FrameCallGraphEntry: + frame: FrameType + +@dataclass(frozen=True, slots=True) +class FutureCallGraph[_T]: + future: Future[_T] + call_stack: tuple[FrameCallGraphEntry, ...] + awaited_by: tuple[FutureCallGraph[Any], ...] + +@overload +def capture_call_graph( + future: None = None, + /, + *, + depth: int = 1, + limit: int | None = None, +) -> FutureCallGraph[Any] | None: ... +@overload +def capture_call_graph( + future: Future[_T], + /, + *, + depth: int = 1, + limit: int | None = None, +) -> FutureCallGraph[_T] | None: ... +def format_call_graph( + future: Future[_T] | None = None, + /, + *, + depth: int = 1, + limit: int | None = None, +) -> str: ... +def print_call_graph( + future: Future[_T] | None = None, + /, + *, + file: TextIO | None = None, + depth: int = 1, + limit: int | None = None, +) -> None: ... From 278728a914d5c45796807cad8dc64681cc30ee2a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 05:07:06 +0000 Subject: [PATCH 02/12] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stdlib/asyncio/graph.pyi | 39 +++++---------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/stdlib/asyncio/graph.pyi b/stdlib/asyncio/graph.pyi index f43e50a14df8..5b518f05e029 100644 --- a/stdlib/asyncio/graph.pyi +++ b/stdlib/asyncio/graph.pyi @@ -5,13 +5,7 @@ from typing import Any, TextIO, TypeVar, overload _T = TypeVar("_T") -__all__ = ( - "capture_call_graph", - "format_call_graph", - "print_call_graph", - "FrameCallGraphEntry", - "FutureCallGraph", -) +__all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph") @dataclass(frozen=True, slots=True) class FrameCallGraphEntry: @@ -24,33 +18,10 @@ class FutureCallGraph[_T]: awaited_by: tuple[FutureCallGraph[Any], ...] @overload -def capture_call_graph( - future: None = None, - /, - *, - depth: int = 1, - limit: int | None = None, -) -> FutureCallGraph[Any] | None: ... +def capture_call_graph(future: None = None, /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph[Any] | None: ... @overload -def capture_call_graph( - future: Future[_T], - /, - *, - depth: int = 1, - limit: int | None = None, -) -> FutureCallGraph[_T] | None: ... -def format_call_graph( - future: Future[_T] | None = None, - /, - *, - depth: int = 1, - limit: int | None = None, -) -> str: ... +def capture_call_graph(future: Future[_T], /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph[_T] | None: ... +def format_call_graph(future: Future[_T] | None = None, /, *, depth: int = 1, limit: int | None = None) -> str: ... def print_call_graph( - future: Future[_T] | None = None, - /, - *, - file: TextIO | None = None, - depth: int = 1, - limit: int | None = None, + future: Future[_T] | None = None, /, *, file: TextIO | None = None, depth: int = 1, limit: int | None = None ) -> None: ... From 06273651b0e645c18f3c1603daeae058748f7ff9 Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 00:07:13 -0500 Subject: [PATCH 03/12] Update __all__ --- stdlib/asyncio/__init__.pyi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stdlib/asyncio/__init__.pyi b/stdlib/asyncio/__init__.pyi index c414c059a964..88f68248787e 100644 --- a/stdlib/asyncio/__init__.pyi +++ b/stdlib/asyncio/__init__.pyi @@ -536,6 +536,13 @@ else: "Future", # from futures "wrap_future", # from futures "isfuture", # from futures + "future_discard_from_awaited_by", # from futures + "future_add_to_awaited_by", # from futures + "capture_call_graph", # from graph + "format_call_graph", # from graph + "print_call_graph", # from graph + "FrameCallGraphEntry", # from graph + "FutureCallGraph", # from graph "Lock", # from locks "Event", # from locks "Condition", # from locks From dc7e99b9c914c00b303378d7e58e58c81540f58f Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 00:12:08 -0500 Subject: [PATCH 04/12] Tweak --- stdlib/asyncio/graph.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/asyncio/graph.pyi b/stdlib/asyncio/graph.pyi index 5b518f05e029..41a375fc2209 100644 --- a/stdlib/asyncio/graph.pyi +++ b/stdlib/asyncio/graph.pyi @@ -7,11 +7,11 @@ _T = TypeVar("_T") __all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph") -@dataclass(frozen=True, slots=True) +@dataclass(frozen=True) class FrameCallGraphEntry: frame: FrameType -@dataclass(frozen=True, slots=True) +@dataclass(frozen=True) class FutureCallGraph[_T]: future: Future[_T] call_stack: tuple[FrameCallGraphEntry, ...] From 57c02a9aadad646e154efa72bbc5b1e45dfda7c0 Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 00:14:05 -0500 Subject: [PATCH 05/12] Fix --- stdlib/VERSIONS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/VERSIONS b/stdlib/VERSIONS index 4d1a920c4965..5fa8b72d6a2b 100644 --- a/stdlib/VERSIONS +++ b/stdlib/VERSIONS @@ -85,8 +85,8 @@ array: 3.0- ast: 3.0- asynchat: 3.0-3.11 asyncio: 3.4- -asyncio.graph: 3.14- asyncio.exceptions: 3.8- +asyncio.graph: 3.14- asyncio.format_helpers: 3.7- asyncio.mixins: 3.10- asyncio.runners: 3.7- From 6725d39444554d5b2967a4dfcdfa7a17332cffe4 Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 00:16:42 -0500 Subject: [PATCH 06/12] Fix --- stdlib/asyncio/__init__.pyi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/asyncio/__init__.pyi b/stdlib/asyncio/__init__.pyi index 88f68248787e..f903ddad9976 100644 --- a/stdlib/asyncio/__init__.pyi +++ b/stdlib/asyncio/__init__.pyi @@ -508,6 +508,8 @@ if sys.platform == "win32": ) else: if sys.version_info >= (3, 14): + from .graph import * + __all__ = ( "BaseEventLoop", # from base_events "Server", # from base_events From b0004f9fb38fc72de39acd9359d60dfc8c968cd4 Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 00:18:37 -0500 Subject: [PATCH 07/12] Fix --- stdlib/VERSIONS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/VERSIONS b/stdlib/VERSIONS index 5fa8b72d6a2b..24385aba274c 100644 --- a/stdlib/VERSIONS +++ b/stdlib/VERSIONS @@ -86,8 +86,8 @@ ast: 3.0- asynchat: 3.0-3.11 asyncio: 3.4- asyncio.exceptions: 3.8- -asyncio.graph: 3.14- asyncio.format_helpers: 3.7- +asyncio.graph: 3.14- asyncio.mixins: 3.10- asyncio.runners: 3.7- asyncio.staggered: 3.8- From 8bfa488eba8de961153ce297fa47e23bce1d274e Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 00:24:48 -0500 Subject: [PATCH 08/12] Use Generic --- stdlib/asyncio/graph.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/asyncio/graph.pyi b/stdlib/asyncio/graph.pyi index 41a375fc2209..e465a578d515 100644 --- a/stdlib/asyncio/graph.pyi +++ b/stdlib/asyncio/graph.pyi @@ -1,7 +1,7 @@ from asyncio import Future from dataclasses import dataclass from types import FrameType -from typing import Any, TextIO, TypeVar, overload +from typing import Any, Generic, TextIO, TypeVar, overload _T = TypeVar("_T") @@ -12,7 +12,7 @@ class FrameCallGraphEntry: frame: FrameType @dataclass(frozen=True) -class FutureCallGraph[_T]: +class FutureCallGraph(Generic[_T]): future: Future[_T] call_stack: tuple[FrameCallGraphEntry, ...] awaited_by: tuple[FutureCallGraph[Any], ...] From b13953183f594a30f9308d05ed6e3d8e3b441066 Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 13:27:24 -0500 Subject: [PATCH 09/12] Address comments --- stdlib/asyncio/graph.pyi | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/stdlib/asyncio/graph.pyi b/stdlib/asyncio/graph.pyi index e465a578d515..a03133bcf88d 100644 --- a/stdlib/asyncio/graph.pyi +++ b/stdlib/asyncio/graph.pyi @@ -1,9 +1,8 @@ from asyncio import Future from dataclasses import dataclass from types import FrameType -from typing import Any, Generic, TextIO, TypeVar, overload +from typing import Any, TextIO, overload -_T = TypeVar("_T") __all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph") @@ -12,16 +11,16 @@ class FrameCallGraphEntry: frame: FrameType @dataclass(frozen=True) -class FutureCallGraph(Generic[_T]): - future: Future[_T] +class FutureCallGraph: + future: Future[Any] call_stack: tuple[FrameCallGraphEntry, ...] - awaited_by: tuple[FutureCallGraph[Any], ...] + awaited_by: tuple[FutureCallGraph, ...] @overload -def capture_call_graph(future: None = None, /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph[Any] | None: ... +def capture_call_graph(future: None = None, /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ... @overload -def capture_call_graph(future: Future[_T], /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph[_T] | None: ... -def format_call_graph(future: Future[_T] | None = None, /, *, depth: int = 1, limit: int | None = None) -> str: ... +def capture_call_graph(future: Future[Any], /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ... +def format_call_graph(future: Future[Any] | None = None, /, *, depth: int = 1, limit: int | None = None) -> str: ... def print_call_graph( - future: Future[_T] | None = None, /, *, file: TextIO | None = None, depth: int = 1, limit: int | None = None + future: Future[Any] | None = None, /, *, file: TextIO | None = None, depth: int = 1, limit: int | None = None ) -> None: ... From 4928caab073679364053d64d5cdf6134e17319b9 Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 13:28:32 -0500 Subject: [PATCH 10/12] Switch to `SupportsWrite` --- stdlib/asyncio/graph.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/asyncio/graph.pyi b/stdlib/asyncio/graph.pyi index a03133bcf88d..3115a33211c7 100644 --- a/stdlib/asyncio/graph.pyi +++ b/stdlib/asyncio/graph.pyi @@ -1,8 +1,8 @@ from asyncio import Future from dataclasses import dataclass from types import FrameType -from typing import Any, TextIO, overload - +from typing import Any, overload +from _typeshed import SupportsWrite __all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph") @@ -22,5 +22,5 @@ def capture_call_graph(future: None = None, /, *, depth: int = 1, limit: int | N def capture_call_graph(future: Future[Any], /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ... def format_call_graph(future: Future[Any] | None = None, /, *, depth: int = 1, limit: int | None = None) -> str: ... def print_call_graph( - future: Future[Any] | None = None, /, *, file: TextIO | None = None, depth: int = 1, limit: int | None = None + future: Future[Any] | None = None, /, *, file: SupportsWrite[str] | None = None, depth: int = 1, limit: int | None = None ) -> None: ... From 2ff3b9fd0bc5f58d24316621a55c655e0ad52fe0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 18:30:40 +0000 Subject: [PATCH 11/12] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stdlib/asyncio/graph.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/asyncio/graph.pyi b/stdlib/asyncio/graph.pyi index 3115a33211c7..cb2cf0174995 100644 --- a/stdlib/asyncio/graph.pyi +++ b/stdlib/asyncio/graph.pyi @@ -1,8 +1,8 @@ +from _typeshed import SupportsWrite from asyncio import Future from dataclasses import dataclass from types import FrameType from typing import Any, overload -from _typeshed import SupportsWrite __all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph") From d51860149815a417dd13d77726a852dd251d0034 Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Sun, 11 May 2025 13:32:10 -0500 Subject: [PATCH 12/12] Address comment --- stdlib/asyncio/__init__.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/asyncio/__init__.pyi b/stdlib/asyncio/__init__.pyi index f903ddad9976..f9118608060e 100644 --- a/stdlib/asyncio/__init__.pyi +++ b/stdlib/asyncio/__init__.pyi @@ -21,6 +21,9 @@ from .tasks import * from .threads import * from .transports import * +if sys.version_info >= (3, 14): + from .graph import * + if sys.version_info >= (3, 11): from .taskgroups import * from .timeouts import * @@ -32,7 +35,6 @@ else: if sys.platform == "win32": if sys.version_info >= (3, 14): - from .graph import * __all__ = ( "BaseEventLoop", # from base_events @@ -508,8 +510,6 @@ if sys.platform == "win32": ) else: if sys.version_info >= (3, 14): - from .graph import * - __all__ = ( "BaseEventLoop", # from base_events "Server", # from base_events