Record once, replay anywhere: a time-travel debugger for Python with a visual browser-based player.
Project description
tracesnap
Record once, replay anywhere. A time-travel debugger for Python with a visual browser-based player. Run your code under instrumentation, get a JSON trace, then scrub through it line-by-line in three different views (text, flowchart, call graph) — no re-execution.
Status: alpha. Stdlib-only core, plug-and-play integrations for Flask, Django, and FastAPI.
Why
pdb lets you pause once. print() litters your code. Profilers count
nanoseconds but don't show you state. tracesnap captures every line, every
assignment, every branch, and every outbound HTTP call into one
self-contained JSON file you can replay, share in a PR, or attach to a
bug report.
Install
pip install tracesnap # core + CLI + bundled players
pip install tracesnap[flask] # core + Flask integration
pip install tracesnap[django]
pip install tracesnap[fastapi]
pip install tracesnap[all]
30-second quickstart
Record a script:
tracesnap record examples/sample_complex.py --out trace.json
Open it in your browser:
tracesnap view trace.json
You'll get a three-view player: source code on the left, and one of:
- Text view — current step, call stack, full per-variable history.
- Simulator — animated flowchart with loops as cycle boxes, branches with the taken arm tagged ✓, variable chips that flash when changed.
- Call graph — every function as a node, edges labelled with args going in and return values coming back; click a node for per-call details.
All three views consume the same trace.json and switch via the buttons
in the header.
Library use
Three equally-valid entry points, same underlying engine:
import tracesnap
# 1) Context manager (preferred for explicit scope)
with tracesnap.record(trace_id="demo") as out:
do_stuff()
# out.path -> "trace.json"
# out.event_count
# out.trace -> the trace dict in memory
# 2) Decorator (records every call to the wrapped function)
@tracesnap.record(trace_id="checkout")
def checkout(items, coupon):
...
# 3) Imperative (lowest level; what the integrations use under the hood)
tracesnap.start_recording(trace_id="x", source_files=[__file__])
try:
do_stuff()
finally:
trace = tracesnap.stop_recording()
tracesnap.write_trace(trace, "trace.json")
Framework integrations
Flask
from flask import Flask
from tracesnap.integrations.flask import TraceSnap
app = Flask(__name__)
TraceSnap(app, output_dir="traces")
Every request → one traces/req-<timestamp>.json. Open any of them with
tracesnap view.
Django
# settings.py
MIDDLEWARE = [
...,
"tracesnap.integrations.django.RecorderMiddleware",
]
TRACESNAP = {
"output_dir": "traces",
"source_files": ["/path/to/your/app/__init__.py"],
}
FastAPI
from fastapi import FastAPI
from tracesnap.integrations.fastapi import install
app = FastAPI()
install(app, output_dir="traces", source_files=[__file__])
Async note: sys.settrace is per-thread; contextvars are per-task. For
a single handler per request (the common case) this works correctly.
Concurrent asyncio.gather(...) of multiple traced sub-tasks within a
single request boundary share the same recording session — not
recommended for production. Document/test your specific use.
What gets recorded
Every event carries seq, ts, depth, line, parent_seq plus
type-specific fields:
| type | fields |
|---|---|
call |
func, args (each value with repr, type, redacted) |
line |
func |
assign |
var, scope, value, prev, change_index |
branch |
node_id, taken ("if" / "else") |
loop |
node_id, iteration (0-based) |
return |
func, value |
extcall |
kind, verb, target, status, duration_ms, started_ts, ended_ts |
Values are {repr, type, id, truncated, redacted}. repr is capped at
120 chars (truncated: true if hit). Variables named password,
token, secret, authorization, api_key get <redacted> — both as
function args and as locals. Configurable via redact_names= to any
entry point.
parent_seq links events into a tree:
- Inside a
forbody, every event hasparent_seqpointing at the current iteration'sloopevent. - Inside an
ifarm, every event points at thebranchevent. - Top-level events in a function have
parent_seq: null. - A
callevent'sparent_seqis the caller's context at the call site (so a call made inside anifarm points at the branch event in the caller, not at the new frame).
Full spec: docs/trace-format-v0.1.md.
CLI
tracesnap record PATH [--out trace.json] [--id NAME] [--redact NAMES]
tracesnap view PATH [--view text|simulator|graph] [--port 0] [--no-browser]
tracesnap --version
view starts a tiny stdlib http.server on a free port (no extra
dependencies), copies the bundled player HTMLs into a tmpdir alongside
your trace, and opens your default browser pointed at
<view>.html?trace=<file>. Switch views in-app via the header buttons.
Known issues / edges
- Recursion: the player keys frames by stack position, so recursive
calls collide. Roadmap item: per-call
frame_id. - Assign attribution is one line late (
sys.settracefires before a line runs; we attribute the diff to the previous line). Documented in the trace-format spec. - Value-change vs assignment: we log value changes, so in-place
mutation like
xs.append(y)doesn't emit. Use rebinding (xs = xs + [y]) to see growth. extcallscope: onlyrequests.Session.sendandurllib.request.urlopenare wrapped.httpx, DB drivers, and stdlibsocketare not (yet).- Async: per-task
contextvarswork; per-taskthreading.settracedoes not (yet). Concurrent traced sub-tasks share the same session.
Roadmap
- SQLite backend for traces > ~10k events (single-trace decision; JSON stays default).
depends_onfield on assigns → true data-flow graphs in the call graph view.exceptionevents on unwind.httpx+ DB driverextcallcapture.- Per-task
threading.settracefor parallel-async support.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tracesnap-0.1.0.tar.gz.
File metadata
- Download URL: tracesnap-0.1.0.tar.gz
- Upload date:
- Size: 115.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1210418d1f41265bcc697d1e3a4203c9b3d438b43ff9281b5774f648347ad775
|
|
| MD5 |
e25cfccfcf52482f94f02d4e3db91f23
|
|
| BLAKE2b-256 |
e1f965e570aa00f3175407d17915e7c51b8ab8fd9065481576a362b6465252c9
|
Provenance
The following attestation bundles were made for tracesnap-0.1.0.tar.gz:
Publisher:
publish.yml on bangi98/mindplayer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tracesnap-0.1.0.tar.gz -
Subject digest:
1210418d1f41265bcc697d1e3a4203c9b3d438b43ff9281b5774f648347ad775 - Sigstore transparency entry: 1614814966
- Sigstore integration time:
-
Permalink:
bangi98/mindplayer@4339e7639811b01020c2edb5558dcb5833f8497d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/bangi98
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4339e7639811b01020c2edb5558dcb5833f8497d -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file tracesnap-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tracesnap-0.1.0-py3-none-any.whl
- Upload date:
- Size: 107.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
444fe447f1534600bbb66f524316f506e1b78e20529ef32a9275daf63b6aa8f0
|
|
| MD5 |
611345a8aa2f141b13362c475ab1c6c0
|
|
| BLAKE2b-256 |
d3f5e87eaca54fd59dbbd668a12368c29dbe0bf455ec673dd63882f2ae97a630
|
Provenance
The following attestation bundles were made for tracesnap-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on bangi98/mindplayer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tracesnap-0.1.0-py3-none-any.whl -
Subject digest:
444fe447f1534600bbb66f524316f506e1b78e20529ef32a9275daf63b6aa8f0 - Sigstore transparency entry: 1614814984
- Sigstore integration time:
-
Permalink:
bangi98/mindplayer@4339e7639811b01020c2edb5558dcb5833f8497d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/bangi98
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4339e7639811b01020c2edb5558dcb5833f8497d -
Trigger Event:
workflow_dispatch
-
Statement type: