feat(extract): Door B extraction registry (Phase 2 step 1)
First, guard-independent step of Phase 2: a stdlib-only registry mapping a dropped file's extension to its text extractor, with the fail-fast gates that keep binary parsing out of core. `extract_text(filename, data)` dispatches (case-insensitively) to: - `md`/`txt` — utf-8-sig passthrough (BOM never leaks, baseline parity with Door A's read_csv); - `csv` — the Phase 1 `render_table` (renderer reused, not duplicated); - `json` — verbatim inside `render_fenced_block`; - `html`/`htm` — text via `html.parser`, `script`/`style` stripped, tags as word boundaries (spec B3: adequate for v1, richer is out of scope). `pdf`/`docx`/`xlsx` are `[extract]`-gated; until that extra ships a parser they fail fast with a typed error naming the extra — never a silent skip, never a bundled parser in core. New `ExtractionError(IngestError)` carries four stable codes (`extractor_unknown`, `extractor_extra_missing`, `extractor_decode_error`, `extractor_empty_csv`); a non-UTF-8 file is a typed corrupt-input failure, never a leaked UnicodeDecodeError. `extract_text` returns text content only — LF framing and concept frontmatter are the materializer's job (step 2). No runtime dependency and no guard call yet (the guard pin and 0.4.0 land with the persist gate in steps 4–5). TDD: test_extract.py + the four codes in the test_error_codes.py registry precede the implementation; mypy --strict, ruff, and the `sanitize|quarantine|lexicon` boundary grep-gate all clean; the Phase 1 golden suite still passes byte-for-byte. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HBbjgS5A55RVavoyjJC4FX
This commit is contained in:
parent
99c899f4ed
commit
db93de4aef
5 changed files with 301 additions and 0 deletions
|
|
@ -72,6 +72,23 @@ class SourceError(IngestError):
|
|||
"""
|
||||
|
||||
|
||||
class ExtractionError(IngestError):
|
||||
"""A dropped file could not be converted to text (Door B, Phase 2).
|
||||
|
||||
File-type -> text extraction is text-only plumbing; a corrupt file, an
|
||||
unknown type, or a binary type without its optional parser is a typed
|
||||
per-file failure — never a silent skip, never a leaked stdlib error, and
|
||||
never a bundled parser in core.
|
||||
|
||||
Codes:
|
||||
- `extractor_unknown` — no extractor is registered for the file extension
|
||||
- `extractor_extra_missing` — a `[extract]`-gated binary type (pdf/docx/
|
||||
xlsx) was given but the optional extra is not installed
|
||||
- `extractor_decode_error` — a text-type file's bytes are not valid UTF-8
|
||||
- `extractor_empty_csv` — a CSV has no header row
|
||||
"""
|
||||
|
||||
|
||||
class MaterializationError(IngestError):
|
||||
"""Materialization refused or failed (ingest-spec §5).
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue