0
# Stack Frame Analysis
1
2
Core functionality for analyzing Python stack frames and extracting detailed information including source code context, variable values, execution state, and AST-based code understanding. This module provides the foundation for all stack_data functionality.
3
4
## Capabilities
5
6
### FrameInfo Creation
7
8
Central class that extracts rich information from stack frames, providing access to source code, variables, execution context, and formatting options.
9
10
```python { .api }
11
class FrameInfo:
12
def __init__(self, frame_or_tb: Union[FrameType, TracebackType],
13
options: Optional[Options] = None):
14
"""
15
Create FrameInfo from a frame or traceback object.
16
17
Args:
18
frame_or_tb: Frame or traceback object to analyze
19
options: Configuration for analysis and display
20
"""
21
22
@classmethod
23
def stack_data(cls, frame_or_tb: Union[FrameType, TracebackType],
24
options: Optional[Options] = None,
25
*, collapse_repeated_frames: bool = True) -> Iterator[Union['FrameInfo', RepeatedFrames]]:
26
"""
27
Create iterator of FrameInfo objects for entire stack.
28
29
Args:
30
frame_or_tb: Starting frame or traceback
31
options: Configuration for analysis
32
collapse_repeated_frames: Whether to collapse recursive frames
33
34
Yields:
35
FrameInfo or RepeatedFrames objects for each stack level
36
"""
37
```
38
39
**Key Properties:**
40
- `frame: FrameType` - The actual frame object
41
- `options: Options` - Configuration options
42
- `code: CodeType` - Frame code object
43
- `source: Source` - Enhanced source object
44
- `filename: str` - Absolute file path
45
- `scope: Optional[ast.AST]` - Innermost function/class/module AST
46
- `lines: List[Union[Line, LineGap, BlankLineRange]]` - Lines to display
47
- `executing: executing.Executing` - Executing object from executing library
48
- `lineno: int` - Line number being executed
49
- `variables: List[Variable]` - All variables in scope
50
- `variables_in_lines: List[Variable]` - Variables in displayed lines
51
- `variables_by_lineno: Mapping[int, List[Tuple[Variable, ast.AST]]]` - Variables organized by line number
52
- `variables_in_executing_piece: List[Variable]` - Variables in currently executing piece
53
- `scope_pieces: List[range]` - All pieces (line ranges) in the scope
54
- `included_pieces: List[range]` - Pieces to display determined by options
55
- `executing_piece: range` - Currently executing piece
56
57
### Display Configuration
58
59
Configuration system for controlling how stack frames are analyzed and displayed, including context size, formatting preferences, and display options.
60
61
```python { .api }
62
class Options:
63
def __init__(self, *,
64
before: int = 3,
65
after: int = 1,
66
include_signature: bool = False,
67
max_lines_per_piece: int = 6,
68
pygments_formatter = None,
69
blank_lines: BlankLines = BlankLines.HIDDEN):
70
"""
71
Configuration for FrameInfo analysis and display.
72
73
Args:
74
before: Number of context pieces before executing piece
75
after: Number of context pieces after executing piece
76
include_signature: Whether to include function signature
77
max_lines_per_piece: Max lines per piece before truncation
78
pygments_formatter: Pygments formatter for syntax highlighting
79
blank_lines: How to handle blank lines in output
80
"""
81
```
82
83
### Enhanced Source Code Analysis
84
85
Enhanced source code representation with AST parsing, tokenization, and metadata extraction for rich code analysis and display.
86
87
```python { .api }
88
class Source:
89
"""
90
Enhanced source code of a single file with associated metadata.
91
Inherits from executing.Source with additional features.
92
"""
93
94
@property
95
def pieces(self) -> List[range]:
96
"""List of code piece ranges for logical grouping."""
97
98
@property
99
def tokens_by_lineno(self) -> Mapping[int, List[Token]]:
100
"""Tokens grouped by line number for detailed analysis."""
101
102
def line_range(self, node: ast.AST) -> Tuple[int, int]:
103
"""
104
Get line range for AST node.
105
106
Args:
107
node: AST node to analyze
108
109
Returns:
110
Tuple of (start_line, end_line)
111
"""
112
```
113
114
### Variable Inspection
115
116
Safe expression evaluation and variable inspection using pure_eval for extracting variable values and expressions from stack frames.
117
118
```python { .api }
119
class Variable(NamedTuple):
120
"""
121
An expression that appears in source code and its evaluated value.
122
123
Fields:
124
name: Source text of the expression
125
nodes: List of equivalent AST nodes representing the expression
126
value: Safely evaluated value of the expression
127
"""
128
name: str
129
nodes: Sequence[ast.AST]
130
value: Any
131
```
132
133
### Line-Level Analysis
134
135
Detailed analysis of individual source code lines with token information, variable ranges, and rendering capabilities.
136
137
```python { .api }
138
class Line:
139
def __init__(self, frame_info: 'FrameInfo', lineno: int):
140
"""
141
Create Line object for specific line in frame.
142
143
Args:
144
frame_info: Parent FrameInfo object
145
lineno: 1-based line number
146
"""
147
148
def render(self, markers: Iterable[MarkerInLine] = (), *,
149
strip_leading_indent: bool = True,
150
pygmented: bool = False,
151
escape_html: bool = False) -> str:
152
"""
153
Render line with optional markers and formatting.
154
155
Args:
156
markers: Markers to insert in the line
157
strip_leading_indent: Whether to strip leading whitespace
158
pygmented: Whether to apply syntax highlighting
159
escape_html: Whether to escape HTML characters
160
161
Returns:
162
Formatted line string
163
"""
164
165
@property
166
def variable_ranges(self) -> List[RangeInLine]:
167
"""Get ranges for variables in this line."""
168
169
@property
170
def executing_node_ranges(self) -> List[RangeInLine]:
171
"""Get ranges for the executing node in this line."""
172
173
@property
174
def token_ranges(self) -> List[RangeInLine]:
175
"""Get ranges for each token in this line."""
176
```
177
178
**Key Properties:**
179
- `text: str` - Raw source text of the line
180
- `lineno: int` - 1-based line number
181
- `is_current: bool` - Whether this is the currently executing line
182
- `tokens: List[Token]` - Source tokens in this line
183
- `leading_indent: Optional[int]` - Leading spaces to strip
184
185
### Repeated Frame Handling
186
187
Management of repeated stack frames that occur in deep recursion or loops, providing condensed representation.
188
189
```python { .api }
190
class RepeatedFrames:
191
def __init__(self, frames: List[Union[FrameType, TracebackType]],
192
frame_keys: List[Tuple[CodeType, int]]):
193
"""
194
Sequence of repeated stack frames.
195
196
Args:
197
frames: Raw frame objects
198
frame_keys: Extracted frame information
199
"""
200
201
@property
202
def description(self) -> str:
203
"""Brief description of the repeated frames."""
204
```
205
206
### Marker System
207
208
System for inserting markers and annotations into source code lines for highlighting, formatting, and visual enhancement.
209
210
```python { .api }
211
def markers_from_ranges(ranges: Iterable[RangeInLine],
212
converter: Callable[[RangeInLine], Optional[Tuple[str, str]]]) -> List[MarkerInLine]:
213
"""
214
Convert RangeInLine objects to MarkerInLine objects.
215
216
Args:
217
ranges: Iterable of RangeInLine objects defining character ranges
218
converter: Function that converts range to start/end marker strings or None
219
220
Returns:
221
List of MarkerInLine objects for insertion
222
"""
223
224
class RangeInLine(NamedTuple):
225
"""
226
Represents a range of characters within one line of source code.
227
228
Fields:
229
start: Start position in the line
230
end: End position in the line
231
data: Associated data for this range
232
"""
233
start: int
234
end: int
235
data: Any
236
237
class MarkerInLine(NamedTuple):
238
"""
239
A string meant to be inserted at a given position in a line.
240
241
Fields:
242
position: Position in the line to insert the marker
243
is_start: True if this is the opening marker of a pair
244
string: The marker string to insert (ANSI codes, HTML tags, etc.)
245
"""
246
position: int
247
is_start: bool
248
string: str
249
```
250
251
### Syntax Highlighting Integration
252
253
Integration with Pygments for syntax highlighting with custom styles for highlighting executing nodes.
254
255
```python { .api }
256
def style_with_executing_node(style, modifier):
257
"""
258
Create a Pygments style that highlights executing nodes.
259
260
Args:
261
style: Pygments style name or class
262
modifier: CSS-like modifier string for highlighting
263
264
Returns:
265
Modified Pygments style class
266
"""
267
```
268
269
### Blank Line Handling
270
271
Configuration and management of blank lines in source code display.
272
273
```python { .api }
274
class BlankLines(Enum):
275
"""
276
Configuration for blank line display behavior.
277
278
Values:
279
HIDDEN: Blank lines are not shown in output
280
VISIBLE: Blank lines are visible in output
281
SINGLE: Consecutive blank lines shown as single blank line
282
"""
283
HIDDEN = 1
284
VISIBLE = 2
285
SINGLE = 3
286
287
class BlankLineRange:
288
def __init__(self, begin_lineno: int, end_lineno: int):
289
"""
290
Records line number range for blank line gaps.
291
292
Args:
293
begin_lineno: Start line number of blank range
294
end_lineno: End line number of blank range
295
"""
296
297
begin_lineno: int
298
end_lineno: int
299
```
300
301
## Usage Examples
302
303
### Basic Frame Analysis
304
305
```python
306
import inspect
307
from stack_data import FrameInfo, Options
308
309
# Analyze current frame
310
frame = inspect.currentframe()
311
frame_info = FrameInfo(frame)
312
313
# Access frame information
314
print(f"File: {frame_info.filename}")
315
print(f"Line: {frame_info.lineno}")
316
print(f"Variables: {len(frame_info.variables)}")
317
318
# Display source lines with context
319
for line in frame_info.lines:
320
if hasattr(line, 'text'):
321
marker = ">>>" if line.is_current else " "
322
print(f"{marker} {line.lineno}: {line.text}")
323
```
324
325
### Stack Analysis with Options
326
327
```python
328
from stack_data import FrameInfo, Options, BlankLines
329
330
# Configure analysis options
331
options = Options(
332
before=5, # Show 5 pieces before current
333
after=2, # Show 2 pieces after current
334
include_signature=True, # Include function signatures
335
blank_lines=BlankLines.SINGLE # Collapse consecutive blank lines
336
)
337
338
# Analyze entire stack
339
for item in FrameInfo.stack_data(frame, options=options):
340
if isinstance(item, FrameInfo):
341
print(f"\nFrame: {item.filename}:{item.lineno}")
342
for line in item.lines:
343
if hasattr(line, 'text'):
344
print(f" {line.lineno}: {line.text}")
345
```
346
347
### Variable Inspection
348
349
```python
350
from stack_data import FrameInfo
351
352
frame_info = FrameInfo(frame)
353
354
# Inspect variables in frame
355
for var in frame_info.variables:
356
print(f"Variable: {var.name} = {var.value}")
357
358
# Variables in displayed lines only
359
for var in frame_info.variables_in_lines:
360
print(f"Line variable: {var.name} = {var.value}")
361
```
362
363
### Custom Line Rendering
364
365
```python
366
from stack_data import FrameInfo, markers_from_ranges, RangeInLine, MarkerInLine
367
368
frame_info = FrameInfo(frame)
369
370
for line in frame_info.lines:
371
if hasattr(line, 'text'):
372
# Get variable ranges for highlighting
373
var_ranges = line.variable_ranges()
374
375
# Convert to markers
376
markers = markers_from_ranges(
377
var_ranges,
378
lambda r: ("[VAR]", "[/VAR]") if r.data else None
379
)
380
381
# Render with markers
382
rendered = line.render(markers, pygmented=True)
383
print(rendered)
384
```