Skip to content

Add asyncio.graph, updates to asyncio.futures (3.14) #14003

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 12, 2025
Merged
11 changes: 0 additions & 11 deletions stdlib/@tests/stubtest_allowlists/py314.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions stdlib/VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -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-
Expand Down
18 changes: 18 additions & 0 deletions stdlib/asyncio/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
Expand All @@ -32,6 +35,7 @@ else:

if sys.platform == "win32":
if sys.version_info >= (3, 14):

__all__ = (
"BaseEventLoop", # from base_events
"Server", # from base_events
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion stdlib/asyncio/futures.pyi
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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")

Expand All @@ -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: ...
26 changes: 26 additions & 0 deletions stdlib/asyncio/graph.pyi
Original file line number Diff line number Diff line change
@@ -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: ...