Skip to content

Commit c252d04

Browse files
committed
Per mpage's suggestion: use traceback style formatring for frames
1 parent dabd158 commit c252d04

File tree

3 files changed

+36
-27
lines changed

3 files changed

+36
-27
lines changed

Doc/library/asyncio-stack.rst

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ a suspended *future*.
1818
.. versionadded:: 3.14
1919

2020

21-
.. function:: print_call_graph(*, future=None, file=None)
21+
.. function:: print_call_graph(*, future=None, file=None, depth=1)
2222

2323
Print the async call graph for the current task or the provided
2424
:class:`Task` or :class:`Future`.
@@ -27,6 +27,10 @@ a suspended *future*.
2727
If not passed, the current running task will be used. If there's no
2828
current task, the function returns ``None``.
2929

30+
If the function is called on *the current task*, the optional
31+
keyword-only ``depth`` argument can be used to skip the specified
32+
number of frames from top of the stack.
33+
3034
If *file* is not specified the function will print to :data:`sys.stdout`.
3135

3236
**Example:**
@@ -48,19 +52,14 @@ a suspended *future*.
4852
4953
will print::
5054

51-
* Task(name='Task-2', id=0x105038fe0)
52-
+ Call stack:
53-
| * print_call_graph()
54-
| asyncio/stack.py:231
55-
| * async test()
56-
| test.py:4
57-
+ Awaited by:
58-
* Task(name='Task-1', id=0x1050a6060)
59-
+ Call stack:
60-
| * async TaskGroup.__aexit__()
61-
| asyncio/taskgroups.py:107
62-
| * async main()
63-
| test.py:7
55+
* Task(name='Task-2', id=0x1039f0fe0)
56+
+ Call stack:
57+
| * File 't2.py', line 4, in async test()
58+
+ Awaited by:
59+
* Task(name='Task-1', id=0x103a5e060)
60+
+ Call stack:
61+
| * File 'taskgroups.py', line 107, in async TaskGroup.__aexit__()
62+
| * File 't2.py', line 7, in async main()
6463

6564
For rendering the call stack to a string the following pattern
6665
should be used:
@@ -85,6 +84,10 @@ a suspended *future*.
8584
If not passed, the current running task will be used. If there's no
8685
current task, the function returns ``None``.
8786

87+
If the function is called on *the current task*, the optional
88+
keyword-only ``depth`` argument can be used to skip the specified
89+
number of frames from top of the stack.
90+
8891
Returns a ``FutureCallGraph`` named tuple:
8992

9093
* ``FutureCallGraph(future, call_graph, awaited_by)``

Lib/asyncio/stack.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ def _build_stack_for_future(future: any) -> FutureCallGraph:
7575
return FutureCallGraph(future, st, awaited_by)
7676

7777

78-
def capture_call_graph(*, future: any = None) -> FutureCallGraph | None:
78+
def capture_call_graph(
79+
*,
80+
future: any = None,
81+
depth: int = 1,
82+
) -> FutureCallGraph | None:
7983
"""Capture async call stack for the current task or the provided Future.
8084
8185
The stack is represented with three data structures:
@@ -103,6 +107,10 @@ def capture_call_graph(*, future: any = None) -> FutureCallGraph | None:
103107
Receives an optional keyword-only "future" argument. If not passed,
104108
the current task will be used. If there's no current task, the function
105109
returns None.
110+
111+
If "capture_call_graph()" is introspecting *the current task*, the
112+
optional keyword-only "depth" argument can be used to skip the specified
113+
number of frames from top of the stack.
106114
"""
107115

108116
loop = events._get_running_loop()
@@ -135,7 +143,7 @@ def capture_call_graph(*, future: any = None) -> FutureCallGraph | None:
135143

136144
call_graph: list[FrameCallGraphEntry | CoroutineCallGraphEntry] = []
137145

138-
f = sys._getframe(1)
146+
f = sys._getframe(depth)
139147
try:
140148
while f is not None:
141149
is_async = f.f_generator is not None
@@ -161,7 +169,7 @@ def capture_call_graph(*, future: any = None) -> FutureCallGraph | None:
161169
return FutureCallGraph(future, call_graph, awaited_by)
162170

163171

164-
def print_call_graph(*, future: any = None, file=None) -> None:
172+
def print_call_graph(*, future: any = None, file=None, depth: int = 1) -> None:
165173
"""Print async call stack for the current task or the provided Future."""
166174

167175
def render_level(st: FutureCallGraph, buf: list[str], level: int):
@@ -185,10 +193,9 @@ def add_line(line: str):
185193
if isinstance(ste, FrameCallGraphEntry):
186194
f = ste.frame
187195
add_line(
188-
f' | * {f.f_code.co_qualname}()'
189-
)
190-
add_line(
191-
f' | {f.f_code.co_filename}:{f.f_lineno}'
196+
f' | * File {f.f_code.co_filename!r},'
197+
f' line {f.f_lineno}, in'
198+
f' {f.f_code.co_qualname}()'
192199
)
193200
else:
194201
assert isinstance(ste, CoroutineCallGraphEntry)
@@ -209,10 +216,9 @@ def add_line(line: str):
209216
tag = 'generator'
210217

211218
add_line(
212-
f' | * {tag} {code.co_qualname}()'
213-
)
214-
add_line(
215-
f' | {f.f_code.co_filename}:{f.f_lineno}'
219+
f' | * File {f.f_code.co_filename!r},'
220+
f' line {f.f_lineno}, in'
221+
f' {tag} {code.co_qualname}()'
216222
)
217223

218224
if st.awaited_by:
@@ -222,7 +228,7 @@ def add_line(line: str):
222228
for fut in st.awaited_by:
223229
render_level(fut, buf, level + 1)
224230

225-
stack = capture_call_graph(future=future)
231+
stack = capture_call_graph(future=future, depth=depth + 1)
226232
if stack is None:
227233
return
228234

Lib/test/test_asyncio/test_stack.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ async def main():
107107
])
108108

109109
self.assertIn(
110-
'* async TestCallStack.test_stack_tgroup()',
110+
' async TestCallStack.test_stack_tgroup()',
111111
stack_for_c5[1])
112112

113113

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy