@@ -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
0 commit comments