Python · correctness
no-frozen-after-validator-field-write
python:no-frozen-after-validator-field-write Do not assign declared fields in after-validators on frozen Pydantic models.
- Code
- SARJ401
- Default
- error
- Fix
- none
- Languages
- python
Why
Direct field assignment contradicts the model's frozen contract and can fail only when the validator runs, making construction behavior surprising and brittle.
Fix
Validate without mutation, compute the value before constructing the model, or return an explicitly updated model when replacement is part of the design.
Before / after
Executed by this rule’s unit tests.
Before
After-validator assigns a frozen model field
from pydantic import BaseModel, ConfigDict, model_validator
class Counter(BaseModel):
model_config = ConfigDict(frozen=True)
value: int
@model_validator(mode="after")
def normalize(self):
self.value = abs(self.value)
return self
After
After-validator checks without mutating the model
from pydantic import BaseModel, ConfigDict, model_validator
class Counter(BaseModel):
model_config = ConfigDict(frozen=True)
value: int
@model_validator(mode="after")
def require_non_negative(self):
if self.value < 0:
raise ValueError("value must be non-negative")
return self
Limits
- The rule checks direct public fields on direct Pydantic `BaseModel` subclasses configured with literal `ConfigDict(frozen=True)`.
- It detects direct assignment, annotated assignment, augmented assignment, and tuple or list destructuring through the validator's receiver.
- Indirect mutation through method calls, `setattr`, or `object.__setattr__` is outside its scope.
- Test and generated files are excluded.