feat(errors): stable machine-readable codes on IngestError
Consumer tests were binding message wording (pytest.raises(match=...)) to distinguish sub-causes within one error type. Every raise site now carries a documented, stable code attribute; the registry lives in the errors.py class docstrings. Codes are stable API; message text is explicitly not. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
9e87f656fc
commit
9d296ca686
6 changed files with 468 additions and 43 deletions
|
|
@ -166,13 +166,16 @@ def materialize_bundle(
|
|||
if not _INGESTED_AT_RE.match(ingested_at):
|
||||
raise MaterializationError(
|
||||
"ingested_at must be ISO-8601 UTC with a Z suffix "
|
||||
f"(e.g. 2026-07-03T12:00:00Z), got {ingested_at!r}"
|
||||
f"(e.g. 2026-07-03T12:00:00Z), got {ingested_at!r}",
|
||||
code="ingested_at_invalid",
|
||||
)
|
||||
manifest_file = Path(manifest_path)
|
||||
try:
|
||||
raw = manifest_file.read_bytes()
|
||||
except OSError as exc:
|
||||
raise ManifestError(f"cannot read manifest {manifest_file}: {exc}") from exc
|
||||
raise ManifestError(
|
||||
f"cannot read manifest {manifest_file}: {exc}", code="manifest_unreadable"
|
||||
) from exc
|
||||
# §5 provenance stamp over the exact bytes that are parsed below.
|
||||
stamp = f"{manifest_file.stem}@{hashlib.sha256(raw).hexdigest()[:16]}"
|
||||
manifest = load_manifest_bytes(raw)
|
||||
|
|
@ -183,7 +186,8 @@ def materialize_bundle(
|
|||
raise NetworkGateError(
|
||||
"http source requires the per-run network opt-in "
|
||||
"(materialize_bundle(..., allow_network=True)) — the manifest cannot "
|
||||
"grant itself network access (spec §8, local-only default)"
|
||||
"grant itself network access (spec §8, local-only default)",
|
||||
code="network_opt_in_missing",
|
||||
)
|
||||
|
||||
# Pinned decision (file): a relative root resolves against the manifest
|
||||
|
|
@ -248,7 +252,8 @@ def materialize_bundle(
|
|||
if (bundle / name).is_file() and name not in owned:
|
||||
raise MaterializationError(
|
||||
f"generated filename {name!r} collides with an existing file that does "
|
||||
"not carry the ingest stamp — refusing to overwrite curated content (§3)"
|
||||
"not carry the ingest stamp — refusing to overwrite curated content (§3)",
|
||||
code="collision_unstamped",
|
||||
)
|
||||
|
||||
# §5 replacement: remove every stamped file, then write the new set.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue