PyComp / python_doc_md /Intermediate /library /asyncio-graph.md
ITookAPill's picture
PyComp First Commit
9273228
|
Raw
History Blame Contribute Delete
4.98 kB
::: 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.
:::