Skip to main content

pycasher

Cache Python function results and their side effects — stdout, stderr, and filesystem writes — with automatic invalidation.

uv add pycasher

If you want the casher CLI outside a project environment, install it as a tool instead:

uv tool install pycasher

What makes it different

Most caching libraries cache return values. casher also captures and replays:

  • stdout/stderr printed during execution
  • Files written by the function (restored from cache on hit)
  • Files read by the function (used as cache keys — change a true upstream input file, cache auto-invalidates)

No manual file declarations needed. casher discovers file I/O automatically via strace (subprocess mode) or wrapped Python file handles plus tracked shutil.copyfile()-based copies in in-process mode. Files that are written before they are first read during one invocation are treated as generated outputs, not cache inputs.

On Linux, subprocess=True is the authoritative dependency mode: cache reuse is validated against the exact implicit input paths recorded during the last successful execution, including imported project source files discovered in the child process. subprocess=False remains useful, but only as best-effort Python-level tracking.

Usage

from casher import cached, expand_input_dir

@cached
def train(data_path: str, output_path: str, lr: float = 0.01) -> dict:
    df = read_csv(data_path)
    model = fit(df, lr=lr)
    save(model, output_path)
    return {"accuracy": model.score}

# First call — runs function, traces file I/O, caches everything
result = train("train.csv", "model.pkl")

# Second call — instant replay from cache (model.pkl restored too)
result = train("train.csv", "model.pkl")

# Change train.csv — casher detects it, re-runs automatically

For directory-shaped inputs, keep the argument semantics explicit instead of making every directory Path recursive by magic:

from pathlib import Path

from casher import cached, expand_input_dir


@cached(input_files=lambda data_dir: expand_input_dir(data_dir, "*.csv"))
def build_dataset(data_dir: Path) -> int:
    return len(list(data_dir.glob("*.csv")))

Path arguments that point to files are hashed by file content for the function-argument portion of the cache key. Auto-discovered input files remain path-sensitive and content-sensitive.

If you pass an existing filesystem path as a plain str, casher warns once per parameter and process. String arguments still hash as strings; Path arguments make path-aware hashing intent explicit.

Declared output paths are treated differently: Path arguments that are listed in output_files= or fall under output_roots= are hashed by path identity, not file content. That keeps cache keys stable when the function creates those outputs during the first run.

casher now also learns this automatically for common workflow signatures. If a Path argument is observed during execution to be a generated output, later lookups treat that argument as an output-path identity even without explicit output_files= / output_roots= declarations.

Workflow-style functions can declare output directories explicitly to keep reads under those roots out of input_files:

from pathlib import Path

from casher import cached


work_dir = Path("work")


@cached(output_roots=[work_dir], replay_outputs="if-missing")
def assemble_workset() -> Path:
    generated = work_dir / "reference" / "mworld.par"
    generated.parent.mkdir(parents=True, exist_ok=True)
    generated.write_text("patched content")
    generated.read_text()
    return generated

On cache hit, unchanged output files are not restored again. You can also set replay_outputs=False or replay_outputs="if-missing" to control file replay.

Cache any shell command without code changes:

casher -- python train.py --data train.csv

Key features

  • Automatic file tracking: authoritative strace-based subprocess tracking on Linux, plus best-effort Python-level tracking in in-process mode
  • Generated-output awareness: files written before their first read are excluded from input_files
  • Automatic output-arg stabilization: generated output Path arguments are learned from runtime I/O and matched by path identity on later lookups
  • Dependency invalidation: in subprocess mode, imported project .py files are recorded as ordinary implicit inputs and revalidated by path + content hash on lookup
  • Optional dependency narrowing: use dep_roots=[...] or dep_files=[...] to limit which imported source files from subprocess mode are considered relevant
  • File-hash memoization: unchanged files reuse cached content hashes from a small SQLite metadata store
  • LRU eviction: configurable via max_cache_bytes or CASHER_MAX_CACHE_BYTES env var (default 32 GB)
  • Faster hits for large artifacts: output replay skips files whose current hash already matches the cached output
  • DataFrame support: polars and pandas DataFrames serialized via Arrow IPC
  • Environment-aware: include env vars in cache key with env_vars=["MY_VAR"]
  • Miss diagnostics: diagnose_misses=True logs which recorded input changed or disappeared
  • Path-clarity warnings: existing filesystem paths passed as plain str args emit a one-time warning suggesting Path(...)
  • Earlier progress logs: lookup, execution start, execution finish, and cache-store phases are logged so long misses are visible live
  • Structured logging: loguru INFO for config changes, enablement, hit/miss, mode, eviction
  • Explicit directory expansion helper: expand_input_dir() for stable input_files lists

Configuration

Env var Default Description
CASHER_CACHE_DIR unset Cache storage directory. Caching stays disabled until this is set.
CASHER_MAX_CACHE_BYTES 34359738368 (32 GB) Max cache size before LRU eviction

Or set programmatically (takes priority over env vars):

from casher import configure, get_config

configure(cache_dir="/data/my_cache", max_cache_bytes=10 * 1024**3)
print(get_config())  # effective config

If no cache directory is configured via CASHER_CACHE_DIR, configure(cache_dir=...), @cached(cache_dir=...), or casher --cache-dir ..., casher runs transparently without caching and emits a one-time warning.

Platform support

Full caching on Linux only (requires strace for authoritative subprocess mode, fcntl for locking). On macOS and Windows the decorator is a transparent pass-through — functions execute normally, caching is skipped with a one-time warning.

Documentation

See documentation/ for detailed docs:

License

MIT

Metadata

Release files for pycasher 0.5.18

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

Source distribution (sdist)

Source distribution for pycasher 0.5.18
File Size Uploaded Provenance
pycasher-0.5.18.tar.gz 99.1 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 158.4 kB

Release files / pycasher-0.5.18.tar.gz

Download URL pycasher-0.5.18.tar.gz
Size 99.1 kB
Tags Source
SHA-256 checksum
How to use checksums
e854bd87bc4345226f309b45a96af7531c40fa666a448a67764258501f293f2c
BLAKE2b-256 checksum
How to use checksums
2bcc57633c8779f9a148b8e78f81c90c295adbb14bfff9d511c4493f76dcdc12
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.9

Release files / pycasher-0.5.18-py3-none-any.whl

Download URL pycasher-0.5.18-py3-none-any.whl
Size 59.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
402fbaff8a67a387fe77ac028af20ff42406c731ba3e62558a3cb40bf0d112e1
BLAKE2b-256 checksum
How to use checksums
452d55dfb5aaa5d2149e46893a8d76d0be040852092233f020c8bb306f2aea74
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.9

Security for pycasher 0.5.18

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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