67 lines
2.0 KiB
Python
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
|
|
)
|