Pure-Rust test engine for Python · no pytest at runtime

Your tests, forced through the fastest water.

A tide race is where the sea is squeezed through a narrow channel and accelerates into the fastest navigable current on the coast. tiderace does that to your test suite: collection, fixtures, scheduling, isolation, coverage and impact analysis all live in compiled Rust. A tiny Python shim only imports your tests and calls their bodies. Your suite stops being a tax.

~90× warm, nothing changed ~5 ms inner loop, 1 test changed 509 fixture tests · numpy / sqlite Windows parallel, no fork()

Ships as tiderace (one-shot) & tiderace-daemon (warm server)

The boldest thing a test runner can do

Watch what doesn't run.

tiderace records a per-test source footprint with CPython sys.monitoring and content-hashes everything it touches. Change one file and only the tests that actually depend on it re-run. Change nothing and the interpreter is never even launched. Touch a file below.

fx_corpus · 509 tests
ran 0 skipped 509 wall

Touch a file. See the ripple.

edit one of these

Nothing changed. Nothing runs — the interpreter isn't even launched.

Why it's actually fast — not a trick

Pay for isolation only where a test needs it.

Fork-per-test is the tax everyone quietly pays. tiderace classifies each test and runs it the cheapest sound way. Most tests never touch global state — so most tests never pay.

Tier 1 · pure

In-process, no fork

Deterministic tests that don't mutate shared state run straight in the interpreter. No process spawn, no page-table copy, no teardown.

~90×vs fork-per-test
Tier 2 · mutating

In-process, snapshot & restore

State-mutating tests get a captured snapshot of module globals and os.environ, then a restore afterward — isolation without a whole new process.

~5–14×vs fork-per-test
Tier 3 · opaque

Forked — only when it must be

The genuinely un-snapshotable cases still fork for full isolation. Correctness first; you just don't pay for it everywhere.

the classic cost

Sound by construction: a module that can't be snapshotted always falls back to fork. On Windows there's no fork() — so an opt-in sub-interpreter tier (CPython 3.14+, PEP 684 per-interpreter GIL) delivers parallel, no-fork execution there too.

The numbers, measured — not vibes

509 fixture tests. hyperfine. numpy & sqlite.

The corpus is real (fx_corpus), the tool is hyperfine, the comparison is honest — including the run where tiderace only wins 1.4×.

Cold · full run
1.4×
A clean full run — no cache to lean on. Still ahead, honestly modest.
Warm · no change
~90×
Nothing changed since last run. Content-addressed outcomes make it nearly free.
Warm · 1 test changed
~50–70×
The inner loop you live in. Impact analysis re-runs only what moved.

Method: fx_corpus (509 fixture tests, numpy/sqlite), measured with hyperfine. tiderace runs its own engine — collection, fixture graph, scheduling, hashing and impact analysis are all native Rust. There is no pytest in the loop at runtime. Reproduce the benchmarks →

The engine room

A build system, aimed at your tests.

Not a pytest plugin. Not a wrapper. tiderace owns the whole pipeline in compiled Rust and treats a test outcome like a build artifact — content-addressed, cacheable, portable.

Content-addressed outcomes

A result computed in CI is reusable on any machine. An unchanged test is free — you don't recompute what you already know. This is a build cache for correctness.

Impact analysis, natively

Per-test footprints via sys.monitoring plus content hashing. No change means nothing runs — the interpreter isn't launched at all.

Windows-first parallelism

No fork()? No problem. An opt-in sub-interpreter tier on CPython 3.14+ (PEP 684 per-interpreter GIL) runs no-fork isolation in parallel — on Windows too.

Rust owns the pipeline

Collection, the fixture graph, scheduling, hashing and impact analysis run at native speed. The Python side is a shim that imports tests and calls their bodies. That's it.

Migrate off pytest with one command

The tiderace migrate codemod auto-maps the vast majority of real suites — so you're not rewriting tests by hand to try it. Read the migration guide →

91%auto-mapped, avg
95%click
99%anyio
83%flask

Stop paying the test tax.

Point tiderace at your suite, run the codemod, and let the current do the work. Unchanged tests are free — everywhere, on every machine.

$ git clone github.com/snoodleboot-io/tiderace && cd tiderace/engine && cargo build --release