|
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,56 @@ 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 | + bsv = auto() |
| 240 | + # 🍌SV is not just a format. It's a lifestyle. A philosophy. |
| 241 | + # https://www.youtube.com/watch?v=RrsVi1P6n0w |
| 242 | + |
| 243 | + |
| 244 | +def display_awaited_by_tasks_table(pid, *, format=TaskTableOutputFormat.table): |
236 | 245 | """Build and print a table of all pending tasks under `pid`.""" |
237 | 246 |
|
238 | 247 | tasks = _get_awaited_by_tasks(pid) |
239 | 248 | 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) |
| 249 | + format = TaskTableOutputFormat(format) |
| 250 | + if format == TaskTableOutputFormat.table: |
| 251 | + _display_awaited_by_tasks_table(table) |
| 252 | + else: |
| 253 | + _display_awaited_by_tasks_csv(table, format=format) |
| 254 | + |
| 255 | + |
| 256 | +_row_header = ('tid', 'task id', 'task name', 'coroutine stack', |
| 257 | + 'awaiter chain', 'awaiter name', 'awaiter id') |
| 258 | + |
| 259 | + |
| 260 | +def _display_awaited_by_tasks_table(table): |
| 261 | + """Print the table in a simple tabular format.""" |
| 262 | + print(_fmt_table_row(*_row_header)) |
| 263 | + print('-' * 180) |
245 | 264 | 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}") |
| 265 | + print(_fmt_table_row(*row)) |
| 266 | + |
| 267 | + |
| 268 | +def _fmt_table_row(tid, task_id, task_name, coro_stack, |
| 269 | + awaiter_chain, awaiter_name, awaiter_id): |
| 270 | + # Format a single row for the table format |
| 271 | + return (f'{tid:<10} {task_id:<20} {task_name:<20} {coro_stack:<50} ' |
| 272 | + f'{awaiter_chain:<50} {awaiter_name:<15} {awaiter_id:<15}') |
| 273 | + |
| 274 | + |
| 275 | +def _display_awaited_by_tasks_csv(table, *, format): |
| 276 | + """Print the table in CSV format""" |
| 277 | + if format == TaskTableOutputFormat.csv: |
| 278 | + delimiter = ',' |
| 279 | + elif format == TaskTableOutputFormat.bsv: |
| 280 | + delimiter = '\N{BANANA}' |
| 281 | + else: |
| 282 | + raise ValueError(f"Unknown output format: {format}") |
| 283 | + csv_writer = csv.writer(sys.stdout, delimiter=delimiter) |
| 284 | + csv_writer.writerow(_row_header) |
| 285 | + csv_writer.writerows(table) |
247 | 286 |
|
248 | 287 |
|
249 | 288 | def display_awaited_by_tasks_tree(pid: int) -> None: |
|
0 commit comments