Call stack profiler for Python that shows you why your code is slow
—
Profiling session data structures and persistence functionality for saving and loading profile results, combining multiple sessions, and accessing detailed timing and execution information.
Container for profiling data with metadata about execution timing, call stack samples, and system information.
class Session:
"""
Represents a profiling session containing collected performance data.
Attributes:
frame_records (list): Raw frame sampling data
start_time (float): Session start timestamp
duration (float): Total session duration in seconds
min_interval (float): Minimum sampling interval used
max_interval (float): Maximum sampling interval used
sample_count (int): Number of samples collected
start_call_stack (list[str]): Initial call stack when profiling began
target_description (str): Description of profiled target
cpu_time (float): Total CPU time consumed
sys_path (list[str]): Python sys.path at time of profiling
sys_prefixes (list[str]): Python sys.prefixes at time of profiling
"""
def __init__(
self,
frame_records: list[FrameRecordType],
start_time: float,
duration: float,
min_interval: float,
max_interval: float,
sample_count: int,
start_call_stack: list[str],
target_description: str,
cpu_time: float,
sys_path: list[str],
sys_prefixes: list[str]
):
"""
Initialize session with profiling data.
Args:
frame_records: List of captured frame samples
start_time: Unix timestamp when profiling started
duration: Total profiling duration in seconds
min_interval: Minimum sampling interval used
max_interval: Maximum sampling interval used
sample_count: Total number of samples collected
start_call_stack: Initial call stack when profiling began
target_description: Human-readable description of profiled code
cpu_time: Total CPU time consumed during profiling
sys_path: Python module search path during profiling
sys_prefixes: Python installation prefixes during profiling
"""
def save(self, filename: PathOrStr) -> None:
"""
Save session data to disk in JSON format.
Args:
filename: Path to save session data (.pyisession extension recommended)
"""
@staticmethod
def load(filename: PathOrStr) -> Session:
"""
Load previously saved session from disk.
Args:
filename: Path to saved session file
Returns:
Session object with loaded profiling data
Raises:
FileNotFoundError: If session file doesn't exist
JSONDecodeError: If session file is corrupted
"""
def to_json(self, include_frame_records: bool = True) -> dict[str, Any]:
"""
Convert session to JSON-serializable dictionary.
Args:
include_frame_records: Whether to include raw frame data
Returns:
Dictionary representation suitable for JSON serialization
"""
@staticmethod
def from_json(json_dict: dict[str, Any]) -> Session:
"""
Create session from JSON dictionary.
Args:
json_dict: Dictionary with session data (from to_json())
Returns:
Reconstructed Session object
"""
@staticmethod
def combine(session1: Session, session2: Session) -> Session:
"""
Combine two profiling sessions into one.
Args:
session1: First session to combine
session2: Second session to combine
Returns:
New session containing combined data
"""
@staticmethod
def current_sys_prefixes() -> list[str]:
"""
Get current Python sys.prefixes.
Returns:
List of current Python installation prefixes
"""Methods for accessing and working with the collected frame data within a session.
def root_frame(self, trim_stem: bool = True) -> Frame | None:
"""
Get the root frame of the call tree.
Parses the internal frame records and returns a tree of Frame objects
that can be rendered using a Renderer object.
Args:
trim_stem: Whether to trim the root stem frames before branches
Returns:
Root Frame object representing the call hierarchy, or None if session is empty
"""
def shorten_path(self, path: str) -> str:
"""
Shorten a file path to a more readable form, relative to sys_path.
Used by Frame.short_file_path for display purposes.
Args:
path: Full file path to shorten
Returns:
Shortened, more readable path string
"""from pyinstrument import Profiler
# Create and run profiling session
with Profiler() as profiler:
expensive_operation()
session = profiler.last_session
# Save session to disk
session.save('profile_data.pyisession')
# Later, load the session
from pyinstrument.session import Session
loaded_session = Session.load('profile_data.pyisession')
# Use loaded session with any renderer
from pyinstrument.renderers import HTMLRenderer
renderer = HTMLRenderer()
html_output = renderer.render(loaded_session)from pyinstrument import Profiler
from pyinstrument.session import Session
# Profile different parts of application
profiler = Profiler()
# First operation
profiler.start()
operation_a()
profiler.stop()
session_a = profiler.last_session
# Second operation
profiler.start()
operation_b()
profiler.stop()
session_b = profiler.last_session
# Combine sessions for comprehensive view
combined_session = Session.combine(session_a, session_b)
# Analyze combined results
profiler._last_session = combined_session
profiler.print()from pyinstrument import Profiler
from pyinstrument.session import Session
import json
with Profiler() as profiler:
process_data()
session = profiler.last_session
# Export to JSON
json_data = session.to_json()
with open('session.json', 'w') as f:
json.dump(json_data, f, indent=2)
# Import from JSON
with open('session.json', 'r') as f:
loaded_data = json.load(f)
restored_session = Session.from_json(loaded_data)from pyinstrument import Profiler
with Profiler() as profiler:
analyze_performance()
session = profiler.last_session
# Access session information
print(f"Duration: {session.duration:.3f} seconds")
print(f"CPU time: {session.cpu_time:.3f} seconds")
print(f"Samples: {session.sample_count}")
print(f"Target: {session.target_description}")
print(f"Start time: {session.start_time}")
# Access frame data
root = session.root_frame()
print(f"Root frame: {root.function} in {root.file_path}")from pyinstrument import Profiler
with Profiler() as profiler:
computation_task()
session = profiler.last_session
# Access raw frame sampling data
for call_stack, time_delta in session.frame_records:
print(f"Time: {time_delta:.6f}s")
for frame_info in call_stack:
print(f" {frame_info}")
print()PathOrStr = str | os.PathLike[str]
FrameRecordType = tuple[list[str], float]
ProcessorType = Callable[..., Frame | None]
class Frame:
"""Represents a single frame in the call stack."""
identifier: str
time: float
children: list[Frame]
parent: Frame | None
file_path: str | None
function: str | None
line_no: int | None
absorbed_time: float
attributes: dict[str, float]Install with Tessl CLI
npx tessl i tessl/pypi-pyinstrument