https://github.com/python/cpython/commit/60cdd800d95517f4395210f38709de9e8f11ea90
commit: 60cdd800d95517f4395210f38709de9e8f11ea90
branch: main
author: Łukasz Langa <[email protected]>
committer: pablogsal <[email protected]>
date: 2025-05-05T23:07:33Z
summary:
gh-91048: Add filename:line_no information to `asyncio pstree` (#133478)
files:
M Lib/asyncio/__main__.py
M Lib/asyncio/tools.py
M Lib/test/test_asyncio/test_tools.py
diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py
index d85a3269215272..21ca5c5f62ae83 100644
--- a/Lib/asyncio/__main__.py
+++ b/Lib/asyncio/__main__.py
@@ -146,6 +146,7 @@ def interrupt(self) -> None:
parser = argparse.ArgumentParser(
prog="python3 -m asyncio",
description="Interactive asyncio shell and CLI tools",
+ color=True,
)
subparsers = parser.add_subparsers(help="sub-commands", dest="command")
ps = subparsers.add_parser(
diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py
index dde755e3796bbb..bf1cb5e64cbfbe 100644
--- a/Lib/asyncio/tools.py
+++ b/Lib/asyncio/tools.py
@@ -21,13 +21,21 @@ class CycleFoundException(Exception):
# ─── indexing helpers ───────────────────────────────────────────
+def _format_stack_entry(elem: tuple[str, str, int] | str) -> str:
+ if isinstance(elem, tuple):
+ fqname, path, line_no = elem
+ return f"{fqname} {path}:{line_no}"
+
+ return elem
+
+
def _index(result):
id2name, awaits = {}, []
for _thr_id, tasks in result:
for tid, tname, awaited in tasks:
id2name[tid] = tname
for stack, parent_id in awaited:
- stack = [elem[0] if isinstance(elem, tuple) else elem for elem
in stack]
+ stack = [_format_stack_entry(elem) for elem in stack]
awaits.append((parent_id, stack, tid))
return id2name, awaits
@@ -106,7 +114,7 @@ def dfs(v):
# ─── PRINT TREE FUNCTION ───────────────────────────────────────
def build_async_tree(result, task_emoji="(T)", cor_emoji=""):
"""
- Build a list of strings for pretty-print a async call tree.
+ Build a list of strings for pretty-print an async call tree.
The call tree is produced by `get_all_async_stacks()`, prefixing tasks
with `task_emoji` and coroutine frames with `cor_emoji`.
@@ -169,7 +177,7 @@ def build_task_table(result):
return table
def _print_cycle_exception(exception: CycleFoundException):
- print("ERROR: await-graph contains cycles – cannot print a tree!",
file=sys.stderr)
+ print("ERROR: await-graph contains cycles - cannot print a tree!",
file=sys.stderr)
print("", file=sys.stderr)
for c in exception.cycles:
inames = " → ".join(exception.id2name.get(tid, hex(tid)) for tid in c)
diff --git a/Lib/test/test_asyncio/test_tools.py
b/Lib/test/test_asyncio/test_tools.py
index 2caf56172c9193..0413e236c27dfa 100644
--- a/Lib/test/test_asyncio/test_tools.py
+++ b/Lib/test/test_asyncio/test_tools.py
@@ -18,10 +18,18 @@
3,
"timer",
[
- [["awaiter3", "awaiter2", "awaiter"], 4],
- [["awaiter1_3", "awaiter1_2", "awaiter1"], 5],
- [["awaiter1_3", "awaiter1_2", "awaiter1"], 6],
- [["awaiter3", "awaiter2", "awaiter"], 7],
+ [[("awaiter3", "/path/to/app.py", 130),
+ ("awaiter2", "/path/to/app.py", 120),
+ ("awaiter", "/path/to/app.py", 110)], 4],
+ [[("awaiterB3", "/path/to/app.py", 190),
+ ("awaiterB2", "/path/to/app.py", 180),
+ ("awaiterB", "/path/to/app.py", 170)], 5],
+ [[("awaiterB3", "/path/to/app.py", 190),
+ ("awaiterB2", "/path/to/app.py", 180),
+ ("awaiterB", "/path/to/app.py", 170)], 6],
+ [[("awaiter3", "/path/to/app.py", 130),
+ ("awaiter2", "/path/to/app.py", 120),
+ ("awaiter", "/path/to/app.py", 110)], 7],
],
),
(
@@ -91,14 +99,14 @@
" │ └── __aexit__",
" │ └── _aexit",
" │ ├── (T) child1_1",
- " │ │ └── awaiter",
- " │ │ └──
awaiter2",
- " │ │ └──
awaiter3",
+ " │ │ └── awaiter
/path/to/app.py:110",
+ " │ │ └── awaiter2
/path/to/app.py:120",
+ " │ │ └──
awaiter3 /path/to/app.py:130",
" │ │ └──
(T) timer",
" │ └── (T) child2_1",
- " │ └── awaiter1",
- " │ └──
awaiter1_2",
- " │ └──
awaiter1_3",
+ " │ └── awaiterB
/path/to/app.py:170",
+ " │ └──
awaiterB2 /path/to/app.py:180",
+ " │ └──
awaiterB3 /path/to/app.py:190",
" │ └──
(T) timer",
" └── (T) root2",
" └── bloch",
@@ -106,14 +114,14 @@
" └── __aexit__",
" └── _aexit",
" ├── (T) child1_2",
- " │ └── awaiter",
- " │ └──
awaiter2",
- " │ └──
awaiter3",
+ " │ └── awaiter
/path/to/app.py:110",
+ " │ └── awaiter2
/path/to/app.py:120",
+ " │ └──
awaiter3 /path/to/app.py:130",
" │ └──
(T) timer",
" └── (T) child2_2",
- " └── awaiter1",
- " └──
awaiter1_2",
- " └──
awaiter1_3",
+ " └── awaiterB
/path/to/app.py:170",
+ " └──
awaiterB2 /path/to/app.py:180",
+ " └──
awaiterB3 /path/to/app.py:190",
" └──
(T) timer",
]
]
@@ -589,7 +597,6 @@
class TestAsyncioToolsTree(unittest.TestCase):
-
def test_asyncio_utils(self):
for input_, tree in TEST_INPUTS_TREE:
with self.subTest(input_):
_______________________________________________
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]