Extract data from python stack frames and tracebacks for informative displays
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.
Central class that extracts rich information from stack frames, providing access to source code, variables, execution context, and formatting options.
class FrameInfo:
def __init__(self, frame_or_tb: Union[FrameType, TracebackType],
options: Optional[Options] = None):
"""
Create FrameInfo from a frame or traceback object.
Args:
frame_or_tb: Frame or traceback object to analyze
options: Configuration for analysis and display
"""
@classmethod
def stack_data(cls, frame_or_tb: Union[FrameType, TracebackType],
options: Optional[Options] = None,
*, collapse_repeated_frames: bool = True) -> Iterator[Union['FrameInfo', RepeatedFrames]]:
"""
Create iterator of FrameInfo objects for entire stack.
Args:
frame_or_tb: Starting frame or traceback
options: Configuration for analysis
collapse_repeated_frames: Whether to collapse recursive frames
Yields:
FrameInfo or RepeatedFrames objects for each stack level
"""Key Properties:
frame: FrameType - The actual frame objectoptions: Options - Configuration optionscode: CodeType - Frame code objectsource: Source - Enhanced source objectfilename: str - Absolute file pathscope: Optional[ast.AST] - Innermost function/class/module ASTlines: List[Union[Line, LineGap, BlankLineRange]] - Lines to displayexecuting: executing.Executing - Executing object from executing librarylineno: int - Line number being executedvariables: List[Variable] - All variables in scopevariables_in_lines: List[Variable] - Variables in displayed linesvariables_by_lineno: Mapping[int, List[Tuple[Variable, ast.AST]]] - Variables organized by line numbervariables_in_executing_piece: List[Variable] - Variables in currently executing piecescope_pieces: List[range] - All pieces (line ranges) in the scopeincluded_pieces: List[range] - Pieces to display determined by optionsexecuting_piece: range - Currently executing pieceConfiguration system for controlling how stack frames are analyzed and displayed, including context size, formatting preferences, and display options.
class Options:
def __init__(self, *,
before: int = 3,
after: int = 1,
include_signature: bool = False,
max_lines_per_piece: int = 6,
pygments_formatter = None,
blank_lines: BlankLines = BlankLines.HIDDEN):
"""
Configuration for FrameInfo analysis and display.
Args:
before: Number of context pieces before executing piece
after: Number of context pieces after executing piece
include_signature: Whether to include function signature
max_lines_per_piece: Max lines per piece before truncation
pygments_formatter: Pygments formatter for syntax highlighting
blank_lines: How to handle blank lines in output
"""Enhanced source code representation with AST parsing, tokenization, and metadata extraction for rich code analysis and display.
class Source:
"""
Enhanced source code of a single file with associated metadata.
Inherits from executing.Source with additional features.
"""
@property
def pieces(self) -> List[range]:
"""List of code piece ranges for logical grouping."""
@property
def tokens_by_lineno(self) -> Mapping[int, List[Token]]:
"""Tokens grouped by line number for detailed analysis."""
def line_range(self, node: ast.AST) -> Tuple[int, int]:
"""
Get line range for AST node.
Args:
node: AST node to analyze
Returns:
Tuple of (start_line, end_line)
"""Safe expression evaluation and variable inspection using pure_eval for extracting variable values and expressions from stack frames.
class Variable(NamedTuple):
"""
An expression that appears in source code and its evaluated value.
Fields:
name: Source text of the expression
nodes: List of equivalent AST nodes representing the expression
value: Safely evaluated value of the expression
"""
name: str
nodes: Sequence[ast.AST]
value: AnyDetailed analysis of individual source code lines with token information, variable ranges, and rendering capabilities.
class Line:
def __init__(self, frame_info: 'FrameInfo', lineno: int):
"""
Create Line object for specific line in frame.
Args:
frame_info: Parent FrameInfo object
lineno: 1-based line number
"""
def render(self, markers: Iterable[MarkerInLine] = (), *,
strip_leading_indent: bool = True,
pygmented: bool = False,
escape_html: bool = False) -> str:
"""
Render line with optional markers and formatting.
Args:
markers: Markers to insert in the line
strip_leading_indent: Whether to strip leading whitespace
pygmented: Whether to apply syntax highlighting
escape_html: Whether to escape HTML characters
Returns:
Formatted line string
"""
@property
def variable_ranges(self) -> List[RangeInLine]:
"""Get ranges for variables in this line."""
@property
def executing_node_ranges(self) -> List[RangeInLine]:
"""Get ranges for the executing node in this line."""
@property
def token_ranges(self) -> List[RangeInLine]:
"""Get ranges for each token in this line."""Key Properties:
text: str - Raw source text of the linelineno: int - 1-based line numberis_current: bool - Whether this is the currently executing linetokens: List[Token] - Source tokens in this lineleading_indent: Optional[int] - Leading spaces to stripManagement of repeated stack frames that occur in deep recursion or loops, providing condensed representation.
class RepeatedFrames:
def __init__(self, frames: List[Union[FrameType, TracebackType]],
frame_keys: List[Tuple[CodeType, int]]):
"""
Sequence of repeated stack frames.
Args:
frames: Raw frame objects
frame_keys: Extracted frame information
"""
@property
def description(self) -> str:
"""Brief description of the repeated frames."""System for inserting markers and annotations into source code lines for highlighting, formatting, and visual enhancement.
def markers_from_ranges(ranges: Iterable[RangeInLine],
converter: Callable[[RangeInLine], Optional[Tuple[str, str]]]) -> List[MarkerInLine]:
"""
Convert RangeInLine objects to MarkerInLine objects.
Args:
ranges: Iterable of RangeInLine objects defining character ranges
converter: Function that converts range to start/end marker strings or None
Returns:
List of MarkerInLine objects for insertion
"""
class RangeInLine(NamedTuple):
"""
Represents a range of characters within one line of source code.
Fields:
start: Start position in the line
end: End position in the line
data: Associated data for this range
"""
start: int
end: int
data: Any
class MarkerInLine(NamedTuple):
"""
A string meant to be inserted at a given position in a line.
Fields:
position: Position in the line to insert the marker
is_start: True if this is the opening marker of a pair
string: The marker string to insert (ANSI codes, HTML tags, etc.)
"""
position: int
is_start: bool
string: strIntegration with Pygments for syntax highlighting with custom styles for highlighting executing nodes.
def style_with_executing_node(style, modifier):
"""
Create a Pygments style that highlights executing nodes.
Args:
style: Pygments style name or class
modifier: CSS-like modifier string for highlighting
Returns:
Modified Pygments style class
"""Configuration and management of blank lines in source code display.
class BlankLines(Enum):
"""
Configuration for blank line display behavior.
Values:
HIDDEN: Blank lines are not shown in output
VISIBLE: Blank lines are visible in output
SINGLE: Consecutive blank lines shown as single blank line
"""
HIDDEN = 1
VISIBLE = 2
SINGLE = 3
class BlankLineRange:
def __init__(self, begin_lineno: int, end_lineno: int):
"""
Records line number range for blank line gaps.
Args:
begin_lineno: Start line number of blank range
end_lineno: End line number of blank range
"""
begin_lineno: int
end_lineno: intimport inspect
from stack_data import FrameInfo, Options
# Analyze current frame
frame = inspect.currentframe()
frame_info = FrameInfo(frame)
# Access frame information
print(f"File: {frame_info.filename}")
print(f"Line: {frame_info.lineno}")
print(f"Variables: {len(frame_info.variables)}")
# Display source lines with context
for line in frame_info.lines:
if hasattr(line, 'text'):
marker = ">>>" if line.is_current else " "
print(f"{marker} {line.lineno}: {line.text}")from stack_data import FrameInfo, Options, BlankLines
# Configure analysis options
options = Options(
before=5, # Show 5 pieces before current
after=2, # Show 2 pieces after current
include_signature=True, # Include function signatures
blank_lines=BlankLines.SINGLE # Collapse consecutive blank lines
)
# Analyze entire stack
for item in FrameInfo.stack_data(frame, options=options):
if isinstance(item, FrameInfo):
print(f"\nFrame: {item.filename}:{item.lineno}")
for line in item.lines:
if hasattr(line, 'text'):
print(f" {line.lineno}: {line.text}")from stack_data import FrameInfo
frame_info = FrameInfo(frame)
# Inspect variables in frame
for var in frame_info.variables:
print(f"Variable: {var.name} = {var.value}")
# Variables in displayed lines only
for var in frame_info.variables_in_lines:
print(f"Line variable: {var.name} = {var.value}")from stack_data import FrameInfo, markers_from_ranges, RangeInLine, MarkerInLine
frame_info = FrameInfo(frame)
for line in frame_info.lines:
if hasattr(line, 'text'):
# Get variable ranges for highlighting
var_ranges = line.variable_ranges()
# Convert to markers
markers = markers_from_ranges(
var_ranges,
lambda r: ("[VAR]", "[/VAR]") if r.data else None
)
# Render with markers
rendered = line.render(markers, pygmented=True)
print(rendered)Install with Tessl CLI
npx tessl i tessl/pypi-stack-data