@@ -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
513603A new type of interpreter
0 commit comments