File size: 4,982 Bytes
9273228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
::: 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.
:::