Summary
Add a Pydantic BaseModel for constraint rules, replacing the current ConstraintRule NamedTuple.
Problem
The Pydantic milestone has models planned for every spec sub-structure — Axis (#23), Transition (#21), Mixing (#24), Chain (#26), StateAxes (#27), Operator (#28), ExpressionString (#22) — but not for constraints. ConstraintRule is a NamedTuple with loose types:
mode: str instead of Literal["allow", "exclude"]
rules: tuple[dict[str, list[str]], ...] with no validation that rule keys match declared axes
All validation currently lives downstream in ~120 lines of procedural code (_validate_constraint_axes(), _resolve_constraint_mode(), _validate_constraint_rule() in specs.py).
Scope
- Define
Constraint Pydantic model with:
axes: tuple[str, ...] — validated ≥2 unique entries
mode: Literal["allow", "exclude"]
rules with validated key→coordinate mappings
- Absorb validation from
_validate_constraint_axes(), _resolve_constraint_mode(), _validate_constraint_rule() into model validators
- Update
_normalize_constraints() to accept/return Pydantic models
- Update tests
Related
Summary
Add a Pydantic
BaseModelfor constraint rules, replacing the currentConstraintRuleNamedTuple.Problem
The Pydantic milestone has models planned for every spec sub-structure — Axis (#23), Transition (#21), Mixing (#24), Chain (#26), StateAxes (#27), Operator (#28), ExpressionString (#22) — but not for constraints.
ConstraintRuleis aNamedTuplewith loose types:mode: strinstead ofLiteral["allow", "exclude"]rules: tuple[dict[str, list[str]], ...]with no validation that rule keys match declared axesAll validation currently lives downstream in ~120 lines of procedural code (
_validate_constraint_axes(),_resolve_constraint_mode(),_validate_constraint_rule()inspecs.py).Scope
ConstraintPydantic model with:axes: tuple[str, ...]— validated ≥2 unique entriesmode: Literal["allow", "exclude"]ruleswith validated key→coordinate mappings_validate_constraint_axes(),_resolve_constraint_mode(),_validate_constraint_rule()into model validators_normalize_constraints()to accept/return Pydantic modelsRelated