Generic Mappings with DictSchema
In pydantic-core, generic mappings are handled by the DictSchema structure. Unlike TypedDictSchema, which defines a fixed set of keys with specific types, DictSchema is designed for dictionaries where any number of keys can exist, provided they and their corresponding values conform to specified sub-schemas.
The primary way to construct these schemas is through the dict_schema() helper function found in pydantic_core.core_schema.
Core Concepts
A DictSchema defines how to validate the keys and values of a dictionary. It is essentially a template for key-value pairs.
Key and Value Validation
The keys_schema and values_schema fields allow you to apply any CoreSchema to the dictionary's components. If omitted, they default to AnySchema.
from pydantic_core import SchemaValidator, core_schema
# A dictionary where keys must be strings and values must be integers
schema = core_schema.dict_schema(
keys_schema=core_schema.str_schema(),
values_schema=core_schema.int_schema()
)
v = SchemaValidator(schema)
# Validates and coerces values
assert v.validate_python({'a': '1', 'b': 2}) == {'a': 1, 'b': 2}
When validating JSON, keys are always strings. If keys_schema is something other than a string schema (e.g., int_schema), pydantic-core will attempt to coerce the string key into the target type.
Validation Constraints
DictSchema supports several constraints to control the size and validation flow of the mapping:
min_lengthandmax_length: Restrict the number of items in the dictionary.strict: IfTrue, only actual Pythondictobjects are accepted. IfFalse(the default), any object implementing theMappinginterface is accepted and coerced to adict.fail_fast: Determines whether validation should stop immediately upon encountering the first error.
Fail Fast Behavior
By default, pydantic-core collects all errors found during dictionary validation. Setting fail_fast=True changes this to stop at the first failure, which can be useful for performance or specific error reporting requirements.
# Example from pydantic-core/tests/validators/test_dict.py
v = SchemaValidator(
{
'type': 'dict',
'keys_schema': {'type': 'int'},
'values_schema': {'type': 'int'},
'fail_fast': True
}
)
# Validation stops at the first error ('a' is not an int)
# instead of continuing to check 'c'
Error Reporting and the [key] Marker
When a key fails validation, pydantic-core uses a specific convention in the error location to distinguish between a failure in the key itself versus a failure in the value associated with that key.
If a key fails validation, the location in the ValidationError will include the key's value followed by the special marker [key].
# Example from pydantic-core/tests/validators/test_dict.py
v = SchemaValidator(core_schema.dict_schema(keys_schema=core_schema.int_schema()))
try:
v.validate_python({'x': 1})
except ValidationError as exc:
# The error location points to the key 'x' specifically
assert exc.errors()[0]['loc'] == ('x', '[key]')
Serialization and Filtering
DictSchema also supports advanced serialization options through the serialization field. The filter_dict_schema() helper is used to define include or exclude sets for dictionary keys during serialization.
# Example from pydantic-core/tests/serializers/test_dict.py
from pydantic_core import SchemaSerializer, core_schema
schema = core_schema.dict_schema(
serialization=core_schema.filter_dict_schema(include={'a', 'c'})
)
s = SchemaSerializer(schema)
# Only keys 'a' and 'c' are included in the output
assert s.to_python({'a': 1, 'b': 2, 'c': 3, 'd': 4}) == {'a': 1, 'c': 3}
This filtering mechanism allows for fine-grained control over which data is exported, even when the underlying dictionary contains more information than needed for a specific output format.