Skip to content

Commit 22a4cf0

Browse files
committed
Document asyncio introspection tools
1 parent 7a014f4 commit 22a4cf0

3 files changed

Lines changed: 154 additions & 1 deletion

File tree

Doc/library/asyncio-graph.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.. _asyncio-graph:
55

66
========================
7-
Call Graph Introspection
7+
Call graph introspection
88
========================
99

1010
**Source code:** :source:`Lib/asyncio/graph.py`
@@ -17,6 +17,12 @@ a suspended *future*. These utilities and the underlying machinery
1717
can be used from within a Python program or by external profilers
1818
and debuggers.
1919

20+
.. seealso::
21+
22+
:ref:`asyncio-introspection-tools`
23+
Command-line tools for inspecting tasks in another running Python
24+
process.
25+
2026
.. versionadded:: 3.14
2127

2228

Doc/library/asyncio-tools.rst

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
.. currentmodule:: asyncio
2+
3+
.. _asyncio-introspection-tools:
4+
5+
================================
6+
Command-line introspection tools
7+
================================
8+
9+
**Source code:** :source:`Lib/asyncio/tools.py`
10+
11+
The :mod:`!asyncio` module can be executed as a script to inspect asyncio
12+
tasks in another Python process:
13+
14+
.. code-block:: shell-session
15+
16+
$ python -m asyncio ps [--retries N] PID
17+
$ python -m asyncio pstree [--retries N] PID
18+
19+
``PID`` is the process ID of the Python process to inspect. The commands use
20+
Python's :ref:`remote debugging support <remote-debugging>` to read the target
21+
process state, but do not execute code in the target process. They are only
22+
available on supported platforms and may require permission to inspect another
23+
process. See :ref:`permission-requirements` for details.
24+
25+
.. seealso::
26+
27+
:ref:`asyncio-graph`
28+
Programmatic APIs for inspecting the async call graph of a task or
29+
future in the current process.
30+
31+
The command examples below use this program, which creates a task hierarchy
32+
suitable for inspection and prints its process ID:
33+
34+
.. code-block:: python
35+
36+
import asyncio
37+
import os
38+
39+
async def play(track):
40+
await asyncio.sleep(3600)
41+
print(f"🎵 Finished: {track}")
42+
43+
async def album(name, tracks):
44+
async with asyncio.TaskGroup() as tg:
45+
for track in tracks:
46+
tg.create_task(play(track), name=track)
47+
48+
async def main():
49+
print(f"PID: {os.getpid()}", flush=True)
50+
async with asyncio.TaskGroup() as tg:
51+
tg.create_task(
52+
album("Sundowning", ["TNDNBTG", "Levitate"]),
53+
name="Sundowning",
54+
)
55+
tg.create_task(
56+
album("TMBTE", ["DYWTYLM", "Aqua Regia"]),
57+
name="TMBTE",
58+
)
59+
60+
asyncio.run(main())
61+
62+
Run the program in one terminal and leave it running:
63+
64+
.. code-block:: shell-session
65+
66+
$ python example.py
67+
PID: 12345
68+
69+
Then pass the printed process ID to the commands from another terminal.
70+
Thread IDs, task IDs, file paths, and line numbers vary between runs and
71+
source layouts.
72+
73+
.. versionadded:: 3.14
74+
75+
Command-line options
76+
====================
77+
78+
.. option:: ps PID
79+
80+
Display a table of pending tasks in the process *PID*. The table includes
81+
the thread ID, task ID, task name, coroutine stack, awaiter chain, awaiter
82+
name, and awaiter ID:
83+
84+
.. code-block:: shell-session
85+
86+
$ python -m asyncio ps 12345
87+
tid task id task name coroutine stack awaiter chain awaiter name awaiter id
88+
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
89+
18445801 0x10a456060 Task-1 TaskGroup._aexit -> TaskGroup.__aexit__ -> main 0x0
90+
18445801 0x10a439f60 Sundowning TaskGroup._aexit -> TaskGroup.__aexit__ -> album TaskGroup._aexit -> TaskGroup.__aexit__ -> main Task-1 0x10a456060
91+
18445801 0x10a439d70 TMBTE TaskGroup._aexit -> TaskGroup.__aexit__ -> album TaskGroup._aexit -> TaskGroup.__aexit__ -> main Task-1 0x10a456060
92+
18445801 0x10a2a3a80 TNDNBTG sleep -> play TaskGroup._aexit -> TaskGroup.__aexit__ -> album Sundowning 0x10a439f60
93+
18445801 0x10a2a38a0 Levitate sleep -> play TaskGroup._aexit -> TaskGroup.__aexit__ -> album Sundowning 0x10a439f60
94+
18445801 0x10a2d7150 DYWTYLM sleep -> play TaskGroup._aexit -> TaskGroup.__aexit__ -> album TMBTE 0x10a439d70
95+
18445801 0x10a6bdaa0 Aqua Regia sleep -> play TaskGroup._aexit -> TaskGroup.__aexit__ -> album TMBTE 0x10a439d70
96+
97+
.. option:: pstree PID
98+
99+
Display the same task and coroutine relationships as a tree:
100+
101+
.. code-block:: shell-session
102+
103+
$ python -m asyncio pstree 12345
104+
└── (T) Task-1
105+
└── main example.py:12
106+
└── TaskGroup.__aexit__ Lib/asyncio/taskgroups.py:75
107+
└── TaskGroup._aexit Lib/asyncio/taskgroups.py:124
108+
├── (T) Sundowning
109+
│ └── album example.py:7
110+
│ └── TaskGroup.__aexit__ Lib/asyncio/taskgroups.py:75
111+
│ └── TaskGroup._aexit Lib/asyncio/taskgroups.py:124
112+
│ ├── (T) TNDNBTG
113+
│ │ └── play example.py:4
114+
│ │ └── sleep Lib/asyncio/tasks.py:702
115+
│ └── (T) Levitate
116+
│ └── play example.py:4
117+
│ └── sleep Lib/asyncio/tasks.py:702
118+
└── (T) TMBTE
119+
└── album example.py:7
120+
└── TaskGroup.__aexit__ Lib/asyncio/taskgroups.py:75
121+
└── TaskGroup._aexit Lib/asyncio/taskgroups.py:124
122+
├── (T) DYWTYLM
123+
│ └── play example.py:4
124+
│ └── sleep Lib/asyncio/tasks.py:702
125+
└── (T) Aqua Regia
126+
└── play example.py:4
127+
└── sleep Lib/asyncio/tasks.py:702
128+
129+
.. option:: --retries N
130+
131+
Retry failed attempts to inspect the target process up to *N* times. See
132+
:ref:`sampling-efficiency` for details about failed process memory reads.
133+
134+
.. versionadded:: 3.15

Doc/library/asyncio.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ asyncio provides a set of **high-level** APIs to:
4747

4848
* :ref:`synchronize <asyncio-sync>` concurrent code;
4949

50+
There are also **introspection** APIs and tools for:
51+
52+
* inspecting the :ref:`async call graph <asyncio-graph>` of tasks and futures;
53+
54+
* inspecting tasks in another running Python process with
55+
:ref:`command-line tools <asyncio-introspection-tools>`;
56+
5057
Additionally, there are **low-level** APIs for
5158
*library and framework developers* to:
5259

@@ -108,7 +115,13 @@ for full functionality and the latest features.
108115
asyncio-subprocess.rst
109116
asyncio-queue.rst
110117
asyncio-exceptions.rst
118+
119+
.. toctree::
120+
:caption: Introspection APIs
121+
:maxdepth: 1
122+
111123
asyncio-graph.rst
124+
asyncio-tools.rst
112125

113126
.. toctree::
114127
:caption: Low-level APIs

0 commit comments

Comments
 (0)