Files

67 lines
2.0 KiB
Python

"""Per-object export coordination and content-aware artifact reuse."""
from contextlib import contextmanager, ExitStack
import hashlib
import json
from pathlib import Path
import threading
from weakref import WeakValueDictionary
_guard = threading.Lock()
_locks: WeakValueDictionary = WeakValueDictionary()
TERMINAL = {"completed", "success", "failed", "cancelled", "interrupted", "skipped"}
@contextmanager
def export_lock(*keys: str):
# Retain strong references until every lock is released. Unrelated exports
# run concurrently; nested session exports can reacquire their own RLock.
with _guard:
locks = []
for key in sorted(set(keys)):
lock = _locks.get(key)
if lock is None:
lock = threading.RLock()
_locks[key] = lock
locks.append(lock)
with ExitStack() as stack:
for lock in locks:
stack.enter_context(lock)
yield
def fingerprint(report: dict, files=(), extra=None) -> str:
metadata = []
for path, name in files:
stat = path.stat()
metadata.append((name, str(path), stat.st_size, stat.st_mtime_ns))
payload = json.dumps([2, report, metadata, extra], sort_keys=True, default=str)
return hashlib.sha256(payload.encode()).hexdigest()
def reusable(path: Path, signature: str, status: str) -> bool:
if status not in TERMINAL or not path.is_file():
return False
try:
return path.with_suffix(path.suffix + ".fingerprint").read_text() == signature
except OSError:
return False
def remember(path: Path, signature: str) -> None:
marker = path.with_suffix(path.suffix + ".fingerprint")
temporary = marker.with_suffix(".tmp")
temporary.write_text(signature, encoding="ascii")
temporary.replace(marker)
def compression(path: Path) -> int:
import zipfile
return (
zipfile.ZIP_STORED
if path.suffix.lower()
in {".mp4", ".mkv", ".webm", ".docx", ".xlsx", ".png", ".jpg", ".jpeg", ".zip"}
else zipfile.ZIP_DEFLATED
)