Lab 01 — Typed Data-Contract Library
Phase: 01 — Production Python Engineering | Difficulty: ⭐⭐⭐☆☆ | Time: 4–6 hours
Every production ML pipeline has boundaries where data arrives from systems you don't control. This lab builds the boundary guard — a miniature pydantic/pandera — and in doing so drills the production-Python toolkit: protocols, frozen dataclasses, collect-don't-raise error design, and property-style testing.
What you build
ValidationIssue/ValidationReport— frozen value objects; issues carrypath+rule+ message so a million-record batch aggregates into a dashboard- Built-in rules —
TypeRule(with the bool-is-not-int trap),RangeRule,RegexRule(fullmatch!),EnumRule— each a frozen dataclass satisfying a structuralRuleprotocol Schema.validate— collects every issue: unknown fields, missing required, nullability, coercion failures, rule failures- Coercion —
"42"→ 42 where declared, failures as issues, never exceptions - Composition —
extend/subsetreturning new schemas (immutability pays off)
Key concepts
| Concept | What to understand |
|---|---|
| Collect-don't-raise | Data problems are values (issues), not exceptions; exceptions are for programmer errors (see extend's clash check) |
| Structural typing | A custom rule is any object with name + check() — the test proves it with a class that inherits nothing |
| bool ⊂ int | Python's subclassing trap: a data contract that accepts True as an int corrupts downstream aggregates |
| fullmatch vs search | search would accept "xu_12y" for pattern u_\d+ — boundary anchoring is contract semantics |
| Frozen composition | extend/subset return new schemas because the originals can't change — share schemas fearlessly |
Files
| File | Purpose |
|---|---|
lab.py | Skeleton with # TODO markers |
solution.py | Complete reference; python solution.py runs a demo |
test_lab.py | 15 tests incl. the property-style single-mutation localization test |
requirements.txt | pytest only — pure stdlib library |
Run
pytest test_lab.py -v # your lab.py
LAB_MODULE=solution pytest test_lab.py -v # the reference
Success criteria
- All 15 tests pass — especially
test_property_single_field_mutation_localizes_issue(mutating exactly one field must produce issues at exactly that path) - You can explain why
validatereturns a report whileextendraises — the data-error vs programmer-error line, drawn correctly - You can write a new rule (e.g.,
LengthRule) in under two minutes without touching the library
Extensions
- Nested schemas (
Field(dtype=Schema)) with dotted paths (user.address.zip) - A pandas adapter: validate a DataFrame column-wise, returning issue counts per rule (the data-quality dashboard primitive Phase 02 uses)
- Compare your design to pydantic v2's — what does compiled-core validation buy, and what did you not need?
References
- pydantic v2 docs and pandera — the industrial versions
- Alexis King, Parse, don't validate
- WARMUP.md chapters 2–4 — the design rationale in full