exlab_wizard.cache.creation_writer#
Atomic reader/writer for creation.json. Backend Spec §4.4.5.
All creation.json mutations in the codebase MUST go through
CreationWriter. The class enforces the four invariants of
§4.4.5:
Typed encode/decode via
msgspec.jsonagainst theCreationJsonStruct hierarchy (no stdlibjson, no separate Pydantic round-trip).Tempfile + ``os.replace`` for every write (atomic on POSIX, atomic-on-same-volume on Windows).
Per-file advisory file lock. Writes use
filelock.FileLock(exclusive); reads usefilelock.ReadWriteLockin read mode (shared) so concurrent readers do not block each other.Lock-for-full-cycle on mutations: the read, mutator-apply, and write all happen inside a single exclusive lock acquisition so two writers cannot lost-update each other.
Forward-compat: unknown fields encountered on read are preserved on
write per §11.9.3 writer-policy rule 2. The writer keeps the raw
dict[str, Any] decoded form alongside the typed struct, and
merges any keys that were not consumed by the Struct decoder back
into the encoded output.
Backward-compat: when the file’s schema_version is older than the
writer’s current version, the documented defaults from §11.3’s history
table are applied during decode and the next mutation rewrites the
file at the writer’s current version (§11.9.3 rule 3). Major-version
mismatches raise SchemaMajorMismatchError
per §11.9.2 rule 3.
Functions
|
Return the subset of override entries that are currently active. |
Classes
|
Atomic reader/writer for |
- class exlab_wizard.cache.creation_writer.CreationWriter(lock_timeout_seconds=30.0)[source]#
Bases:
objectAtomic reader/writer for
creation.json. Backend Spec §4.4.5.- Parameters:
lock_timeout_seconds (
float)
- async read_creation_snapshot(path)[source]#
Read a snapshot under
LOCK_SH(shared/read lock).Concurrent readers do not block each other; an
LOCK_EXwriter waits for active readers to release. Use this when you need a typed view of the file but do not intend to mutate.- Parameters:
path (
Path)- Return type:
- async update_creation_atomic(path, mutator)[source]#
Read, mutate, and write
creation.jsonunder oneLOCK_EX.The full read-mutator-write cycle happens inside a single
filelock.FileLockacquisition. Two concurrentupdate_creation_atomiccalls on the same path serialize: the second waits for the first to release before it reads, so neither lost-updates the other.The mutator is allowed to either mutate the struct in place and return it, or return a fresh struct.
- Parameters:
path (
Path)mutator (
Callable[[CreationJson],CreationJson])
- Return type:
- async write_creation(path, payload)[source]#
Write a fresh
creation.json. Reserved for initial creation.Acquires the per-file exclusive lock defensively even though no prior file is expected; that way two simultaneous “first writers” serialize correctly and the second one observes a now-existing file (which the controller treats as a conflict).
- Parameters:
path (
Path)payload (
CreationJson)
- Return type:
- exlab_wizard.cache.creation_writer.select_active_overrides(validation_overrides, *, now=None)[source]#
Return the subset of override entries that are currently active.
Implements the matching algorithm in spec §11.3:
Build a set
revoked_idsof every entry’srevokespointer whereentry.revoked == True.An override entry is active iff
entry.revoked == False, itsidis not inrevoked_ids, and itsexpires_atis either absent/Noneor strictly greater thannow.
nowdefaults to the current UTC time. Pass an explicit value for deterministic tests. Tombstones whoserevokestarget is missing from the array are logged at WARN and otherwise have no effect.