| 
1 | 1 | """Tools to analyze tasks running in asyncio programs."""  | 
2 | 2 | 
 
  | 
3 |  | -from collections import defaultdict, namedtuple  | 
 | 3 | +from collections import defaultdict  | 
 | 4 | +import csv  | 
4 | 5 | from itertools import count  | 
5 |  | -from enum import Enum  | 
 | 6 | +from enum import Enum, StrEnum, auto  | 
6 | 7 | import sys  | 
7 | 8 | from _remote_debugging import RemoteUnwinder, FrameInfo  | 
8 | 9 | 
 
  | 
@@ -232,18 +233,51 @@ def _get_awaited_by_tasks(pid: int) -> list:  | 
232 | 233 |         sys.exit(1)  | 
233 | 234 | 
 
  | 
234 | 235 | 
 
  | 
235 |  | -def display_awaited_by_tasks_table(pid: int) -> None:  | 
 | 236 | +class TaskTableOutputFormat(StrEnum):  | 
 | 237 | +    table = auto()  | 
 | 238 | +    csv = auto()  | 
 | 239 | + | 
 | 240 | + | 
 | 241 | +def display_awaited_by_tasks_table(pid, *, format=TaskTableOutputFormat.table):  | 
236 | 242 |     """Build and print a table of all pending tasks under `pid`."""  | 
237 | 243 | 
 
  | 
238 | 244 |     tasks = _get_awaited_by_tasks(pid)  | 
239 | 245 |     table = build_task_table(tasks)  | 
240 |  | -    # Print the table in a simple tabular format  | 
241 |  | -    print(  | 
242 |  | -        f"{'tid':<10} {'task id':<20} {'task name':<20} {'coroutine stack':<50} {'awaiter chain':<50} {'awaiter name':<15} {'awaiter id':<15}"  | 
243 |  | -    )  | 
244 |  | -    print("-" * 180)  | 
 | 246 | +    format = TaskTableOutputFormat(format)  | 
 | 247 | +    if format == TaskTableOutputFormat.table:  | 
 | 248 | +        _display_awaited_by_tasks_table(table)  | 
 | 249 | +    else:  | 
 | 250 | +        _display_awaited_by_tasks_csv(table, format=format)  | 
 | 251 | + | 
 | 252 | + | 
 | 253 | +_row_header = ('tid', 'task id', 'task name', 'coroutine stack',  | 
 | 254 | +               'awaiter chain', 'awaiter name', 'awaiter id')  | 
 | 255 | + | 
 | 256 | + | 
 | 257 | +def _display_awaited_by_tasks_table(table):  | 
 | 258 | +    """Print the table in a simple tabular format."""  | 
 | 259 | +    print(_fmt_table_row(*_row_header))  | 
 | 260 | +    print('-' * 180)  | 
245 | 261 |     for row in table:  | 
246 |  | -        print(f"{row[0]:<10} {row[1]:<20} {row[2]:<20} {row[3]:<50} {row[4]:<50} {row[5]:<15} {row[6]:<15}")  | 
 | 262 | +        print(_fmt_table_row(*row))  | 
 | 263 | + | 
 | 264 | + | 
 | 265 | +def _fmt_table_row(tid, task_id, task_name, coro_stack,  | 
 | 266 | +                   awaiter_chain, awaiter_name, awaiter_id):  | 
 | 267 | +    # Format a single row for the table format  | 
 | 268 | +    return (f'{tid:<10} {task_id:<20} {task_name:<20} {coro_stack:<50} '  | 
 | 269 | +            f'{awaiter_chain:<50} {awaiter_name:<15} {awaiter_id:<15}')  | 
 | 270 | + | 
 | 271 | + | 
 | 272 | +def _display_awaited_by_tasks_csv(table, *, format):  | 
 | 273 | +    """Print the table in CSV format"""  | 
 | 274 | +    if format == TaskTableOutputFormat.csv:  | 
 | 275 | +        delimiter = ','  | 
 | 276 | +    else:  | 
 | 277 | +        raise ValueError(f"Unknown output format: {format}")  | 
 | 278 | +    csv_writer = csv.writer(sys.stdout, delimiter=delimiter)  | 
 | 279 | +    csv_writer.writerow(_row_header)  | 
 | 280 | +    csv_writer.writerows(table)  | 
247 | 281 | 
 
  | 
248 | 282 | 
 
  | 
249 | 283 | def display_awaited_by_tasks_tree(pid: int) -> None:  | 
 | 
0 commit comments