Skip to main content

Write Python lecture code. Get an interactive viewer on GitHub Pages.

Project description

lectrace

Write Python lecture code. Get an interactive step-through viewer on GitHub Pages — automatically.

lectrace traces your Python code line by line using sys.settrace, captures variable state and rendered content at each step, and produces a static React app that lets anyone step through the execution with arrow keys. No Node.js, no configuration, no build step for the user — just Python.

Inspired by edtrace by Percy Liang.


Install

uv add lectrace
# or
pip install lectrace

Requires Python 3.11+. Zero mandatory dependencies — lectrace uses the standard library only. numpy, torch, and sympy are detected and rendered automatically if they are already installed in your environment.


The recommended pattern

A lecture file is a plain Python script. Define main() first, put helper functions below it, and call main() at the end with the standard guard. This is valid Python — functions defined below main() are resolved at call time, not definition time.

# 01_binary_search.py
from lectrace import text, note, plot

def main():
    text("# Binary Search")
    text("Finds a target in a sorted array in $O(\\log n)$ time.")

    arr = [2, 5, 8, 12, 16, 23, 38, 42]  # @inspect arr
    result = binary_search(arr, 23)        # @inspect result

    text(f"Found 23 at index `{result}`.")
    note("Binary search only works on sorted arrays.")

def binary_search(arr, target):
    lo, hi = 0, len(arr) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

if __name__ == "__main__":
    main()

What this gives you:

  • Run python 01_binary_search.py — works as a plain script, no lectrace involvement
  • Run lectrace serve — opens the interactive viewer in your browser
  • Push to GitHub — viewer deploys automatically to GitHub Pages

The tracer only traces code that runs inside main(). Imports and function definitions are invisible — they're just setup. Other functions appear in the viewer only when main() actually calls them.


How tracing works

lectrace loads your file silently (running imports and defining functions), then calls main() with the tracer active. The result:

  • Module-level code (imports, def statements) — never generates a step
  • main() and every function it calls — stepped through line by line
  • Functions defined but never called — invisible

When execution enters a helper function, the viewer shows the def line first with the function's arguments already in the variable panel, then steps through the body line by line. When the function returns, the viewer jumps back to the call site.


Variable panel

The variable panel on the right shows state at each step:

  • Inside main() — only variables explicitly marked with # @inspect are shown
  • Inside any called function — all local variables are shown automatically, no directives needed
  • Call stack — displayed above the variables when inside a helper function, showing the full chain of calls
  • New variables — highlighted in green when they first appear
  • Changed variables — highlighted in amber when their value changes

Directives

Directives are inline comments that control tracing and display:

Directive Effect
# @inspect x y Show x and y in the variable panel after this line (use in main())
# @inspect Show all local variables at this line in main()
# @clear x Remove x from the variable panel
# @stepover Execute this line without stepping into any calls it makes
# @hide Run this line silently — never shown in the viewer

Rendering functions

Call these anywhere inside main() or any function it calls:

Function What it renders
text("# Heading") Markdown with LaTeX math ($...$ inline, $$...$$ display)
text("...", verbatim=True) Monospace, whitespace preserved
image("fig.png", width=400) Local file or remote URL (cached)
video("demo.mp4") Embedded video with controls
link(my_function) Clickable jump to that function in the viewer
link(title="Paper", url="...", authors=["Smith"], date="2024") Reference card with hover metadata
plot({...}) Interactive Vega-Lite chart
note("speaker annotation") Presenter note shown as a styled callout
system_text(["python3", "--version"]) Shell command output as verbatim text

Custom type rendering

Implement __lectrace__ on any class to control how it appears in the variable panel:

class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

    def __lectrace__(self):
        return {
            "val": self.val,
            "left": self.left.val if self.left else None,
            "right": self.right.val if self.right else None,
        }

Without __lectrace__, nested objects show their full repr. With it, you control exactly what students see.


Viewer

The viewer is a mobile-responsive React app that works in any browser — no installation required for viewers.

Keyboard shortcuts (desktop):

Key Action
or l Step forward
or h Step backward
Shift+→ or j Step over forward (skip into sub-calls)
Shift+← or k Step over backward
u Step out of current function
R Toggle raw code view
A Toggle reveal animation
E Toggle variable panel
F Toggle fullscreen

Mobile: swipe left/right to step, tap the Variables bar at the bottom to expand the variable panel, use the fixed navigation bar for step controls.


File naming

Pattern Behaviour
01_intro.py Lecture — appears in sidebar, traced and deployed
02_sorting.py Lecture — sidebar order follows alphabetical sort
_utils.py Helper — imported normally, never traced or shown

Number prefixes control sidebar order. Helper files starting with _ are ignored by lectrace entirely.

my-course/
  _data.py            ← shared data, ignored by lectrace
  01_intro.py         ← first in sidebar
  02_complexity.py    ← second
  03_sorting.py       ← third

CLI

lectrace serve                  # build + serve all lectures at http://localhost:7000
lectrace serve 01_intro.py      # serve a single file
lectrace build --output _site   # build static site for deployment
lectrace init                   # generate GitHub Actions workflow + lectrace.toml
lectrace run 01_intro.py        # execute and print trace stats (no server)

Deploy to GitHub Pages

lectrace init   # generates .github/workflows/lectrace.yml
git add .
git commit -m "add lectures"
git push

Enable GitHub Pages in your repo settings (Source: GitHub Actions). Every push to main rebuilds and redeploys automatically.


How it works

  • Tracer — loads the module without tracing (so imports and def statements are invisible), then activates sys.settrace and calls main(). Every step the viewer shows is inside a function that was actually called.
  • Serializer — converts Python values to JSON. Primitives are direct. Collections recurse. numpy/torch/sympy are imported lazily only when encountered.
  • Builder — discovers lecture files, runs each through the tracer, writes traces/*.json plus a traces/index.json manifest. Incremental: files are skipped if their SHA-256 hash hasn't changed.
  • Viewer — a pre-built React + TypeScript SPA bundled into lectrace/_static/ and shipped inside the pip package. Uses HashRouter so it works at any URL depth with zero configuration. Math via KaTeX, charts via Vega-Lite, syntax highlighting via highlight.js.

Documentation

Full documentation: https://praisegee.github.io/lectrace/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lectrace-1.1.1.tar.gz (1.5 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lectrace-1.1.1-py3-none-any.whl (1.4 MB view details)

Uploaded Python 3

File details

Details for the file lectrace-1.1.1.tar.gz.

File metadata

  • Download URL: lectrace-1.1.1.tar.gz
  • Upload date:
  • Size: 1.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lectrace-1.1.1.tar.gz
Algorithm Hash digest
SHA256 9839c164935caa2c01d857fa079014b48255c83ea83d2e17d20335377ad27a4c
MD5 454ce52eca359dde0c9d907c1b34d188
BLAKE2b-256 e278e5d8a10514ea57d4f38aa6edac188094c2003d948d2445a802a6ac85ac30

See more details on using hashes here.

Provenance

The following attestation bundles were made for lectrace-1.1.1.tar.gz:

Publisher: publish.yml on praisegee/lectrace

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lectrace-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: lectrace-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 1.4 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lectrace-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 891aaf816709c80d562443cb5a59f2a9dca1f074df988da8b5cc7bceaccd963e
MD5 10a257b16126c12251f4de820fd73efe
BLAKE2b-256 fedb572ee435141043c20ab5caa089603047a5f388761cb7e55291a0da5e4984

See more details on using hashes here.

Provenance

The following attestation bundles were made for lectrace-1.1.1-py3-none-any.whl:

Publisher: publish.yml on praisegee/lectrace

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page