https://github.com/python/cpython/commit/470cbe97a528c5f31823a0cd4e283bf02d5d11e7
commit: 470cbe97a528c5f31823a0cd4e283bf02d5d11e7
branch: main
author: Daniele Parmeggiani <[email protected]>
committer: AA-Turner <[email protected]>
date: 2025-08-06T20:10:40Z
summary:

gh-134861: Add CSV output format to ``python -m asyncio ps`` (#134862)

Co-authored-by: Adam Turner <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst
M Lib/asyncio/__main__.py
M Lib/asyncio/tools.py

diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py
index ff3a69d1e17297..4b43ba5dd70514 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,6 +155,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 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,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)
+            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 2683f34cc7113b..efa8e1844cf3d2 100644
--- a/Lib/asyncio/tools.py
+++ b/Lib/asyncio/tools.py
@@ -1,8 +1,9 @@
 """Tools to analyze tasks running in asyncio programs."""
 
-from collections import defaultdict, namedtuple
+from collections import defaultdict
+import csv
 from itertools import count
-from enum import Enum
+from enum import Enum, StrEnum, auto
 import sys
 from _remote_debugging import RemoteUnwinder, FrameInfo
 
@@ -232,18 +233,51 @@ def _get_awaited_by_tasks(pid: int) -> list:
         sys.exit(1)
 
 
-def display_awaited_by_tasks_table(pid: int) -> None:
+class TaskTableOutputFormat(StrEnum):
+    table = auto()
+    csv = auto()
+
+
+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)
     table = build_task_table(tasks)
-    # 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)
+    format = TaskTableOutputFormat(format)
+    if format == TaskTableOutputFormat.table:
+        _display_awaited_by_tasks_table(table)
+    else:
+        _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):
+    """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):
+    """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(_row_header)
+    csv_writer.writerows(table)
 
 
 def display_awaited_by_tasks_tree(pid: int) -> None:
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..07e4c61b404ba4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst
@@ -0,0 +1 @@
+Add CSV as an output format for :program:`python -m asyncio ps`.

_______________________________________________
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]

Reply via email to