| 
									
										
										
										
											2025-01-22 08:25:29 -08:00
										 |  |  | .. 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.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-11 19:16:07 +02:00
										 |  |  | .. versionadded:: 3.14
 | 
					
						
							| 
									
										
										
										
											2025-01-22 08:25:29 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .. 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:
 | 
					
						
							| 
									
										
										
										
											2025-01-23 21:08:51 +03:00
										 |  |  |               g.create_task(test(), name='test')
 | 
					
						
							| 
									
										
										
										
											2025-01-22 08:25:29 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       asyncio.run(main())
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    will print::
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-23 21:08:51 +03:00
										 |  |  |       * Task(name='test', id=0x1039f0fe0)
 | 
					
						
							| 
									
										
										
										
											2025-01-22 08:25:29 -08:00
										 |  |  |       + 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.
 |