Skip to main content

matview

Materialized views for derived values.

CI Docs PyPI Python versions License: MIT

Documentation: https://tresoldi.github.io/matview/

You declare recipes: functions that compute a value from parameters and from other recipes' outputs. You ask the cache for a value; it returns the stored one if it is current, and computes it if not, recursively resolving whatever that computation turns out to need. Results are memoized in SQLite and reused across processes, machines and runs.

from matview import Cache, SqliteStore, recipe

@recipe(
    version=1, codec="arrow", index=("symbol",), on_miss="raise",
    deterministic=False, max_age=18 * 60 * 60,
)
def ohlcv(ctx, *, symbol: str, interval: str = "1d", adjust: str = "split"):
    return vendor.bars(symbol, interval, adjust)

@recipe(version=3, codec="arrow-series", index=("symbol",))
def sma(ctx, *, symbol: str, window: int, interval: str = "1d"):
    bars = ctx.get("ohlcv", symbol=symbol, interval=interval)
    return bars["close"].rolling_mean(window)

cache = Cache(store=SqliteStore("cache.db"))
cache.get("sma", symbol="AAPL", window=200)

The dependency graph is never declared. Every ctx.get a recipe makes is recorded and written alongside the artifact in one transaction, so the graph is always an accurate record of what actually happened: cycles are caught by the in-flight stack rather than by static analysis, and a recipe that takes a different branch on different parameters records different dependencies, correctly.

Staleness is decided by content digests rather than timestamps, which buys early cutoff. Revise one bar from 2019 and every dependent is marked stale in one transaction; ask for the 200-day moving average and it recomputes, comes out byte-identical because the window never reaches 2019, and nothing above it is touched at all.

This is a build system for data rather than files, in the sense of Build Systems à la Carte (Mokhov, Mitchell, Peyton Jones): a suspending scheduler with constructive traces.

Install

pip install matview-cache            # core: SQLite and the json codec, nothing heavy
pip install 'matview-cache[arrow]'   # polars DataFrames and Series via Arrow IPC
pip install 'matview-cache[pandas]'  # the same Arrow bytes, decoded to pandas
pip install 'matview-cache[npy]'     # numpy arrays
pip install 'matview-cache[all]'

matview requires SQLite with JSON1. SQLite 3.38 or newer includes JSON1 by default; an older or minimal SQLite build must provide JSON1 itself. SqliteStore checks this on every open, including read-only opens, and refuses the database with its linked SQLite version when the capability is absent.

What it does

Three states, never conflated. An artifact is absent (no row), stale (a row whose valid flag is clear) or valid. Stale is not absent: reading a stale artifact resolves its dependencies, recomputes its fingerprint and compares. If it matches, the flag flips back and the stored bytes are returned without recomputing anything. Nothing is ever deleted on invalidation, because deleting collapses absent and stale and loses early cutoff permanently.

on_miss is a ceiling, not a default. A recipe may declare on_miss="raise", and it is then protected everywhere it appears as a transitive dependency — a backtester asking for a Sharpe ratio with on_miss="compute" cannot fire a paid vendor request six levels down that it never mentioned. Ingesting is an explicit top-level act. Protected misses are collected and reported in one batch, so a cold run tells you what to ingest in a single pass rather than one symbol per attempt.

force is local. Forcing one indicator during development recomputes that node and nothing beneath it. force_deps is the transitive one, and the ceiling still applies under it.

Freshness is generic; scheduling is not. A recipe may declare max_age in seconds. After that interval since its last successful computation, matview treats the value as needing recomputation. Successful checks advance a separate refresh clock even when the bytes are unchanged; computed_at retains its content-history meaning. Matview does not know about trading sessions, business days, or cron: callers decide when reads happen. Expiry is checked when that recipe is resolved; matview does not walk dependencies of every valid cache hit merely to inspect their clocks. A scheduler should therefore refresh source roots before asking for their derived values.

Deadlines, not budgets. deadline is wall-clock seconds, converted to an absolute instant at the top-level call and carried down the recursion, because per-recipe cost estimates are guesses that go stale whereas elapsed time is a measurement.

Recipes in any language. A foreign recipe is a TOML manifest plus an executable. It declares its inputs upfront, which makes it the privileged case: a declared graph can be cycle-checked and planned before anything executes. One JSON job file is the only argument; the result is read from a file, never from stdout, so a stray print cannot corrupt a value.

Inspecting a cache

matview info   --db cache.db      # what this file is, whatever version wrote it
matview stats  --db cache.db
matview ls     --db cache.db --recipe sma --invalid
matview show   --db cache.db --id 12
matview verify --db cache.db      # every payload against its recorded digest

matview lineage --db cache.db --id 12       # what produced this?
matview impact  --db cache.db --changed 12  # what breaks if this changes?
matview cost    --db cache.db --by-recipe   # where the compute time goes

Read-only, --json on everything, and no dependencies beyond the core. Metadata needs nothing imported; only a key given as partial parameters and plan need --recipes MODULE. See the command line guide.

The last three walk the dependency graph the cache recorded as it ran — the input closure of a value, what a change or a version bump or a drop would reach, and what a number cost to compute — and render it as an indented tree, JSON, Graphviz DOT or Mermaid. See graph queries; impact --bump in particular is the clearest explanation of why a dependency's recipe version is deliberately not in a dependent's fingerprint.

Examples

Two, one financial and one not, to keep domain assumptions out of the core. Both run without network access and are exercised by the test suite.

uv run --extra arrow python examples/finance/pipeline.py        # early cutoff, end to end
uv run --extra npy   python examples/linguistics/pipeline.py    # a foreign recipe in the graph

Design documents

File What it is
docs/design-spec.md The full design spec. The source of truth for behaviour.
SPEC.md Canonicalization and fingerprints, verbatim and independently versioned. The cross-language interoperability contract.
DECISIONS.md Every choice made where the spec was silent, with the reasoning.
ROADMAP.md Candidates for future work, and what is deliberately excluded.
CHANGELOG.md Release notes. Note that SPEC.md is versioned separately and deliberately.
RELEASING.md How a release is cut, and the account-level setup it assumes.

tests/golden_vectors.json and tests/golden_codecs.json are fixed inputs to fixed digests. They are the regression guard on the interoperability contract and the reference a second-language implementation validates against; regenerating either to make a test pass defeats the point of having them.

Not in v1

Distributed locking or work leases; remote backends (the seam exists, the implementation does not); a consumer for the task rows that on_miss="enqueue" writes; incremental or append-only computation; point-in-time correctness; a web UI, daemon or server.

Development

make install        # sync the venv with every extra
make help           # what else there is
make check          # everything CI runs: quality, both test passes, the codec floor

make test-core runs the suite with no codec extras installed, which must pass too — the core is meant to install without them. make codec-floor re-checks the golden codec bytes against the oldest supported polars, pyarrow and numpy; a codec that drifts does not error, it just stops finding everything already stored.

Install the git hooks once with uv run --extra dev pre-commit install. They run ruff, ruff-format and mypy at exactly the versions pinned in the dev extra.

Documentation is MkDocs Material with an API reference generated from the docstrings: make docs-serve to preview, make docs to build it the way CI does (--strict).

Status and contributions

matview is the cache foundation of Pharos (daily-swing alerts), which depends on it and registers its finance recipes into a caller-owned Registry.

Published for use rather than for collaboration. It is released because it is useful, and it is maintained for the projects that depend on it; there is no ambition to run it as a community project.

Bug reports are welcome as issues, especially ones with a database or a recipe that reproduces the problem. Pull requests may sit unreviewed — please open an issue before writing one, so you do not spend effort on something that will not be merged. Anything under Not in v1 or under "Deliberately not doing" in ROADMAP.md is excluded on purpose rather than for want of a patch.

For anything exploitable, do not open an issue: see SECURITY.md, which also states plainly what matview trusts — recipes are code, and a cache database from an untrusted source is untrusted input.

Citing

CITATION.cff carries the metadata, and GitHub renders it as a "Cite this repository" box in the sidebar.

License

MIT. See LICENSE.

Metadata

Release files for matview-cache 0.1.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 matview-cache 0.1.0
File Size Uploaded
matview_cache-0.1.0.tar.gz 190.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for matview-cache 0.1.0
File Interpreter ABI Platform
matview_cache-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 274.7 kB

Release files / matview_cache-0.1.0.tar.gz

Download URL matview_cache-0.1.0.tar.gz
Size 190.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e19442b5e8b9936f57455da7d7fb08e453e80334c164c20d74e352b3e029252c
BLAKE2b-256 checksum
How to use checksums
d88b926405ff729ed3a76e22f257c221aadc3e6b199d5fd9ebd8d4cfa7bb4e02
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 Oct 13, 2026.

Transparency log

Release files / matview_cache-0.1.0-py3-none-any.whl

Download URL matview_cache-0.1.0-py3-none-any.whl
Size 84.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7fcf618ca1b3752be662a1c6076fbc5e89f0dbadc04da010bba9343e5b8469e7
BLAKE2b-256 checksum
How to use checksums
b5eb31cd865319c359efc59ac77813c870dea74eecc8fc477a91c7bfbfb52f9a
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 Oct 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

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