new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Pydantic · API reference · all subjects

custom validation

15 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

PydanticUseDefault exception in partial JSON parsing

pydantic_core.PydanticUseDefault is an exception that can be raised in validators to signal that a default value should be used. This is useful when handling missing fields from partial JSON parsing.

__get_pydantic_core_schema__ for custom validators on Annotated types

To create a custom validator attached to an Annotated type, implement the __get_pydantic_core_schema__ method that takes source_type and a GetCoreSchemaHandler, and returns a CoreSchema. This method allows you to customize the schema of the annotated type and add custom validation logic.

ValidatorFunctionWrapHandler in wrap validators

A wrap validator receives a ValidatorFunctionWrapHandler as the handler parameter. The handler function is called to perform standard pydantic validation of the input. Wrap validators can perform operations both before and after calling the handler to validate the input.

core_schema.no_info_wrap_validator_function

The core_schema.no_info_wrap_validator_function is used to create a wrap validator schema. It takes two parameters: the validator function and the schema to wrap (typically obtained from handler(source_type)).

Custom datetime validator with timezone constraint via Annotated metadata

This example shows how to create a custom validator using __get_pydantic_core_schema__ that enforces timezone constraints on datetime objects. The validator ensures a datetime object has the correct timezone, supports string specification of the timezone, and raises an error if the timezone does not match the constraint.

Custom datetime validator with UTC offset bounds

This example demonstrates a wrap validator that checks datetime UTC offset is within inclusive bounds. It calls handler(value) to validate the input, then verifies the utcoffset().total_seconds() / 3600 value falls between lower_bound and upper_bound parameters.

model_validator with mode='after' for nested model validation

A model_validator with mode='after' can be placed on the outer model to validate fields of nested models using data from the parent model. The validator function receives self with all fields already validated and must return Self.

field_validator with ValidationInfo context for nested validation

A field_validator can access validation context via the ValidationInfo parameter to receive data from parent models. Use info.context.get() to retrieve values passed via the context parameter in model_validate().

Passing validation context to model_validate

The model_validate() method accepts a context parameter (a dictionary) that is passed to all validators. If context is not included, info.context will be None in field validators.

Mutating context within validators for nested validation

Validation context can be mutated within validators using info.context.update() to pass data from parent to child validators. This approach adds power to nested validation but can make code harder to debug.

Custom validator error messages matter for debugging

Error messages written in raise ValueError() calls in custom validators are displayed when validation fails and are consumed by tooling that processes structured errors. Craft these messages carefully as they will be read during troubleshooting and debugging of real data.

Custom datetime validator with timezone constraint example code

```python import datetime as dt from dataclasses import dataclass from typing import Annotated, Any import pytz from pydantic_core import CoreSchema, core_schema from pydantic import ( GetCoreSchemaHandler, PydanticUserError, TypeAdapter, ValidationError, ValidatorFunctionWrapHandler, ) @dataclass(frozen=True) class MyDatetimeValidator: tz_constraint: str | None = None def tz_constraint_validator( self, value: dt.datetime, handler: ValidatorFunctionWrapHandler, ): """Validate tz_constraint and tz_info.""" if self.tz_constraint is None: assert ( value.tzinfo is None ), 'tz_constraint is None, but provided value is tz-aware.' return handler(value) if self.tz_constraint not in pytz.all_timezones: raise PydanticUserError( f'Invalid tz_constraint: {self.tz_constraint}', code='unevaluable-type-annotation', ) result = handler(value) assert self.tz_constraint == str( result.tzinfo ), f'Invalid tzinfo: {str(result.tzinfo)}, expected: {self.tz_constraint}' return result def __get_pydantic_core_schema__( self, source_type: Any, handler: GetCoreSchemaHandler, ) -> CoreSchema: return core_schema.no_info_wrap_validator_function( self.tz_constraint_validator, handler(source_type), ) LA = 'America/Los_Angeles' ta = TypeAdapter(Annotated[dt.datetime, MyDatetimeValidator(LA)]) print( ta.validate_python(dt.datetime(2023, 1, 1, 0, 0, tzinfo=pytz.timezone(LA))) ) ``` This example creates a custom validator via Annotated metadata that enforces a specific timezone constraint on datetime objects.

Nested model validation with model_validator example

```python from typing_extensions import Self from pydantic import BaseModel, ValidationError, model_validator class User(BaseModel): username: str password: str class Organization(BaseModel): forbidden_passwords: list[str] users: list[User] @model_validator(mode='after') def validate_user_passwords(self) -> Self: for user in self.users: current_pw = user.password if current_pw in self.forbidden_passwords: raise ValueError( f'Password {current_pw} is forbidden. Please choose another password for user {user.username}.' ) return self data = { 'forbidden_passwords': ['123'], 'users': [ {'username': 'Spartacat', 'password': '123'}, {'username': 'Iceburgh', 'password': '87'}, ], } try: org = Organization(**data) except ValidationError as e: print(e) ``` This example shows how to use a model_validator on the outer model to validate nested model fields using parent model data.

Nested model validation with field_validator and context example

```python from pydantic import BaseModel, ValidationError, ValidationInfo, field_validator class User(BaseModel): username: str password: str @field_validator('password', mode='after') @classmethod def validate_user_passwords( cls, password: str, info: ValidationInfo ) -> str: forbidden_passwords = ( info.context.get('forbidden_passwords', []) if info.context else [] ) if password in forbidden_passwords: raise ValueError(f'Password {password} is forbidden.') return password class Organization(BaseModel): forbidden_passwords: list[str] users: list[User] @field_validator('forbidden_passwords', mode='after') @classmethod def add_context(cls, v: list[str], info: ValidationInfo) -> list[str]: if info.context is not None: info.context.update({'forbidden_passwords': v}) return v data = { 'forbidden_passwords': ['123'], 'users': [ {'username': 'Spartacat', 'password': '123'}, {'username': 'Iceburgh', 'password': '87'}, ], } try: org = Organization.model_validate(data, context={}) except ValidationError as e: print(e) ``` This example demonstrates passing forbidden passwords from parent model to nested model validators via validation context.

Example: detecting cyclic references with field_validator

```python from __future__ import annotations from collections.abc import Generator from contextlib import contextmanager from dataclasses import field from pydantic import BaseModel, ValidationError, field_validator def is_recursion_validation_error(exc: ValidationError) -> bool: errors = exc.errors() return len(errors) == 1 and errors[0]['type'] == 'recursion_loop' @contextmanager def suppress_recursion_validation_error() -> Generator[None]: try: yield except ValidationError as exc: if not is_recursion_validation_error(exc): raise exc class Node(BaseModel): id: int children: list[Node] = field(default_factory=list) @field_validator('children', mode='wrap') @classmethod def drop_cyclic_references(cls, children, h): try: return h(children) except ValidationError as exc: if not ( is_recursion_validation_error(exc) and isinstance(children, list) ): raise exc value_without_cyclic_refs = [] for child in children: with suppress_recursion_validation_error(): value_without_cyclic_refs.extend(h([child])) return h(value_without_cyclic_refs) ``` This example shows how to handle cyclic references in validation by using a wrap mode field_validator to catch recursion_loop errors and filter out cyclic references.

Give your agent this brain