From cf700739c0fc984e3fb8bd94ebf0b4671893a094 Mon Sep 17 00:00:00 2001 From: dpdani Date: Wed, 28 May 2025 20:21:18 +0200 Subject: [PATCH 01/17] =?UTF-8?q?Add=20CSV=20and=20=F0=9F=8D=8CSV=20output?= =?UTF-8?q?=20formats=20to=20`asyncio=20ps`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/asyncio/__main__.py | 4 +++- Lib/asyncio/tools.py | 30 ++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 21ca5c5f62ae83..319da013782266 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -153,6 +153,8 @@ def interrupt(self) -> None: "ps", help="Display a table of all pending tasks in a process" ) ps.add_argument("pid", type=int, help="Process ID to inspect") + formats = [fmt.value for fmt in asyncio.tools.TaskTableOutputFormat] + ps.add_argument("--format", choices=formats, default="table") pstree = subparsers.add_parser( "pstree", help="Display a tree of all pending tasks in a process" ) @@ -160,7 +162,7 @@ def interrupt(self) -> None: args = parser.parse_args() match args.command: case "ps": - asyncio.tools.display_awaited_by_tasks_table(args.pid) + asyncio.tools.display_awaited_by_tasks_table(args.pid, args.format) sys.exit(0) case "pstree": asyncio.tools.display_awaited_by_tasks_tree(args.pid) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index b2da7d2f6ba10c..2eb7eb242d198e 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -6,6 +6,7 @@ from enum import Enum import sys from _remote_debugging import get_all_awaited_by +import csv class NodeType(Enum): @@ -200,20 +201,45 @@ def _get_awaited_by_tasks(pid: int) -> list: sys.exit(1) -def display_awaited_by_tasks_table(pid: int) -> None: +class TaskTableOutputFormat(Enum): + table = "table" + csv = "csv" + bsv = "bsv" + + +_header = ('tid', 'task id', 'task name', 'coroutine chain', 'awaiter name', 'awaiter id') + + +def display_awaited_by_tasks_table(pid: int, format_: TaskTableOutputFormat = TaskTableOutputFormat.table) -> None: """Build and print a table of all pending tasks under `pid`.""" tasks = _get_awaited_by_tasks(pid) table = build_task_table(tasks) + if format_ != TaskTableOutputFormat.table: + _display_awaited_by_tasks_csv(table, format_) + return # Print the table in a simple tabular format print( - f"{'tid':<10} {'task id':<20} {'task name':<20} {'coroutine chain':<50} {'awaiter name':<20} {'awaiter id':<15}" + f"{_header[0]:<10} {_header[1]:<20} {_header[2]:<20} {_header[3]:<50} {_header[4]:<20} {_header[5]:<15}" ) print("-" * 135) for row in table: print(f"{row[0]:<10} {row[1]:<20} {row[2]:<20} {row[3]:<50} {row[4]:<20} {row[5]:<15}") +def _display_awaited_by_tasks_csv(table, format_: TaskTableOutputFormat) -> None: + match format_: + case TaskTableOutputFormat.csv: + delimiter = ',' + case TaskTableOutputFormat.bsv: + delimiter = '🍌' + case _: + raise ValueError(f"Unknown output format: {format_}") + csv_writer = csv.writer(sys.stdout, delimiter=delimiter) + csv_writer.writerow(_header) + csv_writer.writerows(table) + + def display_awaited_by_tasks_tree(pid: int) -> None: """Build and print a tree of all pending tasks under `pid`.""" From 4b77995e42d2b1fa1fa778d430688aad4acb6b1c Mon Sep 17 00:00:00 2001 From: Daniele Parmeggiani <8658291+dpdani@users.noreply.github.com> Date: Thu, 29 May 2025 19:32:36 +0200 Subject: [PATCH 02/17] Update Lib/asyncio/tools.py Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Lib/asyncio/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 901e20b72ccad3..a5c8e7808ed579 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -236,7 +236,7 @@ def _display_awaited_by_tasks_csv(table, format_: TaskTableOutputFormat) -> None case TaskTableOutputFormat.csv: delimiter = ',' case TaskTableOutputFormat.bsv: - delimiter = '🍌' + delimiter = '\N{BANANA}' case _: raise ValueError(f"Unknown output format: {format_}") csv_writer = csv.writer(sys.stdout, delimiter=delimiter) From f5132c956f5cd16248aabd018d0164ba2bb317f4 Mon Sep 17 00:00:00 2001 From: dpdani Date: Thu, 29 May 2025 19:41:02 +0200 Subject: [PATCH 03/17] Move table output to separate function --- Lib/asyncio/tools.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index a5c8e7808ed579..2b13109c626e2a 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -219,9 +219,13 @@ def display_awaited_by_tasks_table(pid: int, format_: TaskTableOutputFormat = Ta tasks = _get_awaited_by_tasks(pid) table = build_task_table(tasks) - if format_ != TaskTableOutputFormat.table: + if format_ == TaskTableOutputFormat.table: + _display_awaited_by_tasks_table(table) + else: _display_awaited_by_tasks_csv(table, format_) - return + + +def _display_awaited_by_tasks_table(table) -> None: # Print the table in a simple tabular format print( f"{_header[0]:<10} {_header[1]:<20} {_header[2]:<20} {_header[3]:<50} {_header[4]:<20} {_header[5]:<15}" From cd11ad72338f0fcdcc16fc1c030a6b3da2a1e3c9 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 29 May 2025 19:00:39 +0000 Subject: [PATCH 04/17] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst diff --git a/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst b/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst new file mode 100644 index 00000000000000..a9f67f4f0fd43d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst @@ -0,0 +1,3 @@ +Add CSV output format to asyncio ps + +Absolutely no other output format was added in this PR 🍌 From 21b581a513f91cade44259daaa56940dfbcfe4cd Mon Sep 17 00:00:00 2001 From: dpdani Date: Thu, 29 May 2025 21:06:47 +0200 Subject: [PATCH 05/17] =?UTF-8?q?explain=20why=20=F0=9F=8D=8Cs=20are=20rel?= =?UTF-8?q?evant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/asyncio/tools.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 2b13109c626e2a..96d39a2f6c5969 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -209,6 +209,9 @@ class TaskTableOutputFormat(Enum): table = "table" csv = "csv" bsv = "bsv" + # As per the words of the asyncio 🍌SV spec lead: + # > 🍌SV is not just a format. It’s a lifestyle. A philosophy. + # https://www.youtube.com/watch?v=RrsVi1P6n0w _header = ('tid', 'task id', 'task name', 'coroutine chain', 'awaiter name', 'awaiter id') From e44226d82a7c54811cad6ee4b7a7d770b6392f54 Mon Sep 17 00:00:00 2001 From: dpdani Date: Sat, 31 May 2025 11:59:50 +0200 Subject: [PATCH 06/17] =?UTF-8?q?make=20it=20a=20secret=20=F0=9F=A4=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/asyncio/__main__.py | 8 +++++++- Lib/asyncio/tools.py | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 319da013782266..5608bb671d3f8a 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -154,7 +154,13 @@ def interrupt(self) -> None: ) ps.add_argument("pid", type=int, help="Process ID to inspect") formats = [fmt.value for fmt in asyncio.tools.TaskTableOutputFormat] - ps.add_argument("--format", choices=formats, default="table") + big_secret = asyncio.tools.TaskTableOutputFormat.bsv.value + formats_to_show = [ + fmt for fmt in formats if fmt != big_secret + ] + formats_to_show_str = f"{{{','.join(formats_to_show)}}}" + ps.add_argument("--format", choices=formats, default="table", + metavar=formats_to_show_str) pstree = subparsers.add_parser( "pstree", help="Display a tree of all pending tasks in a process" ) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 96d39a2f6c5969..27aa6b7dc205e9 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -217,11 +217,15 @@ class TaskTableOutputFormat(Enum): _header = ('tid', 'task id', 'task name', 'coroutine chain', 'awaiter name', 'awaiter id') -def display_awaited_by_tasks_table(pid: int, format_: TaskTableOutputFormat = TaskTableOutputFormat.table) -> None: +def display_awaited_by_tasks_table( + pid: int, + format_: TaskTableOutputFormat | str = TaskTableOutputFormat.table + ) -> None: """Build and print a table of all pending tasks under `pid`.""" tasks = _get_awaited_by_tasks(pid) table = build_task_table(tasks) + format_ = TaskTableOutputFormat(format_) if format_ == TaskTableOutputFormat.table: _display_awaited_by_tasks_table(table) else: From 58da049a2ad80345ffa013cef049423d8e5c14d3 Mon Sep 17 00:00:00 2001 From: Daniele Parmeggiani <8658291+dpdani@users.noreply.github.com> Date: Fri, 1 Aug 2025 20:18:22 +0200 Subject: [PATCH 07/17] Update Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- .../Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst b/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst index a9f67f4f0fd43d..07e4c61b404ba4 100644 --- a/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst +++ b/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst @@ -1,3 +1 @@ -Add CSV output format to asyncio ps - -Absolutely no other output format was added in this PR 🍌 +Add CSV as an output format for :program:`python -m asyncio ps`. From a1f69b6ee488d4b58ebb1b181e84d7d2e10e8737 Mon Sep 17 00:00:00 2001 From: dpdani Date: Fri, 1 Aug 2025 20:24:17 +0200 Subject: [PATCH 08/17] review --- Lib/asyncio/tools.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index f28b4ba0958ed2..464d37cccf2c38 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -1,11 +1,11 @@ """Tools to analyze tasks running in asyncio programs.""" from collections import defaultdict, namedtuple +import csv from itertools import count from enum import Enum import sys from _remote_debugging import RemoteUnwinder, FrameInfo -import csv class NodeType(Enum): @@ -248,37 +248,37 @@ class TaskTableOutputFormat(Enum): def display_awaited_by_tasks_table( pid: int, - format_: TaskTableOutputFormat | str = TaskTableOutputFormat.table + format: TaskTableOutputFormat | str = TaskTableOutputFormat.table ) -> None: """Build and print a table of all pending tasks under `pid`.""" tasks = _get_awaited_by_tasks(pid) table = build_task_table(tasks) - format_ = TaskTableOutputFormat(format_) - if format_ == TaskTableOutputFormat.table: + format = TaskTableOutputFormat(format) + if format == TaskTableOutputFormat.table: _display_awaited_by_tasks_table(table) else: - _display_awaited_by_tasks_csv(table, format_) + _display_awaited_by_tasks_csv(table, format) def _display_awaited_by_tasks_table(table) -> None: # Print the table in a simple tabular format print( - f"{_header[0]:<10} {_header[1]:<20} {_header[2]:<20} {_header[3]:<50} {_header[4]:<50} {_header[5]:<15} {_header[6]:<15}" + f"{'tid':<10} {'task id':<20} {'task name':<20} {'coroutine stack':<50} {'awaiter chain':<50} {'awaiter name':<15} {'awaiter id':<15}" ) print("-" * 180) for row in table: print(f"{row[0]:<10} {row[1]:<20} {row[2]:<20} {row[3]:<50} {row[4]:<50} {row[5]:<15} {row[6]:<15}") -def _display_awaited_by_tasks_csv(table, format_: TaskTableOutputFormat) -> None: - match format_: +def _display_awaited_by_tasks_csv(table, format: TaskTableOutputFormat) -> None: + match format: case TaskTableOutputFormat.csv: delimiter = ',' case TaskTableOutputFormat.bsv: delimiter = '\N{BANANA}' case _: - raise ValueError(f"Unknown output format: {format_}") + raise ValueError(f"Unknown output format: {format}") csv_writer = csv.writer(sys.stdout, delimiter=delimiter) csv_writer.writerow(_header) csv_writer.writerows(table) From 60cd13b0d2a04ee57d847ddbc33f09372f86fa6a Mon Sep 17 00:00:00 2001 From: dpdani Date: Fri, 1 Aug 2025 20:33:41 +0200 Subject: [PATCH 09/17] review --- Lib/asyncio/tools.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 464d37cccf2c38..29db7de4005b11 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -3,7 +3,7 @@ from collections import defaultdict, namedtuple import csv from itertools import count -from enum import Enum +from enum import Enum, StrEnum, auto import sys from _remote_debugging import RemoteUnwinder, FrameInfo @@ -234,18 +234,15 @@ def _get_awaited_by_tasks(pid: int) -> list: sys.exit(1) -class TaskTableOutputFormat(Enum): - table = "table" - csv = "csv" - bsv = "bsv" +class TaskTableOutputFormat(StrEnum): + table = auto() + csv = auto() + bsv = auto() # As per the words of the asyncio 🍌SV spec lead: # > 🍌SV is not just a format. It’s a lifestyle. A philosophy. # https://www.youtube.com/watch?v=RrsVi1P6n0w -_header = ('tid', 'task id', 'task name', 'coroutine stack', 'awaiter chain', 'awaiter name', 'awaiter id') - - def display_awaited_by_tasks_table( pid: int, format: TaskTableOutputFormat | str = TaskTableOutputFormat.table @@ -272,6 +269,7 @@ def _display_awaited_by_tasks_table(table) -> None: def _display_awaited_by_tasks_csv(table, format: TaskTableOutputFormat) -> None: + _header = ('tid', 'task id', 'task name', 'coroutine stack', 'awaiter chain', 'awaiter name', 'awaiter id') match format: case TaskTableOutputFormat.csv: delimiter = ',' From 3fd565dfebfb7bb8f7a7e65ea7b5a196d23ddc2f Mon Sep 17 00:00:00 2001 From: dpdani Date: Fri, 1 Aug 2025 23:20:26 +0200 Subject: [PATCH 10/17] review --- Lib/asyncio/tools.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 29db7de4005b11..6eba6ad8267d8e 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -269,7 +269,8 @@ def _display_awaited_by_tasks_table(table) -> None: def _display_awaited_by_tasks_csv(table, format: TaskTableOutputFormat) -> None: - _header = ('tid', 'task id', 'task name', 'coroutine stack', 'awaiter chain', 'awaiter name', 'awaiter id') + csv_header = ('tid', 'task id', 'task name', 'coroutine stack', + 'awaiter chain', 'awaiter name', 'awaiter id') match format: case TaskTableOutputFormat.csv: delimiter = ',' @@ -278,7 +279,7 @@ def _display_awaited_by_tasks_csv(table, format: TaskTableOutputFormat) -> None: case _: raise ValueError(f"Unknown output format: {format}") csv_writer = csv.writer(sys.stdout, delimiter=delimiter) - csv_writer.writerow(_header) + csv_writer.writerow(csv_header) csv_writer.writerows(table) From c38f92965388fd1446b5d65bc334735ac1e7e6f2 Mon Sep 17 00:00:00 2001 From: dpdani Date: Fri, 1 Aug 2025 23:23:34 +0200 Subject: [PATCH 11/17] review --- Lib/asyncio/tools.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 6eba6ad8267d8e..509d27f05813d4 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -271,13 +271,12 @@ def _display_awaited_by_tasks_table(table) -> None: def _display_awaited_by_tasks_csv(table, format: TaskTableOutputFormat) -> None: csv_header = ('tid', 'task id', 'task name', 'coroutine stack', 'awaiter chain', 'awaiter name', 'awaiter id') - match format: - case TaskTableOutputFormat.csv: - delimiter = ',' - case TaskTableOutputFormat.bsv: - delimiter = '\N{BANANA}' - case _: - raise ValueError(f"Unknown output format: {format}") + if format == TaskTableOutputFormat.csv: + delimiter = ',' + elif format == TaskTableOutputFormat.bsv: + delimiter = '\N{BANANA}' + else: + raise ValueError(f"Unknown output format: {format}") csv_writer = csv.writer(sys.stdout, delimiter=delimiter) csv_writer.writerow(csv_header) csv_writer.writerows(table) From 03c655b82c2cbfa20de06e68189b72f6c1ef8f5a Mon Sep 17 00:00:00 2001 From: dpdani Date: Wed, 6 Aug 2025 21:22:09 +0200 Subject: [PATCH 12/17] splitting --- Lib/asyncio/__main__.py | 8 +------- Lib/asyncio/tools.py | 6 ------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 5608bb671d3f8a..319da013782266 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -154,13 +154,7 @@ def interrupt(self) -> None: ) ps.add_argument("pid", type=int, help="Process ID to inspect") formats = [fmt.value for fmt in asyncio.tools.TaskTableOutputFormat] - big_secret = asyncio.tools.TaskTableOutputFormat.bsv.value - formats_to_show = [ - fmt for fmt in formats if fmt != big_secret - ] - formats_to_show_str = f"{{{','.join(formats_to_show)}}}" - ps.add_argument("--format", choices=formats, default="table", - metavar=formats_to_show_str) + ps.add_argument("--format", choices=formats, default="table") pstree = subparsers.add_parser( "pstree", help="Display a tree of all pending tasks in a process" ) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 509d27f05813d4..78b936255fa335 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -237,10 +237,6 @@ def _get_awaited_by_tasks(pid: int) -> list: class TaskTableOutputFormat(StrEnum): table = auto() csv = auto() - bsv = auto() - # As per the words of the asyncio 🍌SV spec lead: - # > 🍌SV is not just a format. It’s a lifestyle. A philosophy. - # https://www.youtube.com/watch?v=RrsVi1P6n0w def display_awaited_by_tasks_table( @@ -273,8 +269,6 @@ def _display_awaited_by_tasks_csv(table, format: TaskTableOutputFormat) -> None: 'awaiter chain', 'awaiter name', 'awaiter id') if format == TaskTableOutputFormat.csv: delimiter = ',' - elif format == TaskTableOutputFormat.bsv: - delimiter = '\N{BANANA}' else: raise ValueError(f"Unknown output format: {format}") csv_writer = csv.writer(sys.stdout, delimiter=delimiter) From 637e7f84e1f431ddf00f969355135caef8a7c1bc Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:30:56 +0100 Subject: [PATCH 13/17] Update Lib/asyncio/tools.py --- Lib/asyncio/tools.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 78b936255fa335..c31b1ad609e499 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -239,10 +239,7 @@ class TaskTableOutputFormat(StrEnum): csv = auto() -def display_awaited_by_tasks_table( - pid: int, - format: TaskTableOutputFormat | str = TaskTableOutputFormat.table - ) -> None: +def display_awaited_by_tasks_table(pid, *, format=TaskTableOutputFormat.table): """Build and print a table of all pending tasks under `pid`.""" tasks = _get_awaited_by_tasks(pid) From 8bdfceaecf3679ef74910240a98b92129be8a09e Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:42:11 +0100 Subject: [PATCH 14/17] Reintroduce _row_header, add _fmt_table_row() utility --- Lib/asyncio/__main__.py | 10 ++++++---- Lib/asyncio/tools.py | 38 +++++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 319da013782266..9fbc77fab5db4f 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -1,7 +1,6 @@ import argparse import ast import asyncio -import asyncio.tools import concurrent.futures import contextvars import inspect @@ -11,6 +10,9 @@ import threading import types import warnings +from asyncio.tools import (TaskTableOutputFormat, + display_awaited_by_tasks_table, + display_awaited_by_tasks_tree) from _colorize import get_theme from _pyrepl.console import InteractiveColoredConsole @@ -153,7 +155,7 @@ def interrupt(self) -> None: "ps", help="Display a table of all pending tasks in a process" ) ps.add_argument("pid", type=int, help="Process ID to inspect") - formats = [fmt.value for fmt in asyncio.tools.TaskTableOutputFormat] + formats = [fmt.value for fmt in TaskTableOutputFormat] ps.add_argument("--format", choices=formats, default="table") pstree = subparsers.add_parser( "pstree", help="Display a tree of all pending tasks in a process" @@ -162,10 +164,10 @@ def interrupt(self) -> None: args = parser.parse_args() match args.command: case "ps": - asyncio.tools.display_awaited_by_tasks_table(args.pid, args.format) + display_awaited_by_tasks_table(args.pid, format=args.format) sys.exit(0) case "pstree": - asyncio.tools.display_awaited_by_tasks_tree(args.pid) + display_awaited_by_tasks_tree(args.pid) sys.exit(0) case None: pass # continue to the interactive shell diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index c31b1ad609e499..9ff923c02e4a12 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -1,11 +1,11 @@ """Tools to analyze tasks running in asyncio programs.""" -from collections import defaultdict, namedtuple import csv -from itertools import count -from enum import Enum, StrEnum, auto import sys from _remote_debugging import RemoteUnwinder, FrameInfo +from collections import defaultdict +from enum import Enum, StrEnum, auto +from itertools import chain, count class NodeType(Enum): @@ -248,28 +248,36 @@ def display_awaited_by_tasks_table(pid, *, format=TaskTableOutputFormat.table): if format == TaskTableOutputFormat.table: _display_awaited_by_tasks_table(table) else: - _display_awaited_by_tasks_csv(table, format) + _display_awaited_by_tasks_csv(table, format=format) + +_row_header = ('tid', 'task id', 'task name', 'coroutine stack', + 'awaiter chain', 'awaiter name', 'awaiter id') -def _display_awaited_by_tasks_table(table) -> None: - # Print the table in a simple tabular format - print( - f"{'tid':<10} {'task id':<20} {'task name':<20} {'coroutine stack':<50} {'awaiter chain':<50} {'awaiter name':<15} {'awaiter id':<15}" - ) - print("-" * 180) + +def _display_awaited_by_tasks_table(table): + """Print the table in a simple tabular format.""" + print(_fmt_table_row(*_row_header)) + print('-' * 180) for row in table: - print(f"{row[0]:<10} {row[1]:<20} {row[2]:<20} {row[3]:<50} {row[4]:<50} {row[5]:<15} {row[6]:<15}") + print(_fmt_table_row(*row)) + + +def _fmt_table_row(tid, task_id, task_name, coro_stack, + awaiter_chain, awaiter_name, awaiter_id): + # Format a single row for the table format + return (f'{tid:<10} {task_id:<20} {task_name:<20} {coro_stack:<50} ' + f'{awaiter_chain:<50} {awaiter_name:<15} {awaiter_id:<15}') -def _display_awaited_by_tasks_csv(table, format: TaskTableOutputFormat) -> None: - csv_header = ('tid', 'task id', 'task name', 'coroutine stack', - 'awaiter chain', 'awaiter name', 'awaiter id') +def _display_awaited_by_tasks_csv(table, *, format): + """Print the table in CSV format""" if format == TaskTableOutputFormat.csv: delimiter = ',' else: raise ValueError(f"Unknown output format: {format}") csv_writer = csv.writer(sys.stdout, delimiter=delimiter) - csv_writer.writerow(csv_header) + csv_writer.writerow(_row_header) csv_writer.writerows(table) From 50fc47307e25ee77e0f552cc9aa7c48578372944 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:43:15 +0100 Subject: [PATCH 15/17] fixup! Reintroduce _row_header, add _fmt_table_row() utility --- Lib/asyncio/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 9ff923c02e4a12..b24fe00508d422 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -5,7 +5,7 @@ from _remote_debugging import RemoteUnwinder, FrameInfo from collections import defaultdict from enum import Enum, StrEnum, auto -from itertools import chain, count +from itertools import count class NodeType(Enum): From 9729eeb58435f97e3f985aee779678a997a44044 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:43:38 +0100 Subject: [PATCH 16/17] whitespace --- Lib/asyncio/tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index b24fe00508d422..f43dccb490ffc7 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -7,7 +7,6 @@ from enum import Enum, StrEnum, auto from itertools import count - class NodeType(Enum): COROUTINE = 1 TASK = 2 From 0d8d00ad96411c88353223dd8dd5f75fc27bc6cb Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:44:24 +0100 Subject: [PATCH 17/17] fixup! Reintroduce _row_header, add _fmt_table_row() utility --- Lib/asyncio/tools.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index f43dccb490ffc7..efa8e1844cf3d2 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -1,11 +1,11 @@ """Tools to analyze tasks running in asyncio programs.""" +from collections import defaultdict import csv +from itertools import count +from enum import Enum, StrEnum, auto import sys from _remote_debugging import RemoteUnwinder, FrameInfo -from collections import defaultdict -from enum import Enum, StrEnum, auto -from itertools import count class NodeType(Enum): COROUTINE = 1