1"""This module contains related classes and functions for validation."""
2
3from __future__ import annotations as _annotations
4
5import dataclasses
6import sys
7import warnings
8from collections.abc import Callable
9from functools import partialmethod
10from typing import TYPE_CHECKING, Annotated, Any, Literal, TypeAlias, TypeVar, cast, overload
11
12from pydantic_core import PydanticUndefined, core_schema
13from typing_extensions import Self, TypeForm
14
15from ._internal import _decorators, _generics
16from .annotated_handlers import GetCoreSchemaHandler
17from .errors import PydanticUserError
18from .version import version_short
19from .warnings import ArbitraryTypeWarning, PydanticDeprecatedSince212
20
21if sys.version_info < (3, 11):
22 from typing_extensions import Protocol
23else:
24 from typing import Protocol
25
26_inspect_validator = _decorators.inspect_validator
27
28
29@dataclasses.dataclass(frozen=True, slots=True)
30class AfterValidator:
31 """!!! abstract "Usage Documentation"
32 [field *after* validators](../concepts/validators.md#field-after-validator)
33
34 A metadata class that indicates that a validation should be applied **after** the inner validation logic.
35
36 Attributes:
37 func: The validator function.
38
39 Example:
40 ```python
41 from typing import Annotated
42
43 from pydantic import AfterValidator, BaseModel, ValidationError
44
45 MyInt = Annotated[int, AfterValidator(lambda v: v + 1)]
46
47 class Model(BaseModel):
48 a: MyInt
49
50 print(Model(a=1).a)
51 #> 2
52
53 try:
54 Model(a='a')
55 except ValidationError as e:
56 print(e.json(indent=2))
57 '''
58 [
59 {
60 "type": "int_parsing",
61 "loc": [
62 "a"
63 ],
64 "msg": "Input should be a valid integer, unable to parse string as an integer",
65 "input": "a",
66 "url": "https://errors.pydantic.dev/2/v/int_parsing"
67 }
68 ]
69 '''
70 ```
71 """
72
73 func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
74
75 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
76 schema = handler(source_type)
77 info_arg = _inspect_validator(self.func, mode='after', type='field')
78 if info_arg:
79 func = cast(core_schema.WithInfoValidatorFunction, self.func)
80 return core_schema.with_info_after_validator_function(func, schema=schema)
81 else:
82 func = cast(core_schema.NoInfoValidatorFunction, self.func)
83 return core_schema.no_info_after_validator_function(func, schema=schema)
84
85 @classmethod
86 def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
87 return cls(func=decorator.func)
88
89
90@dataclasses.dataclass(frozen=True, slots=True)
91class BeforeValidator:
92 """!!! abstract "Usage Documentation"
93 [field *before* validators](../concepts/validators.md#field-before-validator)
94
95 A metadata class that indicates that a validation should be applied **before** the inner validation logic.
96
97 Attributes:
98 func: The validator function.
99 json_schema_input_type: The input type used to generate the appropriate
100 JSON Schema (in validation mode). The actual input type is `Any`.
101
102 Example:
103 ```python
104 from typing import Annotated
105
106 from pydantic import BaseModel, BeforeValidator
107
108 MyInt = Annotated[int, BeforeValidator(lambda v: v + 1)]
109
110 class Model(BaseModel):
111 a: MyInt
112
113 print(Model(a=1).a)
114 #> 2
115
116 try:
117 Model(a='a')
118 except TypeError as e:
119 print(e)
120 #> can only concatenate str (not "int") to str
121 ```
122 """
123
124 func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
125 json_schema_input_type: Any = PydanticUndefined
126
127 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
128 schema = handler(source_type)
129 input_schema = (
130 None
131 if self.json_schema_input_type is PydanticUndefined
132 else handler.generate_schema(self.json_schema_input_type)
133 )
134
135 info_arg = _inspect_validator(self.func, mode='before', type='field')
136 if info_arg:
137 func = cast(core_schema.WithInfoValidatorFunction, self.func)
138 return core_schema.with_info_before_validator_function(
139 func,
140 schema=schema,
141 json_schema_input_schema=input_schema,
142 )
143 else:
144 func = cast(core_schema.NoInfoValidatorFunction, self.func)
145 return core_schema.no_info_before_validator_function(
146 func, schema=schema, json_schema_input_schema=input_schema
147 )
148
149 @classmethod
150 def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
151 return cls(
152 func=decorator.func,
153 json_schema_input_type=decorator.info.json_schema_input_type,
154 )
155
156
157@dataclasses.dataclass(frozen=True, slots=True)
158class PlainValidator:
159 """!!! abstract "Usage Documentation"
160 [field *plain* validators](../concepts/validators.md#field-plain-validator)
161
162 A metadata class that indicates that a validation should be applied **instead** of the inner validation logic.
163
164 !!! note
165 Before v2.9, `PlainValidator` wasn't always compatible with JSON Schema generation for `mode='validation'`.
166 You can now use the `json_schema_input_type` argument to specify the input type of the function
167 to be used in the JSON schema when `mode='validation'` (the default). See the example below for more details.
168
169 Attributes:
170 func: The validator function.
171 json_schema_input_type: The input type used to generate the appropriate
172 JSON Schema (in validation mode). The actual input type is `Any`.
173
174 Example:
175 ```python
176 from typing import Annotated
177
178 from pydantic import BaseModel, PlainValidator
179
180 def validate(v: object) -> int:
181 if not isinstance(v, (int, str)):
182 raise ValueError(f'Expected int or str, got {type(v)}')
183
184 return int(v) + 1
185
186 MyInt = Annotated[
187 int,
188 PlainValidator(validate, json_schema_input_type=str | int), # (1)!
189 ]
190
191 class Model(BaseModel):
192 a: MyInt
193
194 print(Model(a='1').a)
195 #> 2
196
197 print(Model(a=1).a)
198 #> 2
199 ```
200
201 1. In this example, we've specified the `json_schema_input_type` as `Union[str, int]` which indicates to the JSON schema
202 generator that in validation mode, the input type for the `a` field can be either a [`str`][] or an [`int`][].
203 """
204
205 func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
206 json_schema_input_type: Any = Any
207
208 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
209 # Note that for some valid uses of PlainValidator, it is not possible to generate a core schema for the
210 # source_type, so calling `handler(source_type)` will error, which prevents us from generating a proper
211 # serialization schema. To work around this for use cases that will not involve serialization, we simply
212 # catch any PydanticSchemaGenerationError that may be raised while attempting to build the serialization schema
213 # and abort any attempts to handle special serialization.
214 from pydantic import PydanticSchemaGenerationError
215
216 try:
217 schema = handler(source_type)
218 # TODO if `schema['serialization']` is one of `'include-exclude-dict/sequence',
219 # schema validation will fail. That's why we use 'type ignore' comments below.
220 serialization = schema.get(
221 'serialization',
222 core_schema.wrap_serializer_function_ser_schema(
223 function=lambda v, h: h(v),
224 schema=schema,
225 return_schema=handler.generate_schema(source_type),
226 ),
227 )
228 except PydanticSchemaGenerationError:
229 serialization = None
230
231 input_schema = handler.generate_schema(self.json_schema_input_type)
232
233 info_arg = _inspect_validator(self.func, mode='plain', type='field')
234 if info_arg:
235 func = cast(core_schema.WithInfoValidatorFunction, self.func)
236 return core_schema.with_info_plain_validator_function(
237 func,
238 serialization=serialization, # pyright: ignore[reportArgumentType]
239 json_schema_input_schema=input_schema,
240 )
241 else:
242 func = cast(core_schema.NoInfoValidatorFunction, self.func)
243 return core_schema.no_info_plain_validator_function(
244 func,
245 serialization=serialization, # pyright: ignore[reportArgumentType]
246 json_schema_input_schema=input_schema,
247 )
248
249 @classmethod
250 def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
251 return cls(
252 func=decorator.func,
253 json_schema_input_type=decorator.info.json_schema_input_type,
254 )
255
256
257@dataclasses.dataclass(frozen=True, slots=True)
258class WrapValidator:
259 """!!! abstract "Usage Documentation"
260 [field *wrap* validators](../concepts/validators.md#field-wrap-validator)
261
262 A metadata class that indicates that a validation should be applied **around** the inner validation logic.
263
264 Attributes:
265 func: The validator function.
266 json_schema_input_type: The input type used to generate the appropriate
267 JSON Schema (in validation mode). The actual input type is `Any`.
268
269 ```python
270 from datetime import datetime
271 from typing import Annotated
272
273 from pydantic import BaseModel, ValidationError, WrapValidator
274
275 def validate_timestamp(v, handler):
276 if v == 'now':
277 # we don't want to bother with further validation, just return the new value
278 return datetime.now()
279 try:
280 return handler(v)
281 except ValidationError:
282 # validation failed, in this case we want to return a default value
283 return datetime(2000, 1, 1)
284
285 MyTimestamp = Annotated[datetime, WrapValidator(validate_timestamp)]
286
287 class Model(BaseModel):
288 a: MyTimestamp
289
290 print(Model(a='now').a)
291 #> 2032-01-02 03:04:05.000006
292 print(Model(a='invalid').a)
293 #> 2000-01-01 00:00:00
294 ```
295 """
296
297 func: core_schema.NoInfoWrapValidatorFunction | core_schema.WithInfoWrapValidatorFunction
298 json_schema_input_type: Any = PydanticUndefined
299
300 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
301 schema = handler(source_type)
302 input_schema = (
303 None
304 if self.json_schema_input_type is PydanticUndefined
305 else handler.generate_schema(self.json_schema_input_type)
306 )
307
308 info_arg = _inspect_validator(self.func, mode='wrap', type='field')
309 if info_arg:
310 func = cast(core_schema.WithInfoWrapValidatorFunction, self.func)
311 return core_schema.with_info_wrap_validator_function(
312 func,
313 schema=schema,
314 json_schema_input_schema=input_schema,
315 )
316 else:
317 func = cast(core_schema.NoInfoWrapValidatorFunction, self.func)
318 return core_schema.no_info_wrap_validator_function(
319 func,
320 schema=schema,
321 json_schema_input_schema=input_schema,
322 )
323
324 @classmethod
325 def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
326 return cls(
327 func=decorator.func,
328 json_schema_input_type=decorator.info.json_schema_input_type,
329 )
330
331
332if TYPE_CHECKING:
333
334 class _OnlyValueValidatorClsMethod(Protocol):
335 def __call__(self, cls: Any, value: Any, /) -> Any: ...
336
337 class _V2ValidatorClsMethod(Protocol):
338 def __call__(self, cls: Any, value: Any, info: core_schema.ValidationInfo[Any], /) -> Any: ...
339
340 class _OnlyValueWrapValidatorClsMethod(Protocol):
341 def __call__(self, cls: Any, value: Any, handler: core_schema.ValidatorFunctionWrapHandler, /) -> Any: ...
342
343 class _V2WrapValidatorClsMethod(Protocol):
344 def __call__(
345 self,
346 cls: Any,
347 value: Any,
348 handler: core_schema.ValidatorFunctionWrapHandler,
349 info: core_schema.ValidationInfo[Any],
350 /,
351 ) -> Any: ...
352
353 _V2Validator: TypeAlias = (
354 _V2ValidatorClsMethod
355 | core_schema.WithInfoValidatorFunction
356 | _OnlyValueValidatorClsMethod
357 | core_schema.NoInfoValidatorFunction
358 )
359
360 _V2WrapValidator: TypeAlias = (
361 _V2WrapValidatorClsMethod
362 | core_schema.WithInfoWrapValidatorFunction
363 | _OnlyValueWrapValidatorClsMethod
364 | core_schema.NoInfoWrapValidatorFunction
365 )
366
367 _PartialClsOrStaticMethod: TypeAlias = classmethod[Any, Any, Any] | staticmethod[Any, Any] | partialmethod[Any]
368
369 _V2BeforeAfterOrPlainValidatorType = TypeVar(
370 '_V2BeforeAfterOrPlainValidatorType',
371 bound=_V2Validator | _PartialClsOrStaticMethod,
372 )
373 _V2WrapValidatorType = TypeVar('_V2WrapValidatorType', bound=_V2WrapValidator | _PartialClsOrStaticMethod)
374
375FieldValidatorModes: TypeAlias = Literal['before', 'after', 'wrap', 'plain']
376
377
378@overload
379def field_validator(
380 field: str,
381 /,
382 *fields: str,
383 mode: Literal['wrap'],
384 check_fields: bool | None = ...,
385 json_schema_input_type: Any = ...,
386) -> Callable[[_V2WrapValidatorType], _V2WrapValidatorType]: ...
387
388
389@overload
390def field_validator(
391 field: str,
392 /,
393 *fields: str,
394 mode: Literal['before', 'plain'],
395 check_fields: bool | None = ...,
396 json_schema_input_type: Any = ...,
397) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ...
398
399
400@overload
401def field_validator(
402 field: str,
403 /,
404 *fields: str,
405 mode: Literal['after'] = ...,
406 check_fields: bool | None = ...,
407) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ...
408
409
410def field_validator( # noqa: D417
411 field: str,
412 /,
413 *fields: str,
414 mode: FieldValidatorModes = 'after',
415 check_fields: bool | None = None,
416 json_schema_input_type: Any = PydanticUndefined,
417) -> Callable[[Any], Any]:
418 """!!! abstract "Usage Documentation"
419 [field validators](../concepts/validators.md#field-validators)
420
421 Decorate methods on the class indicating that they should be used to validate fields.
422
423 Example usage:
424 ```python
425 from typing import Any
426
427 from pydantic import (
428 BaseModel,
429 ValidationError,
430 field_validator,
431 )
432
433 class Model(BaseModel):
434 a: str
435
436 @field_validator('a')
437 @classmethod
438 def ensure_foobar(cls, v: Any):
439 if 'foobar' not in v:
440 raise ValueError('"foobar" not found in a')
441 return v
442
443 print(repr(Model(a='this is foobar good')))
444 #> Model(a='this is foobar good')
445
446 try:
447 Model(a='snap')
448 except ValidationError as exc_info:
449 print(exc_info)
450 '''
451 1 validation error for Model
452 a
453 Value error, "foobar" not found in a [type=value_error, input_value='snap', input_type=str]
454 '''
455 ```
456
457 For more in depth examples, see [Field Validators](../concepts/validators.md#field-validators).
458
459 Errors raised in a field validator become part of the model's
460 [`ValidationError`][pydantic_core.ValidationError]. In a running application,
461 [Logfire](../integrations/logfire.md) can record the input each failed validation rejected — see
462 [Troubleshooting validation errors](../errors/troubleshooting.md).
463
464 Args:
465 *fields: The field names the validator should apply to.
466 mode: Specifies whether to validate the fields before or after validation.
467 check_fields: Whether to check that the fields actually exist on the model.
468 json_schema_input_type: The input type of the function. This is only used to generate
469 the appropriate JSON Schema (in validation mode) and can only specified
470 when `mode` is either `'before'`, `'plain'` or `'wrap'`.
471
472 Raises:
473 PydanticUserError:
474 - If the decorator is used without any arguments (at least one field name must be provided).
475 - If the provided field names are not strings.
476 - If `json_schema_input_type` is provided with an unsupported `mode`.
477 - If the decorator is applied to an instance method.
478 """
479 if callable(field) or isinstance(field, classmethod):
480 raise PydanticUserError(
481 'The `@field_validator` decorator cannot be used without arguments, at least one field must be provided. '
482 "For example: `@field_validator('<field_name>', ...)`.",
483 code='decorator-missing-arguments',
484 )
485
486 if mode not in ('before', 'plain', 'wrap') and json_schema_input_type is not PydanticUndefined:
487 raise PydanticUserError(
488 f"`json_schema_input_type` can't be used when mode is set to {mode!r}",
489 code='validator-input-type',
490 )
491
492 if json_schema_input_type is PydanticUndefined and mode == 'plain':
493 json_schema_input_type = Any
494
495 fields = field, *fields
496 if not all(isinstance(field, str) for field in fields):
497 raise PydanticUserError(
498 'The provided field names to the `@field_validator` decorator should be strings. '
499 "For example: `@field_validator('<field_name_1>', '<field_name_2>', ...).`",
500 code='decorator-invalid-fields',
501 )
502
503 def dec(
504 f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any],
505 ) -> _decorators.PydanticDescriptorProxy[Any]:
506 if _decorators.is_instance_method_from_sig(f):
507 raise PydanticUserError(
508 'The `@field_validator` decorator cannot be applied to instance methods',
509 code='validator-instance-method',
510 )
511
512 # auto apply the @classmethod decorator
513 f = _decorators.ensure_classmethod_based_on_signature(f)
514
515 dec_info = _decorators.FieldValidatorDecoratorInfo(
516 fields=fields, mode=mode, check_fields=check_fields, json_schema_input_type=json_schema_input_type
517 )
518 return _decorators.PydanticDescriptorProxy(f, dec_info)
519
520 return dec
521
522
523_ModelType = TypeVar('_ModelType')
524_ModelTypeCo = TypeVar('_ModelTypeCo', covariant=True)
525
526
527class ModelWrapValidatorHandler(core_schema.ValidatorFunctionWrapHandler, Protocol[_ModelTypeCo]):
528 """`@model_validator` decorated function handler argument type. This is used when `mode='wrap'`."""
529
530 def __call__( # noqa: D102
531 self,
532 value: Any,
533 outer_location: str | int | None = None,
534 /,
535 ) -> _ModelTypeCo: # pragma: no cover
536 ...
537
538
539class ModelWrapValidatorWithoutInfo(Protocol[_ModelType]):
540 """A `@model_validator` decorated function signature.
541 This is used when `mode='wrap'` and the function does not have info argument.
542 """
543
544 def __call__( # noqa: D102
545 self,
546 cls: type[_ModelType],
547 # this can be a dict, a model instance
548 # or anything else that gets passed to validate_python
549 # thus validators _must_ handle all cases
550 value: Any,
551 handler: ModelWrapValidatorHandler[_ModelType],
552 /,
553 ) -> _ModelType: ...
554
555
556class ModelWrapValidator(Protocol[_ModelType]):
557 """A `@model_validator` decorated function signature. This is used when `mode='wrap'`."""
558
559 def __call__( # noqa: D102
560 self,
561 cls: type[_ModelType],
562 # this can be a dict, a model instance
563 # or anything else that gets passed to validate_python
564 # thus validators _must_ handle all cases
565 value: Any,
566 handler: ModelWrapValidatorHandler[_ModelType],
567 info: core_schema.ValidationInfo,
568 /,
569 ) -> _ModelType: ...
570
571
572class FreeModelBeforeValidatorWithoutInfo(Protocol):
573 """A `@model_validator` decorated function signature.
574 This is used when `mode='before'` and the function does not have info argument.
575 """
576
577 def __call__( # noqa: D102
578 self,
579 # this can be a dict, a model instance
580 # or anything else that gets passed to validate_python
581 # thus validators _must_ handle all cases
582 value: Any,
583 /,
584 ) -> Any: ...
585
586
587class ModelBeforeValidatorWithoutInfo(Protocol):
588 """A `@model_validator` decorated function signature.
589 This is used when `mode='before'` and the function does not have info argument.
590 """
591
592 def __call__( # noqa: D102
593 self,
594 cls: Any,
595 # this can be a dict, a model instance
596 # or anything else that gets passed to validate_python
597 # thus validators _must_ handle all cases
598 value: Any,
599 /,
600 ) -> Any: ...
601
602
603class FreeModelBeforeValidator(Protocol):
604 """A `@model_validator` decorated function signature. This is used when `mode='before'`."""
605
606 def __call__( # noqa: D102
607 self,
608 # this can be a dict, a model instance
609 # or anything else that gets passed to validate_python
610 # thus validators _must_ handle all cases
611 value: Any,
612 info: core_schema.ValidationInfo[Any],
613 /,
614 ) -> Any: ...
615
616
617class ModelBeforeValidator(Protocol):
618 """A `@model_validator` decorated function signature. This is used when `mode='before'`."""
619
620 def __call__( # noqa: D102
621 self,
622 cls: Any,
623 # this can be a dict, a model instance
624 # or anything else that gets passed to validate_python
625 # thus validators _must_ handle all cases
626 value: Any,
627 info: core_schema.ValidationInfo[Any],
628 /,
629 ) -> Any: ...
630
631
632ModelAfterValidatorWithoutInfo: TypeAlias = Callable[[_ModelType], _ModelType]
633"""A `@model_validator` decorated function signature. This is used when `mode='after'` and the function does not
634have info argument.
635"""
636
637ModelAfterValidator: TypeAlias = Callable[[_ModelType, core_schema.ValidationInfo[Any]], _ModelType]
638"""A `@model_validator` decorated function signature. This is used when `mode='after'`."""
639
640_AnyModelWrapValidator: TypeAlias = ModelWrapValidator[_ModelType] | ModelWrapValidatorWithoutInfo[_ModelType]
641_AnyModelBeforeValidator: TypeAlias = (
642 FreeModelBeforeValidator
643 | ModelBeforeValidator
644 | FreeModelBeforeValidatorWithoutInfo
645 | ModelBeforeValidatorWithoutInfo
646)
647_AnyModelAfterValidator: TypeAlias = ModelAfterValidator[_ModelType] | ModelAfterValidatorWithoutInfo[_ModelType]
648
649
650@overload
651def model_validator(
652 *,
653 mode: Literal['wrap'],
654) -> Callable[
655 [_AnyModelWrapValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
656]: ...
657
658
659@overload
660def model_validator(
661 *,
662 mode: Literal['before'],
663) -> Callable[
664 [_AnyModelBeforeValidator], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
665]: ...
666
667
668@overload
669def model_validator(
670 *,
671 mode: Literal['after'],
672) -> Callable[
673 [_AnyModelAfterValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
674]: ...
675
676
677def model_validator(
678 *,
679 mode: Literal['wrap', 'before', 'after'],
680) -> Any:
681 """!!! abstract "Usage Documentation"
682 [Model Validators](../concepts/validators.md#model-validators)
683
684 Decorate model methods for validation purposes.
685
686 Example usage:
687 ```python
688 from typing_extensions import Self
689
690 from pydantic import BaseModel, ValidationError, model_validator
691
692 class Square(BaseModel):
693 width: float
694 height: float
695
696 @model_validator(mode='after')
697 def verify_square(self) -> Self:
698 if self.width != self.height:
699 raise ValueError('width and height do not match')
700 return self
701
702 s = Square(width=1, height=1)
703 print(repr(s))
704 #> Square(width=1.0, height=1.0)
705
706 try:
707 Square(width=1, height=2)
708 except ValidationError as e:
709 print(e)
710 '''
711 1 validation error for Square
712 Value error, width and height do not match [type=value_error, input_value={'width': 1, 'height': 2}, input_type=dict]
713 '''
714 ```
715
716 For more in depth examples, see [Model Validators](../concepts/validators.md#model-validators).
717
718 Cross-field rules like the one above tend to fail on combinations of values you didn't anticipate.
719 The resulting [`ValidationError`][pydantic_core.ValidationError] includes the input that failed;
720 [Logfire](../integrations/logfire.md) can retain these failures with their trace context so you can
721 investigate patterns in a running application (see
722 [Troubleshooting validation errors](../errors/troubleshooting.md)).
723
724 Args:
725 mode: A required string literal that specifies the validation mode.
726 It can be one of the following: 'wrap', 'before', or 'after'.
727
728 Returns:
729 A decorator that can be used to decorate a function to be used as a model validator.
730 """
731
732 def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
733 # auto apply the @classmethod decorator. NOTE: in V3, do not apply the conversion for 'after' validators:
734 f = _decorators.ensure_classmethod_based_on_signature(f)
735 if mode == 'after' and isinstance(f, classmethod):
736 warnings.warn(
737 category=PydanticDeprecatedSince212,
738 message=(
739 "Using `@model_validator` with mode='after' on a classmethod is deprecated. Instead, use an instance method. "
740 f'See the documentation at https://pydantic.dev/docs/validation/{version_short()}/concepts/validators/#model-after-validator.'
741 ),
742 stacklevel=2,
743 )
744
745 dec_info = _decorators.ModelValidatorDecoratorInfo(mode=mode)
746 return _decorators.PydanticDescriptorProxy(f, dec_info)
747
748 return dec
749
750
751AnyType = TypeVar('AnyType')
752
753
754if TYPE_CHECKING:
755 # If we add configurable attributes to IsInstance, we'd probably need to stop hiding it from type checkers like this
756 InstanceOf = Annotated[AnyType, ...] # `IsInstance[Sequence]` will be recognized by type checkers as `Sequence`
757
758else:
759
760 @dataclasses.dataclass(slots=True)
761 class InstanceOf:
762 '''Generic type for annotating a type that is an instance of a given class.
763
764 Example:
765 ```python
766 from pydantic import BaseModel, InstanceOf
767
768 class Foo:
769 ...
770
771 class Bar(BaseModel):
772 foo: InstanceOf[Foo]
773
774 Bar(foo=Foo())
775 try:
776 Bar(foo=42)
777 except ValidationError as e:
778 print(e)
779 """
780 [
781 │ {
782 │ │ 'type': 'is_instance_of',
783 │ │ 'loc': ('foo',),
784 │ │ 'msg': 'Input should be an instance of Foo',
785 │ │ 'input': 42,
786 │ │ 'ctx': {'class': 'Foo'},
787 │ │ 'url': 'https://errors.pydantic.dev/0.38.0/v/is_instance_of'
788 │ }
789 ]
790 """
791 ```
792 '''
793
794 @classmethod
795 def __class_getitem__(cls, item: AnyType) -> AnyType:
796 return Annotated[item, cls()]
797
798 @classmethod
799 def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
800 # Lazy import to avoid cycle:
801 from pydantic._internal._generate_schema import GENERATE_SCHEMA_ERRORS
802
803 # use the generic _origin_ as the second argument to isinstance when appropriate
804 instance_of_schema = core_schema.is_instance_schema(_generics.get_origin(source) or source)
805
806 try:
807 # Try to generate the "standard" schema, which will be used when loading from JSON
808 original_schema = handler(source)
809 except GENERATE_SCHEMA_ERRORS:
810 # If that fails, just produce a schema that can validate from python
811 return instance_of_schema
812 else:
813 # Use the "original" approach to serialization
814 instance_of_schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
815 function=lambda v, h: h(v), schema=original_schema
816 )
817 return core_schema.json_or_python_schema(python_schema=instance_of_schema, json_schema=original_schema)
818
819 __hash__ = object.__hash__
820
821
822if TYPE_CHECKING:
823 SkipValidation = Annotated[AnyType, ...] # SkipValidation[list[str]] will be treated by type checkers as list[str]
824else:
825
826 @dataclasses.dataclass(slots=True)
827 class SkipValidation:
828 """If this is applied as an annotation (e.g., via `x: Annotated[int, SkipValidation]`), validation will be
829 skipped. You can also use `SkipValidation[int]` as a shorthand for `Annotated[int, SkipValidation]`.
830
831 This can be useful if you want to use a type annotation for documentation/IDE/type-checking purposes,
832 and know that it is safe to skip validation for one or more of the fields.
833
834 Because this converts the validation schema to `any_schema`, subsequent annotation-applied transformations
835 may not have the expected effects. Therefore, when used, this annotation should generally be the final
836 annotation applied to a type.
837 """
838
839 def __class_getitem__(cls, item: Any) -> Any:
840 return Annotated[item, SkipValidation()]
841
842 @classmethod
843 def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
844 with warnings.catch_warnings():
845 warnings.simplefilter('ignore', ArbitraryTypeWarning)
846 original_schema = handler(source)
847 metadata = {'pydantic_js_annotation_functions': [lambda _c, h: h(original_schema)]}
848 return core_schema.any_schema(
849 metadata=metadata,
850 serialization=core_schema.wrap_serializer_function_ser_schema(
851 function=lambda v, h: h(v), schema=original_schema
852 ),
853 )
854
855 __hash__ = object.__hash__
856
857
858_FromTypeT = TypeVar('_FromTypeT')
859
860
861class ValidateAs:
862 """A helper class to validate a custom type from a type that is natively supported by Pydantic.
863
864 Args:
865 from_type: The type natively supported by Pydantic to use to perform validation.
866 instantiation_hook: A callable taking the validated type as an argument, and returning
867 the populated custom type.
868
869 Example:
870 ```python {lint="skip"}
871 from typing import Annotated
872
873 from pydantic import BaseModel, TypeAdapter, ValidateAs
874
875 class MyCls:
876 def __init__(self, a: int) -> None:
877 self.a = a
878
879 def __repr__(self) -> str:
880 return f"MyCls(a={self.a})"
881
882 class Model(BaseModel):
883 a: int
884
885
886 ta = TypeAdapter(
887 Annotated[MyCls, ValidateAs(Model, lambda v: MyCls(a=v.a))]
888 )
889
890 print(ta.validate_python({'a': 1}))
891 #> MyCls(a=1)
892 ```
893 """
894
895 def __init__(self, from_type: TypeForm[_FromTypeT], /, instantiation_hook: Callable[[_FromTypeT], Any]) -> None:
896 self.from_type = from_type
897 self.instantiation_hook = instantiation_hook
898
899 def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
900 # Lazy import to avoid cycle:
901 from pydantic._internal._generate_schema import GENERATE_SCHEMA_ERRORS
902
903 from_type_schema = handler(self.from_type)
904 schema = core_schema.no_info_after_validator_function(
905 self.instantiation_hook,
906 schema=from_type_schema,
907 )
908 try:
909 ser_schema = handler(source)
910 except GENERATE_SCHEMA_ERRORS:
911 ser_schema = core_schema.any_schema()
912
913 # TODO: pydantic-core now accepts an arbitrary core schema for serialization, so the
914 # wrap serializer workaround below can be replaced with
915 # `schema['serialization'] = _core_utils.as_ser_schema(ser_schema)`.
916 # The same workaround is used in other parts of the code base, so grep for similar cases when fixing:
917 schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
918 function=lambda v, h: h(v), schema=ser_schema
919 )
920 return schema