"""Atomic artifacts and content-addressed provenance.""" import hashlib import json import os from datetime import UTC, datetime from pathlib import Path def now(): return datetime.now(UTC).isoformat() def canonical(value): return json.dumps(value, ensure_ascii=False, sort_keys=True, allow_nan=False) def fingerprint(value): return hashlib.sha256(canonical(value).encode()).hexdigest() def digest(path): h = hashlib.sha256() with Path(path).open("rb") as f: for block in iter(lambda: f.read(1024 * 1024), b""): h.update(block) return h.hexdigest() def load(path): return json.loads(Path(path).read_text()) def write(path, value): path = Path(path) path.parent.mkdir(parents=True, exist_ok=True) tmp = path.with_name(f".{path.name}.{os.getpid()}.tmp") tmp.write_text(canonical(value) + "\n") tmp.replace(path) def append(path, value): path = Path(path) path.parent.mkdir(parents=True, exist_ok=True) with path.open("a") as f: f.write(canonical(value) + "\n") f.flush() os.fsync(f.fileno()) def rows(path): with Path(path).open() as f: return [json.loads(line) for line in f if line.strip()]