# Pydantic source notes Repo: `pydantic/pydantic` Local checkout: `/home/ubuntu/repos/rodin-sources/pydantic` ## Why this repo is useful - Pydantic is a strong source for boundary-object patterns: validating incoming data, preserving typed state, and serializing back out explicitly. - It is also useful for validation-hook design because the docs distinguish several validator phases and call out their tradeoffs clearly. ## Models are explicit validation + serialization boundaries ### Repeated evidence - `pydantic/main.py:119-145` defines `BaseModel` as the central abstraction and documents that models carry schema, field metadata, and decorator metadata. - `pydantic/main.py:201-205` explicitly exposes model-level serializer and validator machinery as core parts of the abstraction. - `docs/index.md:68-82` shows external data entering through model construction. - `docs/index.md:82-89` immediately turns the model back into a plain data structure with `model_dump()`. - `docs/index.md:109-152` shows invalid boundary data raising `ValidationError` with structured per-field errors instead of silently degrading. ### Why it matters Repeated signal: Pydantic models are meant to sit at I/O boundaries. Input is validated/coerced at construction time; output is serialized through an explicit dump step. ### Caveat / counterexample The strong pattern is not "models are your whole domain model." The evidence here is boundary-oriented: construct from external data, then call `model_dump()` when leaving the boundary again. ## Validators are narrow and phase-aware ### Repeated evidence - `docs/concepts/validators.md:91-114` shows an `after` field validator that checks one parsed field and must return the validated value. - `docs/concepts/validators.md:160-167` explains that `before` validators run prior to internal parsing and therefore receive raw input. - `docs/concepts/validators.md:220-252` demonstrates a `before` validator that reshapes raw input and then lets normal item validation continue. ### Why it matters Repeated signal: the best validator hooks are small in scope and explicit about phase: - `before` for raw-input normalization - `after` for post-parse invariants This prevents validation logic from becoming an opaque second parser. ## Validator mode choice has real behavioral consequences ### Repeated evidence - `docs/concepts/validators.md:160-164` warns that `before` validators should avoid careless mutation when raising later, especially with unions. - `docs/concepts/validators.md:254-255` states that `plain` validators terminate validation immediately. - `docs/concepts/validators.md:273-283` shows the consequence directly: a `PlainValidator` can return `'invalid'` for a field annotated as `int`, and Pydantic will accept it. ### Why it matters Repeated signal: validator mode is not just an implementation detail. It changes whether core type validation still runs. ### Caveat / counterexample This is the sharpest anti-pattern in the repo: `plain` validators are powerful, but they can bypass the type guarantee a reader expects from the annotation. Use them only when terminating validation is the actual goal. ## Pattern candidates supported by this repo - use typed models at I/O boundaries - serialize explicitly with `model_dump()` - keep validators field-scoped and phase-aware - treat `plain` validators as an escape hatch, not the default