Skip to main content

varve

PyPI License

Varve runs Python pipelines and caches their outputs, so re-running only re-executes stages whose deterministic inputs, deterministic source dependencies, or managed artifacts require it. Source changes that Varve cannot classify safely open an explicit Stage-level review gate.

A varve is an annual layer of lake sediment — thin, ordered, and datable. Varve treats pipeline outputs the same way: each stage writes a materialized layer whose input key records config, external inputs, explicit values, and upstream artifact contents. When nothing changes, nothing re-runs; when something does, status tells you exactly what and why.

It runs on a single machine — no daemon, no database, no pipeline DSL. Stages are ordinary Python methods, and the run / status / reuse / invalidate / plan / ls / clean CLI is generated from your pipeline class. Varve is built for local experiments, evaluations, dataset preparation, render/compare jobs, and report generation, where Python is already the source of truth. It is not a distributed scheduler, a deployment platform, a data-version-control system, or a remote artifact store.

Install

Varve requires Python 3.10 or newer.

pip install varve

Quick start

Create demo.py:

from pydantic import BaseModel
from varve import Ctx, Pipeline, stage


class Config(BaseModel):
    prefix: str = "hello"


class Demo(Pipeline):
    Config = Config

    @stage(produces="items.txt")
    def prepare(self, ctx: Ctx) -> None:
        (ctx.out / "items.txt").write_text("alpha\nbeta\n")

    @stage(needs="prepare", produces="result.txt")
    def render(self, ctx: Ctx) -> None:
        items = ctx.input("prepare").read_text().splitlines()
        (ctx.out / "result.txt").write_text(
            "\n".join(f"{ctx.config.prefix} {item}" for item in items)
        )


if __name__ == "__main__":
    raise SystemExit(Demo.cli())

Run it twice, then look at what happened:

python demo.py run
python demo.py run
python demo.py status
python demo.py plan

The first run executes both stages and records their keys and artifacts under out/main/. The second run is a straight cache hit. Changing prefix, changing a declared external input, changing a Stage callable, or deleting result.txt makes the affected stage non-current for a reason status will name. Changes to surrounding pipeline code may instead open a Stage-level review gate that must be resolved with reuse or invalidate before a normal run.

How it works

Each stage declares upstream stages with needs=, non-stage dependencies with depends=, and outputs with produces=. Varve builds an input key from Config, declared inputs and values, and the actual fingerprints of upstream artifacts.

Running a stage writes a record into <output-root>/.varve/: the committed key plus the output paths, relative to the branch root. The next command recomputes the key and checks that the recorded artifacts still exist — a matching key is not a hit if the file it points to is gone. The store is latest-wins and guarded by an output-root lock.

Inside a stage, ctx.input("prepare") returns the single artifact of an upstream and ctx.inputs("prepare") returns all of them in deterministic order. Both require the name to appear in needs=.

Core capabilities

Deterministic source invalidation and review

Varve fingerprints each complete Stage callable AST, including its decorators, signature, docstring, and body. A callable change is part of the input key and produces needs-run · source-changed without Review. Python files or directories declared with Dependencies.sources have the same deterministic invalidation behavior. The remaining AST in the pipeline and callable definition files, plus paths declared with Dependencies.review_sources, forms the Review fingerprint. A changed Review fingerprint opens needs-review only when an existing success or validated partial could otherwise be reused: use reuse to keep it reusable or invalidate to require a rerun. Varve does not infer call graphs, imports, registries, or runtime dispatch. External data and non-Python runtime files belong in Dependencies.inputs; explicit values belong in Dependencies.values.

Resumable batch stages

A @batch_stage iterates deterministic work through ctx.resume(...) and yields the files each item produces. Interrupt a run and the next one skips the indexes that already finished, continuing the same keyed batch. Varve schedules stages serially; a stage body is still free to use asyncio, process pools, or long-lived clients internally.

Matrix stages

Stack @matrix(...) over a stage to expand a Cartesian product of axes into independently keyed cells. Shared axes align dependencies automatically; axes that exist only upstream become a deterministic fan-in.

from varve import Axis, Ctx, Pipeline, matrix, stage

BENCH = Axis("bench", ["ocrbench", "unimer"])
MODEL = Axis("model", ["small", "large"])


class Evaluation(Pipeline):
    Config = Config

    @matrix(BENCH, MODEL)
    @stage(produces="score.json")
    def score(self, ctx: Ctx, *, bench: str, model: str) -> None:
        ctx.cell_out.mkdir(parents=True, exist_ok=True)
        evaluate(bench, model, ctx.cell_out / "score.json")

Each cell gets a concrete identity like score@bench=unimer,model=large and its own input key, attempt, partial state, success record, and artifact directory under .matrix/score/bench=unimer/model=large/. Review Decision belongs to the logical score Stage and applies to all relevant cells through one base-stage ReviewRecord. A StageSelector may name the base, a partial subset such as score@bench=unimer, or one concrete cell for execution and status; Review commands accept only the base Stage name. Large matrices fold to one line per base stage in run and status output — keeping concrete failures and slow cells visible — and --expand shows every cell.

Branches and temporary runs

An optional varve.yaml splits a branch's semantic config from its active matrix axes. Set manual: true alongside them to exclude a branch from default top-level bulk runs; explicit module and generated pipeline commands still run it. This selection policy does not change cache keys or stored results. Persistent branches materialize under out/<branch>/. run --override JSON spins up an isolated throwaway branch under out/.tmp/<branch>/, snapshotting both the validated Config and the active axes so generated commands and top-level commands with --include-temp can find it later.

Generated and top-level CLIs

Every Pipeline gets seven commands:

Command Purpose
run Evaluate cache decisions and execute the selected stages.
status Explain each Stage's input key, materialization, artifact, source relationship, and Review state.
plan Exact-probe the selected stages and draw their logical Stage topology.
ls Show branch-independent stage templates and matrix axes.
clean Safely remove a whole output root or a recorded downstream closure.
reuse Keep existing materializations reusable after Review-source changes.
invalidate Mark existing materializations as needing a rerun after Review-source changes.

Generated reuse and invalidate default to every Stage with a current Review candidate; repeat --stage BASE_STAGE for a topology-ordered union. They only write fingerprint-bound Stage ReviewRecords: they do not execute Stage bodies, alter success records, or immediately delete partial state. A normal run stops before executing any Stage body when its selection or required external upstreams contain needs-review. run --force reruns selected concrete stages without writing ReviewRecord; external upstreams that are only being reused must still pass the normal Review gate.

The top-level varve command finds existing stores from their manifests. varve ls exact-evaluates the discovered branches and reports the user-facing MODULE selector, BRANCH, and effective STATUS. A selector is the package that owns a store's output directory, which is also the name python -m accepts whenever that package has a __main__.py; declare varve_name on a pipeline to choose a different one. Any unambiguous dotted suffix of a selector and the exact persisted module are accepted too. varve ls MODULE, status MODULE, run MODULE, reuse MODULE, invalidate MODULE, plan MODULE, and clean MODULE reuse the generated command backends and renderers. Single dynamic commands use COMMAND MODULE [OPTIONS], so MODULE precedes pipeline-specific Args flags.

varve run runs discovered branches except those marked manual: true; varve run --all includes manual branches. Both run forms, reuse --all, and invalidate --all operate on entries selected by --root, --prefix, --branch, and --include-temp; bulk run also accepts --rehash. Bulk Review uses each pipeline's default Args and records each store independently. Bulk run filters manual branches before pipeline import or state evaluation, skips hits and needs-review, executes eligible branches, refreshes observations after every attempt, and reports exact final state with separate executed, hit, and manual-skipped branch counts. --all neither forces cache hits to rerun nor includes temporary stores without --include-temp; YAML-only branches without existing stores are never initialized by bulk run. Use repeatable varve ls --status STATUS to filter evaluated rows.

Documentation

  • User guide: stage authoring, keys, batch resume, matrix, branches, CLI behavior, dashboard, and recovery.
  • Architecture: package boundaries, store invariants, graph expansion, probing, and implementation decisions.
  • Contributing: setup, public API policy, style, commits, and releases.
  • Changelog: released behavior and migration notes.

Platform and stability

Varve is currently Unix-only, because output locking uses fcntl. Source fingerprints use ast.dump, so a CPython minor-version upgrade may rebuild cached stages.

Varve follows SemVer, but 0.x releases are alpha: a minor release may change the public API or the .varve/ store schema, so check the changelog before upgrading.

License

MIT. See LICENSE.

Release files for varve 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for varve 0.6.0
File Size Uploaded
varve-0.6.0.tar.gz 184.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for varve 0.6.0
File Interpreter ABI Platform
varve-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 273.4 kB

Release files / varve-0.6.0.tar.gz

Download URL varve-0.6.0.tar.gz
Size 184.2 kB
Tags Source
SHA-256 checksum
How to use checksums
683897703857c7c649868b096d1b1e74c02eaabf9c5ca3167463649a0d353a8c
BLAKE2b-256 checksum
How to use checksums
0fa5845f4e2e6438285e6a2cc225a9c11c3d7753ca0709f76d94c6c394148965
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / varve-0.6.0-py3-none-any.whl

Download URL varve-0.6.0-py3-none-any.whl
Size 89.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
377b9825ed24f6e5da51eecc611d41dec934e6627229eaa868e5d49b86898300
BLAKE2b-256 checksum
How to use checksums
040fc9c8c6ee8a7e01c0056e169751b1ff67d2b22518f5b34c98b1bc27c4a301
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page