diff --git a/stdlib/@tests/stubtest_allowlists/py314.txt b/stdlib/@tests/stubtest_allowlists/py314.txt index a2fb49e599e3..09fa0b44ba66 100644 --- a/stdlib/@tests/stubtest_allowlists/py314.txt +++ b/stdlib/@tests/stubtest_allowlists/py314.txt @@ -25,28 +25,17 @@ _thread.set_name 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 bea644c67a8b..77a57cee5727 100644 --- a/stdlib/VERSIONS +++ b/stdlib/VERSIONS @@ -87,6 +87,7 @@ asynchat: 3.0-3.11 asyncio: 3.4- asyncio.exceptions: 3.8- asyncio.format_helpers: 3.7- +asyncio.graph: 3.14- asyncio.mixins: 3.10- asyncio.runners: 3.7- asyncio.staggered: 3.8- diff --git a/stdlib/asyncio/__init__.pyi b/stdlib/asyncio/__init__.pyi index c314acbea1ca..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,6 +35,7 @@ else: if sys.platform == "win32": if sys.version_info >= (3, 14): + __all__ = ( "BaseEventLoop", # from base_events "Server", # from base_events @@ -60,6 +64,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 @@ -527,6 +538,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 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..cb2cf0174995 --- /dev/null +++ b/stdlib/asyncio/graph.pyi @@ -0,0 +1,26 @@ +from _typeshed import SupportsWrite +from asyncio import Future +from dataclasses import dataclass +from types import FrameType +from typing import Any, overload + +__all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph") + +@dataclass(frozen=True) +class FrameCallGraphEntry: + frame: FrameType + +@dataclass(frozen=True) +class FutureCallGraph: + future: Future[Any] + call_stack: tuple[FrameCallGraphEntry, ...] + awaited_by: tuple[FutureCallGraph, ...] + +@overload +def capture_call_graph(future: None = None, /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ... +@overload +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: SupportsWrite[str] | None = None, depth: int = 1, limit: int | None = None +) -> None: ...