https://github.com/python/cpython/commit/b2d8db1ac818e74ffe666146335e9e6c7f1f3f56
commit: b2d8db1ac818e74ffe666146335e9e6c7f1f3f56
branch: main
author: Bartosz SÅ‚awecki <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-07-09T00:17:05+01:00
summary:

gh-151213: Document asyncio debugging tools (#151392)

files:
A Doc/library/asyncio-tools.rst
M Doc/library/asyncio-graph.rst
M Doc/library/asyncio.rst

diff --git a/Doc/library/asyncio-graph.rst b/Doc/library/asyncio-graph.rst
index 5f642a32bf75c2..1a8cdcf6a9e8cb 100644
--- a/Doc/library/asyncio-graph.rst
+++ b/Doc/library/asyncio-graph.rst
@@ -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
index 00000000000000..1782640e83f53a
--- /dev/null
+++ b/Doc/library/asyncio-tools.rst
@@ -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
diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst
index 4ae6d1e43f2ac7..956b00f0873a0d 100644
--- a/Doc/library/asyncio.rst
+++ b/Doc/library/asyncio.rst
@@ -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

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to