-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Opening an issue to discuss this language design feature.
I guess this kind of dynamic class creation is allowed, but it's not very Pythonic. If this kind of dependent type checking is something we want to support/encourage, we should think about how to make it easier and more idiomatic to express.
Originally posted by @eb8680 in #404 (comment)
Pydantic models support field validation which allows placing additional constraints on instances of a type.
Often, the model validation logic may want to use runtime values from elsewhere in the program - for example:
# hypothetical syntax - not supported in python
class ValidStep[no_towers: int, valid_moves: set[tuple[int,int]]]:
start: int
end: int
@pydantic.field_validator("start","end")
def _validate_field(cls, v, info):
assert 0 <= v < no_towers
return v
@pydantic.model_validator("after")
def _validate_model(self):
assert (self.start, self.end) in valid_moves
return selfThe above captures a step that is valid with respect to some game board. The syntax above isn't supported by pydantic, so in practice to implement the above, you'd need to do something like:
def build_validated_model(game_state: GameState) -> type[Step]:
valid_steps = game_state.valid_steps()
@pydantic.dataclasses.dataclass(frozen=True)
class StepModel(Step):
start: int
end: int
explanation: str = ""
model_config = ConfigDict(extra="forbid")
@pydantic.field_validator("start", "end", mode="before")
def validate_indices(cls, v, info):
if isinstance(v, int):
if not (0 <= v < len(game_state.towers)):
raise ValueError(f"{info.field_name} {v} out of range")
else:
raise TypeError("start/end must both be int")
return v
@pydantic.model_validator(mode="after")
def validate_step(self):
if (self.start, self.end) not in valid_steps:
raise ValueError("step is not in {self.valid_steps}")
return self
def __hash__(self):
return hash((self.start, self.end))
return StepModel