Description
Currently because of configs it is not possible to map a type to a SchemaValidator or CoreSchema. Consider:
class Inner(BaseModel):
x: dict[str, list[float]]
model_config = ConfigDict(allow_inf_nan=False)
class Outer(BaseModel):
inner: Inner
x: dict[str, list[float]]
model_config = ConfigDict(allow_inf_nan=True)
Edit by @Viicos:
allow_inf_nan
actually doesn't seem to alter the core schema ofx
. One example that does however isuse_enum_values
.
Because of this we can’t just have a type_cache: dict[type, CoreSchema]
when we build a schema from types in Pydantic: although dict[str, list[float]]
shows up in two places the actual schema differs because of the model config.
To get around this I propose that we establish a rule: “every validator must be derivable from the type and only the type”. That means that all of the things coming from configs have to live elsewhere. For this I propose we (1) move all config things to ValidationState and (2) introduce a validator that mutates ValidationState to set the current config.
For example, we’d have {‘type’: ‘config’, ‘config’: {‘allow_inf_nan’: True}, ‘schema’: {…}}
. At runtime this would mutate the validation context. And FloatValidator would pull that configuration from the context instead of storing it on the struct. Now both CoreSchema::Float and FloatValidator are immutable respect to the type and thus we can map from a type to a CoreSchema or SchemaValidator.
I expect this will slightly simplify some code (it essentially merges runtime parameters and compile time parameters into one code path where compile time just means a compile time defined mutation of runtime parameters) and have a minimal performance impact (we can still convert all configs to rust structs ahead of time, it’s just a struct being passed in through context vs hardcoded on the validator).
I think we might as well also establish or consider current behavior wrt rules for config merging and interaction with runtime parameters.
@davidhewitt @sydney-runkle wdyt?