Python Can Error Frame Analysis Code Examples
Ready-to-use Python code snippets for error frame analysis when Python can error frame issues occur. Complete debugging examples and utilities.
Python Can Error Frame Analysis Code Examples
When Python can error frame issues occur in your applications, these ready-to-use code snippets help you analyze, debug, and handle frame-related problems effectively.
Basic Frame Information Extraction #
import sys
import traceback
def extract_error_frame_info():
"""Extract detailed information from error frames"""
try:
# Simulate an error
result = 10 / 0
except Exception as e:
exc_type, exc_value, exc_traceback = sys.exc_info()
print("=== BASIC FRAME INFO ===")
print(f"Exception type: {exc_type.__name__}")
print(f"Exception value: {exc_value}")
print(f"Traceback object: {exc_traceback}")
# Get frame details
frame = exc_traceback.tb_frame
print(f"Function name: {frame.f_code.co_name}")
print(f"Filename: {frame.f_code.co_filename}")
print(f"Line number: {exc_traceback.tb_lineno}")
print(f"Local variables: {list(frame.f_locals.keys())}")
# Usage
extract_error_frame_info()
Stack Trace Frame Walker #
import traceback
import sys
def walk_error_frames():
"""Walk through all frames in an error stack trace"""
def level_3():
return 1/0 # Error occurs here
def level_2():
return level_3()
def level_1():
return level_2()
try:
level_1()
except ZeroDivisionError:
print("=== WALKING ERROR FRAMES ===")
exc_type, exc_value, exc_traceback = sys.exc_info()
frame_num = 0
tb = exc_traceback
while tb is not None:
frame = tb.tb_frame
code = frame.f_code
print(f"\nFrame #{frame_num}:")
print(f" Function: {code.co_name}")
print(f" File: {code.co_filename.split('/')[-1]}")
print(f" Line: {tb.tb_lineno}")
print(f" Local vars: {list(frame.f_locals.keys())}")
frame_num += 1
tb = tb.tb_next
# Usage
walk_error_frames()
Custom Frame Inspector Class #
import sys
import inspect
from typing import Dict, Any, List
class FrameInspector:
"""Utility class for inspecting Python error frames"""
@staticmethod
def get_current_frame_info() -> Dict[str, Any]:
"""Get information about the current frame"""
frame = inspect.currentframe().f_back
return {
'function': frame.f_code.co_name,
'filename': frame.f_code.co_filename.split('/')[-1],
'line_number': frame.f_lineno,
'local_vars': dict(frame.f_locals),
'arg_names': frame.f_code.co_varnames
}
@staticmethod
def extract_exception_frames(exception: Exception) -> List[Dict[str, Any]]:
"""Extract frame information from an exception"""
frames = []
tb = exception.__traceback__
while tb is not None:
frame = tb.tb_frame
code = frame.f_code
frame_info = {
'function': code.co_name,
'filename': code.co_filename.split('/')[-1],
'line_number': tb.tb_lineno,
'local_vars': dict(frame.f_locals),
'code_context': FrameInspector._get_code_context(
code.co_filename, tb.tb_lineno
)
}
frames.append(frame_info)
tb = tb.tb_next
return frames
@staticmethod
def _get_code_context(filename: str, line_number: int, context=3) -> List[str]:
"""Get code context around the error line"""
try:
with open(filename, 'r') as file:
lines = file.readlines()
start = max(0, line_number - context - 1)
end = min(len(lines), line_number + context)
return [line.rstrip() for line in lines[start:end]]
except:
return ["<code context not available>"]
@staticmethod
def print_frame_analysis(frames: List[Dict[str, Any]]):
"""Pretty print frame analysis"""
print("=== FRAME ANALYSIS ===")
for i, frame in enumerate(frames):
print(f"\nFrame #{i}: {frame['function']}()")
print(f" File: {frame['filename']}:{frame['line_number']}")
print(f" Variables: {list(frame['local_vars'].keys())}")
if frame['code_context']:
print(" Code context:")
for j, line in enumerate(frame['code_context']):
marker = " >> " if j == len(frame['code_context'])//2 else " "
print(f"{marker}{line}")
# Usage example
def demo_frame_inspector():
def problematic_function(x, y):
data = {"x": x, "y": y}
result = x / y # Potential error
return result
try:
result = problematic_function(10, 0)
except Exception as e:
frames = FrameInspector.extract_exception_frames(e)
FrameInspector.print_frame_analysis(frames)
# Run the demo
demo_frame_inspector()
Error Context Capture Decorator #
import functools
import sys
from datetime import datetime
def capture_error_context(include_vars=True):
"""Decorator that captures detailed error context when exceptions occur"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
# Capture error context
exc_type, exc_value, exc_traceback = sys.exc_info()
error_context = {
'timestamp': datetime.now().isoformat(),
'function': func.__name__,
'exception_type': exc_type.__name__,
'exception_message': str(exc_value),
'args': args,
'kwargs': kwargs,
'frames': []
}
# Walk through frames
tb = exc_traceback
while tb is not None:
frame = tb.tb_frame
frame_info = {
'function': frame.f_code.co_name,
'filename': frame.f_code.co_filename.split('/')[-1],
'line_number': tb.tb_lineno,
}
if include_vars:
# Filter out large objects for readability
local_vars = {}
for k, v in frame.f_locals.items():
try:
if len(str(v)) < 100:
local_vars[k] = v
else:
local_vars[k] = f"<{type(v).__name__}: too large>"
except:
local_vars[k] = f"<{type(v).__name__}: cannot display>"
frame_info['local_vars'] = local_vars
error_context['frames'].append(frame_info)
tb = tb.tb_next
# Print the context
print(f"\n=== ERROR CONTEXT [{error_context['timestamp']}] ===")
print(f"Function: {error_context['function']}")
print(f"Exception: {error_context['exception_type']}: {error_context['exception_message']}")
print("\nCall Stack:")
for i, frame in enumerate(error_context['frames']):
print(f" {i+1}. {frame['function']}() in {frame['filename']}:{frame['line_number']}")
if 'local_vars' in frame and frame['local_vars']:
print(f" Variables: {frame['local_vars']}")
# Re-raise the exception
raise
return wrapper
return decorator
# Usage example
@capture_error_context(include_vars=True)
def example_function(x, y, debug_mode=False):
"""Example function with error context capture"""
temp_var = f"processing {x} and {y}"
if debug_mode:
print(f"Debug: {temp_var}")
# This might cause an error
result = x / y
return result
# Test the decorator
try:
result = example_function(10, 0, debug_mode=True)
except ZeroDivisionError:
print("Error was handled and context was captured")
Memory-Efficient Frame Inspector #
import sys
from typing import Generator, Dict, Any
class LightweightFrameInspector:
"""Memory-efficient frame inspector for production use"""
@staticmethod
def inspect_exception_lightweight(exception: Exception) -> Generator[Dict[str, Any], None, None]:
"""Generator that yields frame info without storing all frames in memory"""
tb = exception.__traceback__
frame_count = 0
while tb is not None:
frame = tb.tb_frame
code = frame.f_code
# Yield minimal frame info
yield {
'frame_number': frame_count,
'function': code.co_name,
'filename': code.co_filename.split('/')[-1],
'line_number': tb.tb_lineno,
'arg_count': code.co_argcount,
'var_names': code.co_varnames[:code.co_argcount]
}
frame_count += 1
tb = tb.tb_next
@staticmethod
def get_error_summary(exception: Exception) -> Dict[str, Any]:
"""Get a concise error summary for logging"""
frames = list(LightweightFrameInspector.inspect_exception_lightweight(exception))
return {
'exception_type': type(exception).__name__,
'message': str(exception),
'frame_count': len(frames),
'entry_point': frames[0]['function'] if frames else 'unknown',
'error_location': f"{frames[-1]['filename']}:{frames[-1]['line_number']}" if frames else 'unknown',
'call_chain': [f"{frame['function']}()" for frame in frames]
}
# Usage example
def demonstrate_lightweight_inspection():
def deep_function_3():
return 1 / 0
def deep_function_2():
return deep_function_3()
def deep_function_1():
return deep_function_2()
try:
deep_function_1()
except ZeroDivisionError as e:
# Lightweight inspection
summary = LightweightFrameInspector.get_error_summary(e)
print("=== LIGHTWEIGHT FRAME INSPECTION ===")
print(f"Error: {summary['exception_type']}: {summary['message']}")
print(f"Frames: {summary['frame_count']}")
print(f"Entry point: {summary['entry_point']}")
print(f"Error location: {summary['error_location']}")
print(f"Call chain: {' -> '.join(summary['call_chain'])}")
# Run demonstration
demonstrate_lightweight_inspection()
Production Error Logger #
import json
import logging
import sys
from datetime import datetime
from pathlib import Path
class ProductionErrorLogger:
"""Production-ready error logger with frame context"""
def __init__(self, log_file="error_frames.log", max_frame_vars=5):
self.log_file = Path(log_file)
self.max_frame_vars = max_frame_vars
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
self.logger = logging.getLogger(__name__)
def log_exception_with_frames(self, exception: Exception, context: dict = None):
"""Log exception with detailed frame information"""
error_data = {
'timestamp': datetime.now().isoformat(),
'exception_type': type(exception).__name__,
'exception_message': str(exception),
'context': context or {},
'frames': self._extract_safe_frame_data(exception)
}
# Log to file
with open(self.log_file, 'a') as f:
json.dump(error_data, f, indent=2, default=str)
f.write('\n' + '='*50 + '\n')
self.logger.error(f"Exception logged: {type(exception).__name__}: {exception}")
def _extract_safe_frame_data(self, exception: Exception) -> list:
"""Safely extract frame data without causing memory issues"""
frames = []
tb = exception.__traceback__
while tb is not None:
frame = tb.tb_frame
code = frame.f_code
# Get limited local variables
safe_locals = {}
var_count = 0
for name, value in frame.f_locals.items():
if var_count >= self.max_frame_vars:
safe_locals['...'] = f"and {len(frame.f_locals) - var_count} more variables"
break
try:
if isinstance(value, (str, int, float, bool, type(None))):
if isinstance(value, str) and len(value) > 100:
safe_locals[name] = value[:100] + "..."
else:
safe_locals[name] = value
else:
safe_locals[name] = f"<{type(value).__name__}>"
except:
safe_locals[name] = "<cannot serialize>"
var_count += 1
frame_data = {
'function': code.co_name,
'filename': Path(code.co_filename).name,
'line_number': tb.tb_lineno,
'local_variables': safe_locals
}
frames.append(frame_data)
tb = tb.tb_next
return frames
# Usage context manager
class ErrorFrameCapture:
"""Context manager for capturing error frames"""
def __init__(self, logger: ProductionErrorLogger = None, context: dict = None):
self.logger = logger or ProductionErrorLogger()
self.context = context or {}
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
if exc_value:
self.logger.log_exception_with_frames(exc_value, self.context)
return False # Don't suppress the exception
# Usage examples
def demo_production_logging():
"""Demonstrate production error logging"""
logger = ProductionErrorLogger()
# Example 1: Direct logging
try:
data = [1, 2, 3]
result = data[10] # IndexError
except IndexError as e:
logger.log_exception_with_frames(e, {'operation': 'list_access', 'list_length': len(data)})
# Example 2: Context manager
with ErrorFrameCapture(logger, {'operation': 'division', 'user_id': 12345}):
x = 10
y = 0
result = x / y # ZeroDivisionError
print(f"Errors logged to: {logger.log_file}")
# Run the demo
demo_production_logging()
Interactive Frame Debugger #
import sys
import code
import traceback
def interactive_frame_debugger():
"""Interactive debugger that lets you inspect frames"""
def problematic_code():
x = [1, 2, 3]
y = {"key": "value"}
z = "debugging"
# Simulate complex nested calls
return nested_call_1(x, y, z)
def nested_call_1(a, b, c):
temp = len(a)
return nested_call_2(a, b, c, temp)
def nested_call_2(a, b, c, d):
# Error occurs here
return a[10] # IndexError
try:
result = problematic_code()
except Exception as e:
print("=== INTERACTIVE FRAME DEBUGGER ===")
print(f"Exception: {type(e).__name__}: {e}")
print("\nAvailable commands:")
print(" frames() - show all frames")
print(" frame(n) - inspect frame n")
print(" vars(n) - show variables in frame n")
print(" code(n) - show code context for frame n")
print(" exit() - exit debugger")
# Get exception info
exc_type, exc_value, exc_traceback = sys.exc_info()
# Store frames for inspection
frames_data = []
tb = exc_traceback
frame_num = 0
while tb is not None:
frames_data.append({
'frame': tb.tb_frame,
'line_number': tb.tb_lineno,
'frame_num': frame_num
})
tb = tb.tb_next
frame_num += 1
def frames():
"""Show all frames in the stack"""
for i, frame_data in enumerate(frames_data):
frame = frame_data['frame']
print(f"Frame {i}: {frame.f_code.co_name}() "
f"line {frame_data['line_number']}")
def frame(n):
"""Show details for frame n"""
if 0 <= n < len(frames_data):
frame = frames_data[n]['frame']
print(f"Frame {n}: {frame.f_code.co_name}()")
print(f"File: {frame.f_code.co_filename}")
print(f"Line: {frames_data[n]['line_number']}")
else:
print(f"Invalid frame number {n}")
def vars(n):
"""Show variables for frame n"""
if 0 <= n < len(frames_data):
frame = frames_data[n]['frame']
print(f"Variables in frame {n}:")
for name, value in frame.f_locals.items():
try:
print(f" {name} = {repr(value)}")
except:
print(f" {name} = <cannot display>")
else:
print(f"Invalid frame number {n}")
def code(n):
"""Show code context for frame n"""
if 0 <= n < len(frames_data):
frame = frames_data[n]['frame']
filename = frame.f_code.co_filename
line_number = frames_data[n]['line_number']
try:
with open(filename, 'r') as f:
lines = f.readlines()
start = max(0, line_number - 3)
end = min(len(lines), line_number + 3)
for i in range(start, end):
marker = ">>>" if i + 1 == line_number else " "
print(f"{marker} {i+1:3d}: {lines[i].rstrip()}")
except:
print("Cannot read source file")
else:
print(f"Invalid frame number {n}")
# Start interactive session
local_vars = {
'frames': frames,
'frame': frame,
'vars': vars,
'code': code,
'frames_data': frames_data,
'exception': exc_value
}
console = code.InteractiveConsole(locals=local_vars)
console.interact("Interactive frame debugger ready (type exit() to quit):")
# Run interactive debugger
interactive_frame_debugger()
Frame Performance Monitor #
import time
import functools
import sys
def frame_performance_monitor(func):
"""Decorator that monitors function performance and frame context"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
frame = sys._getframe()
try:
result = func(*args, **kwargs)
execution_time = time.time() - start_time
print(f"Performance Report:")
print(f" Function: {func.__name__}")
print(f" Execution time: {execution_time:.4f} seconds")
print(f" Called from: {frame.f_back.f_code.co_name}")
print(f" Arguments: {len(args)} args, {len(kwargs)} kwargs")
return result
except Exception as e:
execution_time = time.time() - start_time
print(f"Function {func.__name__} failed after {execution_time:.4f}s")
print(f"Error: {type(e).__name__}: {e}")
# Capture frame context for error analysis
exc_type, exc_value, exc_traceback = sys.exc_info()
print("Error frame context:")
tb = exc_traceback
while tb:
frame = tb.tb_frame
print(f" {frame.f_code.co_name} in {frame.f_code.co_filename.split('/')[-1]}:{tb.tb_lineno}")
tb = tb.tb_next
raise
return wrapper
# Usage example
@frame_performance_monitor
def slow_function(n):
"""Example function that might be slow"""
import time
time.sleep(0.1) # Simulate slow operation
if n < 0:
raise ValueError("n must be non-negative")
return sum(range(n))
# Test the monitor
try:
result = slow_function(100)
print(f"Result: {result}")
# Test with error
result = slow_function(-1)
except ValueError:
print("Error handling completed")
Usage Tips #
When to Use These Snippets #
- Basic frame extraction: Simple debugging and error investigation
- Stack trace walker: Understanding complex call chains
- Frame inspector class: Reusable debugging utilities
- Error context decorator: Automatic error context capture
- Lightweight inspector: Production environments with memory constraints
- Production logger: Enterprise applications requiring detailed error logs
- Interactive debugger: Development environments for deep investigation
- Performance monitor: Performance debugging with frame context
Performance Considerations #
- Frame inspection adds overhead - use judiciously in production
- Limit variable data captured to prevent memory issues
- Use generators for large call stacks
- Consider memory-efficient alternatives for long-running applications
Best Practices #
- Clean up frame references to prevent memory leaks
- Use context managers for automatic resource cleanup
- Filter sensitive data when logging frame information
- Implement configurable verbosity levels for production use
These code snippets provide comprehensive tools for when Python can error frame analysis is needed, helping you debug issues effectively while maintaining good performance characteristics.