Skip to content

Commit 2dd3452

Browse files
committed
Add news entry
1 parent b6cb609 commit 2dd3452

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

Doc/whatsnew/3.14.rst

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,96 @@ configuration mechanisms).
508508
.. seealso::
509509
:pep:`741`.
510510

511+
.. _whatsnew314-asyncio-introspection
512+
513+
Asyncio introspection capabilities
514+
----------------------------------
515+
516+
Added a new command-line interface to inspect running Python processes using
517+
asynchronous tasks, available via:
518+
519+
.. code-block:: bash
520+
python -m asyncio.tools [--tree] PID
521+
522+
This tool inspects the given process ID (PID) and displays information about
523+
currently running asyncio tasks. By default, it outputs a task table: a flat
524+
listing of all tasks, their names, their coroutine stacks, and which tasks are
525+
awaiting them.
526+
527+
With the ``--tree`` option, it instead renders a visual async call tree,
528+
showing coroutine relationships in a hierarchical format. This command is
529+
particularly useful for debugging long-running or stuck asynchronous programs.
530+
It can help developers quickly identify where a program is blocked, what tasks
531+
are pending, and how coroutines are chained together.
532+
533+
For example given this code:
534+
535+
.. code-block:: python
536+
537+
import asyncio
538+
539+
async def sleeper(name, delay):
540+
await asyncio.sleep(delay)
541+
print(f"{name} is done sleeping.")
542+
543+
async def inner(name):
544+
await asyncio.sleep(0.1)
545+
await sleeper(name, 1)
546+
547+
async def task_group(name):
548+
await asyncio.gather(
549+
inner(f"{name}-1"),
550+
inner(f"{name}-2"),
551+
)
552+
553+
async def main():
554+
# Start two separate task groups
555+
t1 = asyncio.create_task(task_group("groupA"))
556+
t2 = asyncio.create_task(task_group("groupB"))
557+
await t1
558+
await t2
559+
560+
if __name__ == "__main__":
561+
asyncio.run(main())
562+
563+
Executing the new tool on the running process will yield a table like this:
564+
565+
.. code-block:: bash
566+
567+
python -m asyncio.tools 12345
568+
569+
tid task id task name coroutine chain awaiter name awaiter id
570+
---------------------------------------------------------------------------------------------------------------------------------------
571+
6826911 0x200013c0220 Task-2 main Task-1 0x200013b0020
572+
6826911 0x200013c0620 Task-4 task_group Task-2 0x200013c0220
573+
6826911 0x200013c0820 Task-5 task_group Task-2 0x200013c0220
574+
6826911 0x200013c0c20 Task-6 task_group Task-3 0x200013c0420
575+
6826911 0x200013c0e20 Task-7 task_group Task-3 0x200013c0420
576+
577+
578+
and with the ``--tree`` option:
579+
580+
.. code-block:: bash
581+
582+
python -m asyncio.tools --tree 12345
583+
584+
└── (T) Task-1
585+
└── main
586+
└── (T) Task-2
587+
└── task_group
588+
├── (T) Task-4
589+
└── (T) Task-5
590+
└── (T) Task-3
591+
└── task_group
592+
├── (T) Task-6
593+
└── (T) Task-7
594+
595+
If a cycle is detected in the async await graph (which could indicate a
596+
programming issue), the tool raises an error and lists the cycle paths that
597+
prevent tree construction.
598+
599+
(Contributed by Pablo Galindo, Łukasz Langa and Marta Gomez Macias in :gh:`91048`.)
600+
511601
.. _whatsnew314-tail-call:
512602

513603
A new type of interpreter
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add a new ``python -m asyncio.tools`` command-line interface to inspect
2+
asyncio tasks in a running Python process. Displays a flat table of await
3+
relationships or a tree view with ``--tree``, useful for debugging async
4+
code. Patch by Pablo Galindo, Łukasz Langa and Marta Gomez Macias.

0 commit comments

Comments
 (0)