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]