Capture the async call graph for the current task or the provided Future. The graph is represented with three data structures: * FutureCallGraph(future, call_stack, awaited_by) Where 'future' is an instance of asyncio.Future or asyncio.Task. 'call_stack' is a tuple of FrameGr
(
future: futures.Future | None = None,
/,
*,
depth: int = 1,
limit: int | None = None,
)
| 81 | |
| 82 | |
| 83 | def capture_call_graph( |
| 84 | future: futures.Future | None = None, |
| 85 | /, |
| 86 | *, |
| 87 | depth: int = 1, |
| 88 | limit: int | None = None, |
| 89 | ) -> FutureCallGraph | None: |
| 90 | """Capture the async call graph for the current task or the provided Future. |
| 91 | |
| 92 | The graph is represented with three data structures: |
| 93 | |
| 94 | * FutureCallGraph(future, call_stack, awaited_by) |
| 95 | |
| 96 | Where 'future' is an instance of asyncio.Future or asyncio.Task. |
| 97 | |
| 98 | 'call_stack' is a tuple of FrameGraphEntry objects. |
| 99 | |
| 100 | 'awaited_by' is a tuple of FutureCallGraph objects. |
| 101 | |
| 102 | * FrameCallGraphEntry(frame) |
| 103 | |
| 104 | Where 'frame' is a frame object of a regular Python function |
| 105 | in the call stack. |
| 106 | |
| 107 | Receives an optional 'future' argument. If not passed, |
| 108 | the current task will be used. If there's no current task, the function |
| 109 | returns None. |
| 110 | |
| 111 | If "capture_call_graph()" is introspecting *the current task*, the |
| 112 | optional keyword-only 'depth' argument can be used to skip the specified |
| 113 | number of frames from top of the stack. |
| 114 | |
| 115 | If the optional keyword-only 'limit' argument is provided, each call stack |
| 116 | in the resulting graph is truncated to include at most ``abs(limit)`` |
| 117 | entries. If 'limit' is positive, the entries left are the closest to |
| 118 | the invocation point. If 'limit' is negative, the topmost entries are |
| 119 | left. If 'limit' is omitted or None, all entries are present. |
| 120 | If 'limit' is 0, the call stack is not captured at all, only |
| 121 | "awaited by" information is present. |
| 122 | """ |
| 123 | |
| 124 | loop = events._get_running_loop() |
| 125 | |
| 126 | if future is not None: |
| 127 | # Check if we're in a context of a running event loop; |
| 128 | # if yes - check if the passed future is the currently |
| 129 | # running task or not. |
| 130 | if loop is None or future is not tasks.current_task(loop=loop): |
| 131 | return _build_graph_for_future(future, limit=limit) |
| 132 | # else: future is the current task, move on. |
| 133 | else: |
| 134 | if loop is None: |
| 135 | raise RuntimeError( |
| 136 | 'capture_call_graph() is called outside of a running ' |
| 137 | 'event loop and no *future* to introspect was provided') |
| 138 | future = tasks.current_task(loop=loop) |
| 139 | |
| 140 | if future is None: |
no test coverage detected
searching dependent graphs…