@@ -75,7 +75,11 @@ def _build_stack_for_future(future: any) -> FutureCallGraph:
75
75
return FutureCallGraph (future , st , awaited_by )
76
76
77
77
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 :
79
83
"""Capture async call stack for the current task or the provided Future.
80
84
81
85
The stack is represented with three data structures:
@@ -103,6 +107,10 @@ def capture_call_graph(*, future: any = None) -> FutureCallGraph | None:
103
107
Receives an optional keyword-only "future" argument. If not passed,
104
108
the current task will be used. If there's no current task, the function
105
109
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.
106
114
"""
107
115
108
116
loop = events ._get_running_loop ()
@@ -135,7 +143,7 @@ def capture_call_graph(*, future: any = None) -> FutureCallGraph | None:
135
143
136
144
call_graph : list [FrameCallGraphEntry | CoroutineCallGraphEntry ] = []
137
145
138
- f = sys ._getframe (1 )
146
+ f = sys ._getframe (depth )
139
147
try :
140
148
while f is not None :
141
149
is_async = f .f_generator is not None
@@ -161,7 +169,7 @@ def capture_call_graph(*, future: any = None) -> FutureCallGraph | None:
161
169
return FutureCallGraph (future , call_graph , awaited_by )
162
170
163
171
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 :
165
173
"""Print async call stack for the current task or the provided Future."""
166
174
167
175
def render_level (st : FutureCallGraph , buf : list [str ], level : int ):
@@ -185,10 +193,9 @@ def add_line(line: str):
185
193
if isinstance (ste , FrameCallGraphEntry ):
186
194
f = ste .frame
187
195
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 } ()'
192
199
)
193
200
else :
194
201
assert isinstance (ste , CoroutineCallGraphEntry )
@@ -209,10 +216,9 @@ def add_line(line: str):
209
216
tag = 'generator'
210
217
211
218
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 } ()'
216
222
)
217
223
218
224
if st .awaited_by :
@@ -222,7 +228,7 @@ def add_line(line: str):
222
228
for fut in st .awaited_by :
223
229
render_level (fut , buf , level + 1 )
224
230
225
- stack = capture_call_graph (future = future )
231
+ stack = capture_call_graph (future = future , depth = depth + 1 )
226
232
if stack is None :
227
233
return
228
234
0 commit comments