"""Lock hierarchy and acquisition ordering. Locks are acquired broad → narrow to prevent deadlocks: never take a broader lock while holding a narrower one (concept §16). The coordinator already enforces one active job per lock key; this guards multi-lock operations. library lease → stage/job lease → album/folder lease → asset lease """ from __future__ import annotations from collections.abc import Iterable # Lower rank = broader scope. Mutating lanes map onto these tiers. LOCK_RANK = { "library": 0, "library_write": 0, "rename": 1, "upload": 1, "archive": 1, "album": 2, "asset": 3, "exif": 3, } class LockOrderError(RuntimeError): pass def rank(lock: str) -> int: if lock not in LOCK_RANK: raise LockOrderError(f"unknown lock {lock!r}") return LOCK_RANK[lock] def validate_acquisition(held: Iterable[str], acquiring: str) -> None: """Reject acquiring a broader lock than one already held (deadlock risk).""" new_rank = rank(acquiring) for lock in held: if rank(lock) > new_rank: raise LockOrderError( f"cannot acquire broader lock {acquiring!r} while holding narrower {lock!r}" )