Skip to main content

Validation Context and Configuration

Validation in this codebase is not just about checking types; it involves a rich system for passing configuration and runtime state through the validation pipeline. This is achieved primarily through two entities: CoreConfig, which defines global settings for a validator, and ValidationInfo, which provides runtime context to custom validator functions.

Global Configuration with CoreConfig

The CoreConfig class (defined in pydantic_core/core_schema) is a TypedDict that specifies how a SchemaValidator should behave across all fields. When you instantiate a SchemaValidator, you can provide a config object to set these global rules.

Common configuration options include:

  • strict: When set to True, the validator will not attempt to coerce types (e.g., it won't turn the string "123" into an integer).
  • extra_fields_behavior: Controls how to handle fields not defined in the schema (e.g., 'allow', 'forbid', or 'ignore').
  • str_max_length / str_min_length: Sets global constraints for all string fields unless overridden by a specific field's schema.
  • hide_input_in_errors: A security-focused setting that prevents the original input data from being included in ValidationError messages.

Example: Setting Global Config

In this example, we set a global title and strict mode via CoreConfig.

from pydantic_core import CoreConfig, SchemaValidator, core_schema

config = CoreConfig(title='MyValidator', strict=True)
v = SchemaValidator(core_schema.int_schema(), config=config)

# This will fail because strict=True prevents string-to-int coercion
# v.validate_python('123') -> Raises ValidationError

Runtime Context with ValidationInfo

While CoreConfig is static for the lifetime of a SchemaValidator, ValidationInfo provides dynamic, runtime information to custom validator functions. If you use "with info" validator functions (like with_info_before_validator_function), the second argument passed to your function will be an instance of ValidationInfo.

The ValidationInfo protocol provides access to:

  • config: The CoreConfig object used to initialize the validator.
  • context: Arbitrary data passed at runtime via the context argument of validate_python.
  • mode: Indicates whether the input is 'python' or 'json'.
  • data: A dictionary containing other fields that have already been validated (useful for cross-field validation).
  • field_name: The name of the field currently being validated.

Accessing Context and Config

The following pattern, found in pydantic-core/tests/validators/test_function.py, demonstrates how a custom validator can inspect both the global configuration and the runtime context.

from pydantic_core import CoreConfig, SchemaValidator, core_schema

def validator_func(input_value, info: core_schema.ValidationInfo):
# Access global config
assert info.config.get('title') == 'hello'
# Access runtime context
assert info.context == {'user_id': 123}
return input_value

v = SchemaValidator(
core_schema.with_info_before_validator_function(validator_func, core_schema.str_schema()),
config=CoreConfig(title='hello')
)

# Passing context at runtime
v.validate_python('some input', context={'user_id': 123})

Cross-Field Validation and Field Metadata

One of the most powerful features of ValidationInfo is its ability to facilitate cross-field validation through the data and field_name properties.

  • info.data: In the context of a TypedDict or a model, info.data contains the values of fields that have already been successfully validated. This allows a field's validation logic to depend on the value of another field.
  • info.field_name: This tells the validator which specific field it is currently processing.

Example: Cross-Field Validation

This example (adapted from pydantic-core/tests/validators/test_function.py) shows a validator for field x accessing the value of other_field.

from typing import Any
from pydantic_core import SchemaValidator, core_schema

def check_dependency(input_value: Any, info: core_schema.ValidationInfo) -> Any:
# info.field_name will be 'x'
# info.data will contain {'other_field': 1}
if info.data.get('other_field') == 1:
return f'Validated {info.field_name} with dependency: {input_value}'
return input_value

v = SchemaValidator(
core_schema.typed_dict_schema({
'other_field': core_schema.typed_dict_field(core_schema.int_schema()),
'x': core_schema.typed_dict_field(
core_schema.with_info_after_validator_function(check_dependency, core_schema.str_schema())
),
})
)

result = v.validate_python({'other_field': 1, 'x': 'hello'})
# result == {'other_field': 1, 'x': 'Validated x with dependency: hello'}

Validation Modes

The info.mode property allows validators to behave differently depending on whether they are processing Python objects or JSON strings. This is particularly useful when certain coercions or checks are only appropriate for one input type.

def mode_aware_validator(input_value, info: core_schema.ValidationInfo):
if info.mode == 'json':
# Perform specific logic for JSON inputs
pass
return input_value

By combining the static settings in CoreConfig with the dynamic state in ValidationInfo, the validation engine provides a flexible environment for implementing complex, context-aware validation logic.