146 lines
4.7 KiB
ReStructuredText
146 lines
4.7 KiB
ReStructuredText
.. 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.
|