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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
.. currentmodule:: asyncio
.. _asyncio-graph:
========================
Call Graph Introspection
========================
**Source code:** :source:`Lib/asyncio/graph.py`
-------------------------------------
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
:class:`Task` or :class:`Future`.
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 :data:`sys.stdout`.
**Example:**
The following Python code:
.. code-block:: 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 :func:`print_call_graph`, 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
:class:`Task` or :class:`Future`.
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 :class:`Future` or
a :class:`Task` (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 :func:`shield` or :class:`TaskGroup`.
Any time an intermediate :class:`Future` object with low-level APIs like
:meth:`Future.add_done_callback() <asyncio.Future.add_done_callback>` 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
:class:`Future` or :class:`Task` 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 :func:`future_discard_from_awaited_by` 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
:class:`Future` or :class:`Task` or their subclasses, otherwise
the call would have no effect.
|