9
9
from . import tasks
10
10
11
11
__all__ = (
12
- 'capture_call_stack ' ,
13
- 'print_call_stack ' ,
14
- 'FrameCallStackEntry ' ,
15
- 'CoroutineCallStackEntry ' ,
16
- 'FutureCallStack ' ,
12
+ 'capture_call_graph ' ,
13
+ 'print_call_graph ' ,
14
+ 'FrameCallGraphEntry ' ,
15
+ 'CoroutineCallGraphEntry ' ,
16
+ 'FutureCallGraph ' ,
17
17
)
18
18
19
19
# Sadly, we can't re-use the traceback's module datastructures as those
24
24
# top level asyncio namespace, and want to avoid future name clashes.
25
25
26
26
27
- class FrameCallStackEntry (typing .NamedTuple ):
27
+ class FrameCallGraphEntry (typing .NamedTuple ):
28
28
frame : types .FrameType
29
29
30
30
31
- class CoroutineCallStackEntry (typing .NamedTuple ):
31
+ class CoroutineCallGraphEntry (typing .NamedTuple ):
32
32
coroutine : types .CoroutineType
33
33
34
34
35
- class FutureCallStack (typing .NamedTuple ):
35
+ class FutureCallGraph (typing .NamedTuple ):
36
36
future : futures .Future
37
- call_stack : list [FrameCallStackEntry | CoroutineCallStackEntry ]
38
- awaited_by : list [FutureCallStack ]
37
+ call_graph : list [FrameCallGraphEntry | CoroutineCallGraphEntry ]
38
+ awaited_by : list [FutureCallGraph ]
39
39
40
40
41
- def _build_stack_for_future (future : any ) -> FutureCallStack :
41
+ def _build_stack_for_future (future : any ) -> FutureCallGraph :
42
42
if not isinstance (future , futures .Future ):
43
43
raise TypeError (
44
44
f"{ future !r} object does not appear to be compatible "
@@ -52,17 +52,17 @@ def _build_stack_for_future(future: any) -> FutureCallStack:
52
52
else :
53
53
coro = get_coro ()
54
54
55
- st : list [CoroutineCallStackEntry ] = []
56
- awaited_by : list [FutureCallStack ] = []
55
+ st : list [CoroutineCallGraphEntry ] = []
56
+ awaited_by : list [FutureCallGraph ] = []
57
57
58
58
while coro is not None :
59
59
if hasattr (coro , 'cr_await' ):
60
60
# A native coroutine or duck-type compatible iterator
61
- st .append (CoroutineCallStackEntry (coro ))
61
+ st .append (CoroutineCallGraphEntry (coro ))
62
62
coro = coro .cr_await
63
63
elif hasattr (coro , 'ag_await' ):
64
64
# A native async generator or duck-type compatible iterator
65
- st .append (CoroutineCallStackEntry (coro ))
65
+ st .append (CoroutineCallGraphEntry (coro ))
66
66
coro = coro .ag_await
67
67
else :
68
68
break
@@ -72,30 +72,30 @@ def _build_stack_for_future(future: any) -> FutureCallStack:
72
72
awaited_by .append (_build_stack_for_future (parent ))
73
73
74
74
st .reverse ()
75
- return FutureCallStack (future , st , awaited_by )
75
+ return FutureCallGraph (future , st , awaited_by )
76
76
77
77
78
- def capture_call_stack (* , future : any = None ) -> FutureCallStack | None :
78
+ def capture_call_graph (* , future : any = None ) -> FutureCallGraph | None :
79
79
"""Capture async call stack for the current task or the provided Future.
80
80
81
81
The stack is represented with three data structures:
82
82
83
- * FutureCallStack (future, call_stack , awaited_by)
83
+ * FutureCallGraph (future, call_graph , awaited_by)
84
84
85
85
Where 'future' is a reference to an asyncio.Future or asyncio.Task
86
86
(or their subclasses.)
87
87
88
- 'call_stack ' is a list of FrameCallStackEntry and CoroutineCallStackEntry
88
+ 'call_graph ' is a list of FrameCallGraphEntry and CoroutineCallGraphEntry
89
89
objects (more on them below.)
90
90
91
- 'awaited_by' is a list of FutureCallStack objects.
91
+ 'awaited_by' is a list of FutureCallGraph objects.
92
92
93
- * FrameCallStackEntry (frame)
93
+ * FrameCallGraphEntry (frame)
94
94
95
95
Where 'frame' is a frame object of a regular Python function
96
96
in the call stack.
97
97
98
- * CoroutineCallStackEntry (coroutine)
98
+ * CoroutineCallGraphEntry (coroutine)
99
99
100
100
Where 'coroutine' is a coroutine object of an awaiting coroutine
101
101
or asyncronous generator.
@@ -117,7 +117,7 @@ def capture_call_stack(*, future: any = None) -> FutureCallStack | None:
117
117
else :
118
118
if loop is None :
119
119
raise RuntimeError (
120
- 'capture_call_stack () is called outside of a running '
120
+ 'capture_call_graph () is called outside of a running '
121
121
'event loop and no *future* to introspect was provided' )
122
122
future = tasks .current_task (loop = loop )
123
123
@@ -133,21 +133,21 @@ def capture_call_stack(*, future: any = None) -> FutureCallStack | None:
133
133
f"with asyncio.Future"
134
134
)
135
135
136
- call_stack : list [FrameCallStackEntry | CoroutineCallStackEntry ] = []
136
+ call_graph : list [FrameCallGraphEntry | CoroutineCallGraphEntry ] = []
137
137
138
138
f = sys ._getframe (1 )
139
139
try :
140
140
while f is not None :
141
141
is_async = f .f_generator is not None
142
142
143
143
if is_async :
144
- call_stack .append (CoroutineCallStackEntry (f .f_generator ))
144
+ call_graph .append (CoroutineCallGraphEntry (f .f_generator ))
145
145
if f .f_back is not None and f .f_back .f_generator is None :
146
146
# We've reached the bottom of the coroutine stack, which
147
147
# must be the Task that runs it.
148
148
break
149
149
else :
150
- call_stack .append (FrameCallStackEntry (f ))
150
+ call_graph .append (FrameCallGraphEntry (f ))
151
151
152
152
f = f .f_back
153
153
finally :
@@ -158,13 +158,13 @@ def capture_call_stack(*, future: any = None) -> FutureCallStack | None:
158
158
for parent in fut_waiters :
159
159
awaited_by .append (_build_stack_for_future (parent ))
160
160
161
- return FutureCallStack (future , call_stack , awaited_by )
161
+ return FutureCallGraph (future , call_graph , awaited_by )
162
162
163
163
164
- def print_call_stack (* , future : any = None , file = None ) -> None :
164
+ def print_call_graph (* , future : any = None , file = None ) -> None :
165
165
"""Print async call stack for the current task or the provided Future."""
166
166
167
- def render_level (st : FutureCallStack , buf : list [str ], level : int ):
167
+ def render_level (st : FutureCallGraph , buf : list [str ], level : int ):
168
168
def add_line (line : str ):
169
169
buf .append (level * ' ' + line )
170
170
@@ -177,12 +177,12 @@ def add_line(line: str):
177
177
f'* Future(id=0x{ id (st .future ):x} )'
178
178
)
179
179
180
- if st .call_stack :
180
+ if st .call_graph :
181
181
add_line (
182
182
f' + Call stack:'
183
183
)
184
- for ste in st .call_stack :
185
- if isinstance (ste , FrameCallStackEntry ):
184
+ for ste in st .call_graph :
185
+ if isinstance (ste , FrameCallGraphEntry ):
186
186
f = ste .frame
187
187
add_line (
188
188
f' | * { f .f_code .co_qualname } ()'
@@ -191,7 +191,7 @@ def add_line(line: str):
191
191
f' | { f .f_code .co_filename } :{ f .f_lineno } '
192
192
)
193
193
else :
194
- assert isinstance (ste , CoroutineCallStackEntry )
194
+ assert isinstance (ste , CoroutineCallGraphEntry )
195
195
c = ste .coroutine
196
196
197
197
try :
@@ -222,7 +222,7 @@ def add_line(line: str):
222
222
for fut in st .awaited_by :
223
223
render_level (fut , buf , level + 1 )
224
224
225
- stack = capture_call_stack (future = future )
225
+ stack = capture_call_graph (future = future )
226
226
if stack is None :
227
227
return
228
228
0 commit comments