Refine Python pattern boundaries and Pydantic guidance
This commit is contained in:
+9
-16
@@ -11,16 +11,7 @@ A lot of messy Python comes from making one class do four jobs at once:
|
||||
- serialize output
|
||||
- own persistence or business behavior
|
||||
|
||||
Mature codebases usually split those concerns.
|
||||
|
||||
The recurring pattern is:
|
||||
|
||||
- use explicit boundary models for validated input and output
|
||||
- keep serialization explicit
|
||||
- use lightweight value-like classes for simple internal state
|
||||
- avoid turning every data container into a god object
|
||||
|
||||
## The pattern
|
||||
Mature codebases usually split those concerns:
|
||||
|
||||
1. Use dedicated boundary models where data enters or leaves the system.
|
||||
2. Validate and serialize explicitly.
|
||||
@@ -43,9 +34,7 @@ Use small value-like objects when:
|
||||
|
||||
## When not to use
|
||||
|
||||
Do not make every internal object a heavyweight validation model.
|
||||
|
||||
Do not scatter custom `to_dict()` logic across the codebase.
|
||||
Do not treat a boundary model as proof that every internal object should also become a heavyweight validation model.
|
||||
|
||||
Do not let one model become schema, ORM wrapper, validator, service object, and side-effect manager at the same time.
|
||||
|
||||
@@ -117,9 +106,9 @@ That is not a model. That is a junk drawer.
|
||||
|
||||
- `pydantic/main.py:253-264` says `BaseModel.__init__` parses and validates input data and raises `ValidationError` on bad input.
|
||||
- `pydantic/main.py:455-519` defines `model_dump(...)` as an explicit serialization step with caller-controlled include/exclude behavior.
|
||||
- `pydantic/main.py:721-768` defines `model_validate(...) -> Self` as a named boundary-crossing API.
|
||||
- `docs/index.md:82-107` pairs model creation with `model_dump()` instead of treating instances as already wire-ready.
|
||||
- `docs/index.md:109-124` shows invalid external input failing loudly with `ValidationError`.
|
||||
- `pydantic/main.py:721-768` defines `model_validate(...) -> Self` as a named boundary-crossing API with explicit validation options.
|
||||
- `docs/index.md:68-107` shows raw external data being coerced into typed fields and then dumped back out explicitly.
|
||||
- `docs/index.md:109-152` shows invalid external input failing with structured per-field validation errors instead of ad hoc strings.
|
||||
|
||||
### Attrs
|
||||
|
||||
@@ -131,6 +120,10 @@ That is not a model. That is a junk drawer.
|
||||
|
||||
- `src/_pytest/timing.py:24-64` models `Instant` and `Duration` as frozen dataclasses for simple internal timing state.
|
||||
|
||||
## Related comparison
|
||||
|
||||
- `comparison/pydantic-vs-python.md`
|
||||
|
||||
## Bottom line
|
||||
|
||||
Boundary models should validate and serialize cleanly.
|
||||
|
||||
+7
-2
@@ -106,9 +106,14 @@ If runtime safety matters, attribute-presence checks are not enough. Protocols d
|
||||
|
||||
### Pydantic
|
||||
|
||||
- `pydantic/main.py:156-205` exposes typed `ClassVar[...]` metadata for config, fields, serializer, and validator state even though framework internals are dynamic.
|
||||
- `pydantic/main.py:156-205` exposes typed `ClassVar[...]` metadata for config, schema, fields, serializer, and validator state even though framework internals are dynamic.
|
||||
- `pydantic/main.py:167-168` documents a synthesized `__init__` signature, which is another signal that annotations shape the runtime boundary.
|
||||
- `pydantic/main.py:253-264` makes model construction validate `**data: Any` immediately instead of pretending arbitrary inputs are already safe.
|
||||
- `pydantic/main.py:721-768` gives `model_validate(...) -> Self` an explicit typed boundary contract.
|
||||
- `pydantic/main.py:721-768` gives `model_validate(...) -> Self` an explicit typed boundary contract with runtime validation options.
|
||||
|
||||
## Related comparison
|
||||
|
||||
- `comparison/pydantic-vs-python.md`
|
||||
|
||||
## Bottom line
|
||||
|
||||
|
||||
Reference in New Issue
Block a user