]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-151213: Document asyncio debugging tools (#151392)
authorBartosz Sławecki <bartosz@ilikepython.com>
Wed, 8 Jul 2026 23:17:05 +0000 (01:17 +0200)
committerGitHub <noreply@github.com>
Wed, 8 Jul 2026 23:17:05 +0000 (00:17 +0100)
Doc/library/asyncio-graph.rst
Doc/library/asyncio-tools.rst [new file with mode: 0644]
Doc/library/asyncio.rst

index 5f642a32bf75c24a31d90e1e9a355c732a9a36ac..1a8cdcf6a9e8cbaee8046c23fc28a49dae497b69 100644 (file)
@@ -4,7 +4,7 @@
 .. _asyncio-graph:
 
 ========================
-Call Graph Introspection
+Call graph introspection
 ========================
 
 **Source code:** :source:`Lib/asyncio/graph.py`
@@ -17,6 +17,12 @@ a suspended *future*.  These utilities and the underlying machinery
 can be used from within a Python program or by external profilers
 and debuggers.
 
+.. seealso::
+
+   :ref:`asyncio-introspection-tools`
+      Command-line tools for inspecting tasks in another running Python
+      process.
+
 .. versionadded:: 3.14
 
 
diff --git a/Doc/library/asyncio-tools.rst b/Doc/library/asyncio-tools.rst
new file mode 100644 (file)
index 0000000..1782640
--- /dev/null
@@ -0,0 +1,157 @@
+.. currentmodule:: asyncio
+
+.. _asyncio-introspection-tools:
+
+================================
+Command-line introspection tools
+================================
+
+**Source code:** :source:`Lib/asyncio/tools.py`
+
+-------------------------------------
+
+The :mod:`!asyncio` module can be invoked as a script via ``python -m
+asyncio`` to inspect the task graph of another running Python process without
+modifying it or restarting it.  The :mod:`!asyncio.tools` submodule implements
+this interface.
+
+The following commands inspect the process identified by ``PID``:
+
+.. code-block:: shell-session
+
+   $ python -m asyncio pstree [--retries N] PID
+   $ python -m asyncio ps [--retries N] PID
+
+The commands read the target process state without executing any code in it.
+They are only available on supported platforms and may require permission to
+inspect another process.  See the :ref:`permission-requirements <permission-requirements>` for details.
+
+.. seealso::
+
+   :ref:`asyncio-graph`
+      Programmatic APIs for inspecting the async call graph of a task or
+      future in the current process.
+
+The command examples below use this program, which creates a task hierarchy
+suitable for inspection and prints its process ID:
+
+.. code-block:: python
+   :caption: example.py
+
+   import asyncio
+   import os
+
+   async def play(track):
+       await asyncio.sleep(3600)
+       print(f"🎵 Finished: {track}")
+
+   async def album(name, tracks):
+       async with asyncio.TaskGroup() as tg:
+           for track in tracks:
+               tg.create_task(play(track), name=track)
+
+   async def main():
+       print(f"PID: {os.getpid()}")
+       async with asyncio.TaskGroup() as tg:
+           tg.create_task(
+               album("Sundowning", ["TNDNBTG", "Levitate"]),
+               name="Sundowning",
+           )
+           tg.create_task(
+               album("TMBTE", ["DYWTYLM", "Aqua Regia"]),
+               name="TMBTE",
+           )
+
+   asyncio.run(main())
+
+Run the program in one terminal and leave it running:
+
+.. code-block:: shell-session
+
+   $ python example.py
+   PID: 12345
+
+Then pass the printed process ID to the commands from another terminal.
+Thread IDs, task IDs, file paths, and line numbers vary between runs and
+source layouts.
+
+.. versionadded:: 3.14
+
+Command-line options
+====================
+
+.. option:: pstree PID
+
+   Display task and coroutine relationships as a tree.  Each task is shown
+   with its full coroutine stack, nested under the task (if any) that is
+   awaiting it.  This subcommand is useful for quickly identifying which branch
+   of a task hierarchy is blocked and where in its coroutine stack execution
+   has paused:
+
+   .. code-block:: shell-session
+
+      $ python -m asyncio pstree 12345
+      â””── (T) Task-1
+          â””──  main example.py:12
+              â””──  TaskGroup.__aexit__ Lib/asyncio/taskgroups.py:75
+                  â””──  TaskGroup._aexit Lib/asyncio/taskgroups.py:124
+                      â”œâ”€â”€ (T) Sundowning
+                      â”‚   â””──  album example.py:7
+                      â”‚       â””──  TaskGroup.__aexit__ Lib/asyncio/taskgroups.py:75
+                      â”‚           â””──  TaskGroup._aexit Lib/asyncio/taskgroups.py:124
+                      â”‚               â”œâ”€â”€ (T) TNDNBTG
+                      â”‚               â”‚   â””──  play example.py:4
+                      â”‚               â”‚       â””──  sleep Lib/asyncio/tasks.py:702
+                      â”‚               â””── (T) Levitate
+                      â”‚                   â””──  play example.py:4
+                      â”‚                       â””──  sleep Lib/asyncio/tasks.py:702
+                      â””── (T) TMBTE
+                          â””──  album example.py:7
+                              â””──  TaskGroup.__aexit__ Lib/asyncio/taskgroups.py:75
+                                  â””──  TaskGroup._aexit Lib/asyncio/taskgroups.py:124
+                                      â”œâ”€â”€ (T) DYWTYLM
+                                      â”‚   â””──  play example.py:4
+                                      â”‚       â””──  sleep Lib/asyncio/tasks.py:702
+                                      â””── (T) Aqua Regia
+                                          â””──  play example.py:4
+                                              â””──  sleep Lib/asyncio/tasks.py:702
+
+   If the await graph contains a cycle, ``pstree`` reports an error instead
+   of printing a tree.  A cycle in the await graph is unusual and typically
+   indicates a programming error:
+
+   .. code-block:: shell-session
+
+      $ python -m asyncio pstree 12345
+      ERROR: await-graph contains cycles - cannot print a tree!
+
+      cycle: Task-2 â†’ Task-3 â†’ Task-2
+
+.. option:: ps PID
+
+   Display a flat table of all pending tasks in the process *PID*.  Each row
+   shows the event-loop thread ID, task ID and name, coroutine stack, and the
+   awaiting task's stack, name, and ID, if any.
+
+   This subcommand prints all tasks regardless of whether the await graph
+   contains cycles:
+
+   .. code-block:: shell-session
+
+      $ python -m asyncio ps 12345
+      tid        task id              task name            coroutine stack                                    awaiter chain                                      awaiter name    awaiter id
+      ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+      18445801   0x10a456060          Task-1               TaskGroup._aexit -> TaskGroup.__aexit__ -> main                                                                       0x0
+      18445801   0x10a439f60          Sundowning           TaskGroup._aexit -> TaskGroup.__aexit__ -> album   TaskGroup._aexit -> TaskGroup.__aexit__ -> main    Task-1          0x10a456060
+      18445801   0x10a439d70          TMBTE                TaskGroup._aexit -> TaskGroup.__aexit__ -> album   TaskGroup._aexit -> TaskGroup.__aexit__ -> main    Task-1          0x10a456060
+      18445801   0x10a2a3a80          TNDNBTG              sleep -> play                                      TaskGroup._aexit -> TaskGroup.__aexit__ -> album   Sundowning      0x10a439f60
+      18445801   0x10a2a38a0          Levitate             sleep -> play                                      TaskGroup._aexit -> TaskGroup.__aexit__ -> album   Sundowning      0x10a439f60
+      18445801   0x10a2d7150          DYWTYLM              sleep -> play                                      TaskGroup._aexit -> TaskGroup.__aexit__ -> album   TMBTE           0x10a439d70
+      18445801   0x10a6bdaa0          Aqua Regia           sleep -> play                                      TaskGroup._aexit -> TaskGroup.__aexit__ -> album   TMBTE           0x10a439d70
+
+.. option:: --retries N
+
+   Retry failed attempts to inspect the target process up to *N* times.  This
+   can help when the target process changes while its state is being read.
+
+   .. versionadded:: 3.15
index 4ae6d1e43f2ac7bc9f9a4663cfcea4bd765be30f..956b00f0873a0d1e1f5944939c60a78fc4345c52 100644 (file)
@@ -47,6 +47,13 @@ asyncio provides a set of **high-level** APIs to:
 
 * :ref:`synchronize <asyncio-sync>` concurrent code;
 
+For **introspection**, asyncio provides APIs and tools for:
+
+* inspecting the :ref:`async call graph <asyncio-graph>` of tasks and futures;
+
+* inspecting tasks in another running Python process with
+  :ref:`command-line tools <asyncio-introspection-tools>`;
+
 Additionally, there are **low-level** APIs for
 *library and framework developers* to:
 
@@ -108,7 +115,13 @@ for full functionality and the latest features.
    asyncio-subprocess.rst
    asyncio-queue.rst
    asyncio-exceptions.rst
+
+.. toctree::
+   :caption: Introspection APIs
+   :maxdepth: 1
+
    asyncio-graph.rst
+   asyncio-tools.rst
 
 .. toctree::
    :caption: Low-level APIs