Skip to main content

Dictionary Validation

Dictionary validation in pydantic-core is managed through the DictSchema class, which provides a comprehensive way to validate mapping types. This is typically implemented using the core_schema.dict_schema() helper function found in pydantic_core/core_schema.py.

Core Concepts

The dict_schema allows you to define validation rules for both the keys and the values of a dictionary independently, as well as constraints on the dictionary's overall size.

Key and Value Validation

You can specify separate schemas for keys and values using keys_schema and values_schema. If these are not provided, they default to AnySchema, meaning any type is accepted.

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)

# Successful validation with coercion
assert v.validate_python({'a': '1', 'b': 2}) == {'a': 1, 'b': 2}

Size Constraints

The min_length and max_length parameters enforce limits on the number of items in the dictionary. These constraints are evaluated after the individual keys and values have been validated and coerced.

schema = core_schema.dict_schema(
min_length=2,
max_length=3
)

v = SchemaValidator(schema)

# Fails: too few items
# v.validate_python({'a': 1}) -> ValidationError (too_short)

# Fails: too many items
# v.validate_python({'a': 1, 'b': 2, 'c': 3, 'd': 4}) -> ValidationError (too_long)

Validation Behavior

Strict Mode

The strict parameter determines how the validator handles input types. In strict mode, only actual Python dict instances are accepted. In lax mode (the default), the validator may accept other mapping types or objects that can be converted to a dictionary.

schema = core_schema.dict_schema(strict=True)
v = SchemaValidator(schema)

# In strict mode, a custom Mapping subclass might be rejected
# if it is not a literal dict.

Fail Fast

By default, pydantic-core attempts to collect all validation errors within a dictionary. Setting fail_fast=True causes validation to stop immediately upon encountering the first error, whether it occurs in a key or a value.

schema = core_schema.dict_schema(
values_schema=core_schema.int_schema(),
fail_fast=True
)

Error Reporting

When a dictionary key fails validation, pydantic-core uses a specific marker in the error location to distinguish it from a value error. The location will include the key itself followed by the string "[key]".

For example, if a dictionary {'a': 1} is validated against a schema where keys must be integers, the error location for the key 'a' would be: ('a', '[key]')

This is visible in tests like pydantic-core/tests/validators/test_dict.py:

# Example of error location for key validation failure
'loc': ('a', '[key]'),
'msg': 'Input should be a valid integer',
'type': 'int_parsing',

Serialization and Filtering

Dictionary serialization can be customized using filter_dict_schema, which allows for including or excluding specific keys during the export process. This is passed to the serialization parameter of dict_schema.

from pydantic_core import SchemaSerializer, core_schema

schema = core_schema.dict_schema(
serialization=core_schema.filter_dict_schema(
include={'a', 'c'}
)
)

s = SchemaSerializer(schema)
assert s.to_python({'a': 1, 'b': 2, 'c': 3, 'd': 4}) == {'a': 1, 'c': 3}

This filtering mechanism is particularly useful when you want to control the visibility of internal dictionary keys when converting models to JSON or Python dictionaries.