Spaces:
Running on Zero
Running on Zero
| ::: currentmodule | |
| asyncio | |
| ::: | |
| # Call Graph Introspection {#asyncio-graph} | |
| **Source code:** `Lib/asyncio/graph.py`{.interpreted-text role="source"} | |
| ------------------------------------------------------------------------ | |
| asyncio has powerful runtime call graph introspection utilities to trace the entire call graph of a running *coroutine* or *task*, or a suspended *future*. These utilities and the underlying machinery can be used from within a Python program or by external profilers and debuggers. | |
| ::: versionadded | |
| 3.14 | |
| ::: | |
| ::: function | |
| print_call_graph(future=None, /, \*, file=None, depth=1, limit=None) | |
| Print the async call graph for the current task or the provided `Task`{.interpreted-text role="class"} or `Future`{.interpreted-text role="class"}. | |
| This function prints entries starting from the top frame and going down towards the invocation point. | |
| The function receives an optional *future* argument. If not passed, the current running task will be used. | |
| If the function is called on *the current task*, the optional keyword-only *depth* argument can be used to skip the specified number of frames from top of the stack. | |
| If the optional keyword-only *limit* argument is provided, each call stack in the resulting graph is truncated to include at most `abs(limit)` entries. If *limit* is positive, the entries left are the closest to the invocation point. If *limit* is negative, the topmost entries are left. If *limit* is omitted or `None`, all entries are present. If *limit* is `0`, the call stack is not printed at all, only \"awaited by\" information is printed. | |
| If *file* is omitted or `None`, the function will print to `sys.stdout`{.interpreted-text role="data"}. | |
| **Example:** | |
| The following Python code: | |
| ``` python | |
| import asyncio | |
| async def test(): | |
| asyncio.print_call_graph() | |
| async def main(): | |
| async with asyncio.TaskGroup() as g: | |
| g.create_task(test(), name='test') | |
| asyncio.run(main()) | |
| ``` | |
| will print: | |
| * Task(name='test', id=0x1039f0fe0) | |
| + Call stack: | |
| | File 't2.py', line 4, in async test() | |
| + Awaited by: | |
| * Task(name='Task-1', id=0x103a5e060) | |
| + Call stack: | |
| | File 'taskgroups.py', line 107, in async TaskGroup.__aexit__() | |
| | File 't2.py', line 7, in async main() | |
| ::: | |
| ::: function | |
| format_call_graph(future=None, /, \*, depth=1, limit=None) | |
| Like `print_call_graph`{.interpreted-text role="func"}, but returns a string. If *future* is `None` and there\'s no current task, the function returns an empty string. | |
| ::: | |
| ::: function | |
| capture_call_graph(future=None, /, \*, depth=1, limit=None) | |
| Capture the async call graph for the current task or the provided `Task`{.interpreted-text role="class"} or `Future`{.interpreted-text role="class"}. | |
| The function receives an optional *future* argument. If not passed, the current running task will be used. If there\'s no current task, the function returns `None`. | |
| If the function is called on *the current task*, the optional keyword-only *depth* argument can be used to skip the specified number of frames from top of the stack. | |
| Returns a `FutureCallGraph` data class object: | |
| - `FutureCallGraph(future, call_stack, awaited_by)` | |
| > Where *future* is a reference to a `Future`{.interpreted-text role="class"} or a `Task`{.interpreted-text role="class"} (or their subclasses.) | |
| > | |
| > `call_stack` is a tuple of `FrameCallGraphEntry` objects. | |
| > | |
| > `awaited_by` is a tuple of `FutureCallGraph` objects. | |
| - `FrameCallGraphEntry(frame)` | |
| > Where *frame* is a frame object of a regular Python function in the call stack. | |
| ::: | |
| ## Low level utility functions | |
| To introspect an async call graph asyncio requires cooperation from control flow structures, such as `shield`{.interpreted-text role="func"} or `TaskGroup`{.interpreted-text role="class"}. Any time an intermediate `Future`{.interpreted-text role="class"} object with low-level APIs like `Future.add_done_callback() <asyncio.Future.add_done_callback>`{.interpreted-text role="meth"} is involved, the following two functions should be used to inform asyncio about how exactly such intermediate future objects are connected with the tasks they wrap or control. | |
| ::: function | |
| future_add_to_awaited_by(future, waiter, /) | |
| Record that *future* is awaited on by *waiter*. | |
| Both *future* and *waiter* must be instances of `Future`{.interpreted-text role="class"} or `Task`{.interpreted-text role="class"} or their subclasses, otherwise the call would have no effect. | |
| A call to `future_add_to_awaited_by()` must be followed by an eventual call to the `future_discard_from_awaited_by`{.interpreted-text role="func"} function with the same arguments. | |
| ::: | |
| ::: function | |
| future_discard_from_awaited_by(future, waiter, /) | |
| Record that *future* is no longer awaited on by *waiter*. | |
| Both *future* and *waiter* must be instances of `Future`{.interpreted-text role="class"} or `Task`{.interpreted-text role="class"} or their subclasses, otherwise the call would have no effect. | |
| ::: | |