CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-stack-data

Extract data from python stack frames and tracebacks for informative displays

Overview
Eval results
Files

frame-analysis.mddocs/

Stack Frame Analysis

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.

Capabilities

FrameInfo Creation

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 object
  • options: Options - Configuration options
  • code: CodeType - Frame code object
  • source: Source - Enhanced source object
  • filename: str - Absolute file path
  • scope: Optional[ast.AST] - Innermost function/class/module AST
  • lines: List[Union[Line, LineGap, BlankLineRange]] - Lines to display
  • executing: executing.Executing - Executing object from executing library
  • lineno: int - Line number being executed
  • variables: List[Variable] - All variables in scope
  • variables_in_lines: List[Variable] - Variables in displayed lines
  • variables_by_lineno: Mapping[int, List[Tuple[Variable, ast.AST]]] - Variables organized by line number
  • variables_in_executing_piece: List[Variable] - Variables in currently executing piece
  • scope_pieces: List[range] - All pieces (line ranges) in the scope
  • included_pieces: List[range] - Pieces to display determined by options
  • executing_piece: range - Currently executing piece

Display Configuration

Configuration 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 Analysis

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)
        """

Variable Inspection

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: Any

Line-Level Analysis

Detailed 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 line
  • lineno: int - 1-based line number
  • is_current: bool - Whether this is the currently executing line
  • tokens: List[Token] - Source tokens in this line
  • leading_indent: Optional[int] - Leading spaces to strip

Repeated Frame Handling

Management 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."""

Marker System

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: str

Syntax Highlighting Integration

Integration 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
    """

Blank Line Handling

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: int

Usage Examples

Basic Frame Analysis

import 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}")

Stack Analysis with Options

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}")

Variable Inspection

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}")

Custom Line Rendering

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

docs

formatting.md

frame-analysis.md

index.md

serialization.md

tile.json