]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-91048: Add filename:line_no information to `asyncio pstree` (#133478)
authorŁukasz Langa <lukasz@langa.pl>
Mon, 5 May 2025 23:07:33 +0000 (01:07 +0200)
committerGitHub <noreply@github.com>
Mon, 5 May 2025 23:07:33 +0000 (23:07 +0000)
Lib/asyncio/__main__.py
Lib/asyncio/tools.py
Lib/test/test_asyncio/test_tools.py

index d85a32692152726a304e2b7343842861d9b2a4b2..21ca5c5f62ae838cf1033b77a14a99f305e5189a 100644 (file)
@@ -146,6 +146,7 @@ if __name__ == '__main__':
     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(
index dde755e3796bbb762f45b9c306064c85d575b9d3..bf1cb5e64cbfbe3a7d10bdb15aa39326a7d2e16c 100644 (file)
@@ -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 _find_cycles(graph):
 # ─── 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)
index 2caf56172c91939e3998d4237eeb8145419b815b..0413e236c27dfabd8b368d8fbe3d0ddf74f151ff 100644 (file)
@@ -18,10 +18,18 @@ TEST_INPUTS_TREE = [
                         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 @@ TEST_INPUTS_TREE = [
                     "                │           └──  __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 @@ TEST_INPUTS_TREE = [
                     "                            └──  __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 @@ TEST_INPUTS_TABLE = [
 
 
 class TestAsyncioToolsTree(unittest.TestCase):
-
     def test_asyncio_utils(self):
         for input_, tree in TEST_INPUTS_TREE:
             with self.subTest(input_):