snapdir for Python
The Python binding wraps the same Rust core (snapdir-api → snapdir-core) as
the CLI, so the manifests and snapshot IDs it produces are bit-identical to
snapdir and every other binding. Snapshot IDs are 64-character lowercase hex
BLAKE3 digests. The surface is the same everywhere: synchronous id /
manifest, plus async push / pull / fetch / diff / sync, in Python's
idiom.
Install
The binding is a prebuilt abi3 wheel — the Rust core is compiled in, so there
is nothing to build:
pip install snapdir
Requires Python 3.10 or newer. PyPI serves prebuilt manylinux and musllinux
wheels (x86-64 + aarch64) and macOS wheels; the musllinux wheel runs on Alpine
with no compiler. See Install for the platform matrix.
Usage
id and manifest compute content-addressed data without touching a store; the
I/O operations (push, pull, fetch, diff, sync) are async. The store
URI is always an argument, wrapped in StoreUri; snapshot IDs are wrapped in
SnapshotId. Use file://$PWD/store for a local store and gs://bucket/prefix
for Google Cloud.
import asyncio
import os
import snapdir
from snapdir import DiffOptions, SnapshotId, StoreUri
async def main() -> None:
src = "./project"
store = StoreUri(f"file://{os.getcwd()}/store")
# Content-addressed id + manifest (no store I/O).
snap_id = await snapdir.id(src)
manifest = await snapdir.manifest(src)
print(snap_id) # 64-char lowercase hex, e.g. <snapshot-id>
for entry in manifest.entries:
print(entry.path_type, entry.path, entry.size)
# Stage + upload; returns the snapshot id.
pushed = await snapdir.push(src, store)
# Download into the local cache, then materialise into a directory.
await snapdir.fetch(SnapshotId(pushed), store)
await snapdir.pull(SnapshotId(pushed), store, "./restored")
# Diff two pinned snapshots. The diff ref convention is store@id
# (the last @ splits the URI from the 64-hex id).
opts = DiffOptions.from_refs(
[f"{store}@{snap_id}"],
[f"{store}@{pushed}"],
)
for change in await snapdir.diff(opts):
print(change.status, change.path) # A / D / M / =
asyncio.run(main())
Swap the file:// store for a gs://…, s3://…, or b2://… URI and the same
calls publish to and restore from the cloud. For S3-compatible endpoints, only
the endpoint and credentials come from the environment
(SNAPDIR_S3_STORE_ENDPOINT_URL, AWS_*) — the store URI stays an argument.
Errors
Every failure raises a subclass of SnapdirError, which carries a stable
.code string drawn from the 8-code taxonomy shared by all bindings. Match on
the class or the code:
import snapdir
from snapdir import SnapshotId, StoreUri
try:
await snapdir.pull(SnapshotId("<snapshot-id>"),
StoreUri("file:///srv/store"), "./restored")
except snapdir.SnapdirError as err:
print(err.code) # e.g. "HASH_MISMATCH"
Platform support
The Python wheel runs on Linux (glibc and Alpine/musl, x64 and arm64) and macOS with no libc caveat. Only the Java binding is glibc-only today (musl planned for 1.11.1). Windows is unsupported: snapdir is Unix-only.