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 functools import partialmethod
9from types import FunctionType
10from typing import TYPE_CHECKING, Annotated, Any, Callable, Literal, TypeVar, Union, cast, overload
11
12from pydantic_core import PydanticUndefined, core_schema
13from typing_extensions import Self, TypeAlias
14
15from ._internal import _decorators, _generics, _internal_dataclass
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, **_internal_dataclass.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, **_internal_dataclass.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, **_internal_dataclass.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, Union
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, go {type(v)}')
183
184 return int(v) + 1
185
186 MyInt = Annotated[
187 int,
188 PlainValidator(validate, json_schema_input_type=Union[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, **_internal_dataclass.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 = Union[
354 _V2ValidatorClsMethod,
355 core_schema.WithInfoValidatorFunction,
356 _OnlyValueValidatorClsMethod,
357 core_schema.NoInfoValidatorFunction,
358 ]
359
360 _V2WrapValidator = Union[
361 _V2WrapValidatorClsMethod,
362 core_schema.WithInfoWrapValidatorFunction,
363 _OnlyValueWrapValidatorClsMethod,
364 core_schema.NoInfoWrapValidatorFunction,
365 ]
366
367 _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
368
369 _V2BeforeAfterOrPlainValidatorType = TypeVar(
370 '_V2BeforeAfterOrPlainValidatorType',
371 bound=Union[_V2Validator, _PartialClsOrStaticMethod],
372 )
373 _V2WrapValidatorType = TypeVar('_V2WrapValidatorType', bound=Union[_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(
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 Args:
460 field: The first field the `field_validator` should be called on; this is separate
461 from `fields` to ensure an error is raised if you don't pass at least one.
462 *fields: Additional field(s) the `field_validator` should be called on.
463 mode: Specifies whether to validate the fields before or after validation.
464 check_fields: Whether to check that the fields actually exist on the model.
465 json_schema_input_type: The input type of the function. This is only used to generate
466 the appropriate JSON Schema (in validation mode) and can only specified
467 when `mode` is either `'before'`, `'plain'` or `'wrap'`.
468
469 Returns:
470 A decorator that can be used to decorate a function to be used as a field_validator.
471
472 Raises:
473 PydanticUserError:
474 - If `@field_validator` is used bare (with no fields).
475 - If the args passed to `@field_validator` as fields are not strings.
476 - If `@field_validator` applied to instance methods.
477 """
478 if isinstance(field, FunctionType):
479 raise PydanticUserError(
480 '`@field_validator` should be used with fields and keyword arguments, not bare. '
481 "E.g. usage should be `@validator('<field_name>', ...)`",
482 code='validator-no-fields',
483 )
484
485 if mode not in ('before', 'plain', 'wrap') and json_schema_input_type is not PydanticUndefined:
486 raise PydanticUserError(
487 f"`json_schema_input_type` can't be used when mode is set to {mode!r}",
488 code='validator-input-type',
489 )
490
491 if json_schema_input_type is PydanticUndefined and mode == 'plain':
492 json_schema_input_type = Any
493
494 fields = field, *fields
495 if not all(isinstance(field, str) for field in fields):
496 raise PydanticUserError(
497 '`@field_validator` fields should be passed as separate string args. '
498 "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)`",
499 code='validator-invalid-fields',
500 )
501
502 def dec(
503 f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any],
504 ) -> _decorators.PydanticDescriptorProxy[Any]:
505 if _decorators.is_instance_method_from_sig(f):
506 raise PydanticUserError(
507 '`@field_validator` cannot be applied to instance methods', code='validator-instance-method'
508 )
509
510 # auto apply the @classmethod decorator
511 f = _decorators.ensure_classmethod_based_on_signature(f)
512
513 dec_info = _decorators.FieldValidatorDecoratorInfo(
514 fields=fields, mode=mode, check_fields=check_fields, json_schema_input_type=json_schema_input_type
515 )
516 return _decorators.PydanticDescriptorProxy(f, dec_info)
517
518 return dec
519
520
521_ModelType = TypeVar('_ModelType')
522_ModelTypeCo = TypeVar('_ModelTypeCo', covariant=True)
523
524
525class ModelWrapValidatorHandler(core_schema.ValidatorFunctionWrapHandler, Protocol[_ModelTypeCo]):
526 """`@model_validator` decorated function handler argument type. This is used when `mode='wrap'`."""
527
528 def __call__( # noqa: D102
529 self,
530 value: Any,
531 outer_location: str | int | None = None,
532 /,
533 ) -> _ModelTypeCo: # pragma: no cover
534 ...
535
536
537class ModelWrapValidatorWithoutInfo(Protocol[_ModelType]):
538 """A `@model_validator` decorated function signature.
539 This is used when `mode='wrap'` and the function does not have info argument.
540 """
541
542 def __call__( # noqa: D102
543 self,
544 cls: type[_ModelType],
545 # this can be a dict, a model instance
546 # or anything else that gets passed to validate_python
547 # thus validators _must_ handle all cases
548 value: Any,
549 handler: ModelWrapValidatorHandler[_ModelType],
550 /,
551 ) -> _ModelType: ...
552
553
554class ModelWrapValidator(Protocol[_ModelType]):
555 """A `@model_validator` decorated function signature. This is used when `mode='wrap'`."""
556
557 def __call__( # noqa: D102
558 self,
559 cls: type[_ModelType],
560 # this can be a dict, a model instance
561 # or anything else that gets passed to validate_python
562 # thus validators _must_ handle all cases
563 value: Any,
564 handler: ModelWrapValidatorHandler[_ModelType],
565 info: core_schema.ValidationInfo,
566 /,
567 ) -> _ModelType: ...
568
569
570class FreeModelBeforeValidatorWithoutInfo(Protocol):
571 """A `@model_validator` decorated function signature.
572 This is used when `mode='before'` and the function does not have info argument.
573 """
574
575 def __call__( # noqa: D102
576 self,
577 # this can be a dict, a model instance
578 # or anything else that gets passed to validate_python
579 # thus validators _must_ handle all cases
580 value: Any,
581 /,
582 ) -> Any: ...
583
584
585class ModelBeforeValidatorWithoutInfo(Protocol):
586 """A `@model_validator` decorated function signature.
587 This is used when `mode='before'` and the function does not have info argument.
588 """
589
590 def __call__( # noqa: D102
591 self,
592 cls: Any,
593 # this can be a dict, a model instance
594 # or anything else that gets passed to validate_python
595 # thus validators _must_ handle all cases
596 value: Any,
597 /,
598 ) -> Any: ...
599
600
601class FreeModelBeforeValidator(Protocol):
602 """A `@model_validator` decorated function signature. This is used when `mode='before'`."""
603
604 def __call__( # noqa: D102
605 self,
606 # this can be a dict, a model instance
607 # or anything else that gets passed to validate_python
608 # thus validators _must_ handle all cases
609 value: Any,
610 info: core_schema.ValidationInfo[Any],
611 /,
612 ) -> Any: ...
613
614
615class ModelBeforeValidator(Protocol):
616 """A `@model_validator` decorated function signature. This is used when `mode='before'`."""
617
618 def __call__( # noqa: D102
619 self,
620 cls: Any,
621 # this can be a dict, a model instance
622 # or anything else that gets passed to validate_python
623 # thus validators _must_ handle all cases
624 value: Any,
625 info: core_schema.ValidationInfo[Any],
626 /,
627 ) -> Any: ...
628
629
630ModelAfterValidatorWithoutInfo = Callable[[_ModelType], _ModelType]
631"""A `@model_validator` decorated function signature. This is used when `mode='after'` and the function does not
632have info argument.
633"""
634
635ModelAfterValidator = Callable[[_ModelType, core_schema.ValidationInfo[Any]], _ModelType]
636"""A `@model_validator` decorated function signature. This is used when `mode='after'`."""
637
638_AnyModelWrapValidator = Union[ModelWrapValidator[_ModelType], ModelWrapValidatorWithoutInfo[_ModelType]]
639_AnyModelBeforeValidator = Union[
640 FreeModelBeforeValidator, ModelBeforeValidator, FreeModelBeforeValidatorWithoutInfo, ModelBeforeValidatorWithoutInfo
641]
642_AnyModelAfterValidator = Union[ModelAfterValidator[_ModelType], ModelAfterValidatorWithoutInfo[_ModelType]]
643
644
645@overload
646def model_validator(
647 *,
648 mode: Literal['wrap'],
649) -> Callable[
650 [_AnyModelWrapValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
651]: ...
652
653
654@overload
655def model_validator(
656 *,
657 mode: Literal['before'],
658) -> Callable[
659 [_AnyModelBeforeValidator], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
660]: ...
661
662
663@overload
664def model_validator(
665 *,
666 mode: Literal['after'],
667) -> Callable[
668 [_AnyModelAfterValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
669]: ...
670
671
672def model_validator(
673 *,
674 mode: Literal['wrap', 'before', 'after'],
675) -> Any:
676 """!!! abstract "Usage Documentation"
677 [Model Validators](../concepts/validators.md#model-validators)
678
679 Decorate model methods for validation purposes.
680
681 Example usage:
682 ```python
683 from typing_extensions import Self
684
685 from pydantic import BaseModel, ValidationError, model_validator
686
687 class Square(BaseModel):
688 width: float
689 height: float
690
691 @model_validator(mode='after')
692 def verify_square(self) -> Self:
693 if self.width != self.height:
694 raise ValueError('width and height do not match')
695 return self
696
697 s = Square(width=1, height=1)
698 print(repr(s))
699 #> Square(width=1.0, height=1.0)
700
701 try:
702 Square(width=1, height=2)
703 except ValidationError as e:
704 print(e)
705 '''
706 1 validation error for Square
707 Value error, width and height do not match [type=value_error, input_value={'width': 1, 'height': 2}, input_type=dict]
708 '''
709 ```
710
711 For more in depth examples, see [Model Validators](../concepts/validators.md#model-validators).
712
713 Args:
714 mode: A required string literal that specifies the validation mode.
715 It can be one of the following: 'wrap', 'before', or 'after'.
716
717 Returns:
718 A decorator that can be used to decorate a function to be used as a model validator.
719 """
720
721 def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
722 # auto apply the @classmethod decorator. NOTE: in V3, do not apply the conversion for 'after' validators:
723 f = _decorators.ensure_classmethod_based_on_signature(f)
724 if mode == 'after' and isinstance(f, classmethod):
725 warnings.warn(
726 category=PydanticDeprecatedSince212,
727 message=(
728 "Using `@model_validator` with mode='after' on a classmethod is deprecated. Instead, use an instance method. "
729 f'See the documentation at https://docs.pydantic.dev/{version_short()}/concepts/validators/#model-after-validator.'
730 ),
731 stacklevel=2,
732 )
733
734 dec_info = _decorators.ModelValidatorDecoratorInfo(mode=mode)
735 return _decorators.PydanticDescriptorProxy(f, dec_info)
736
737 return dec
738
739
740AnyType = TypeVar('AnyType')
741
742
743if TYPE_CHECKING:
744 # If we add configurable attributes to IsInstance, we'd probably need to stop hiding it from type checkers like this
745 InstanceOf = Annotated[AnyType, ...] # `IsInstance[Sequence]` will be recognized by type checkers as `Sequence`
746
747else:
748
749 @dataclasses.dataclass(**_internal_dataclass.slots_true)
750 class InstanceOf:
751 '''Generic type for annotating a type that is an instance of a given class.
752
753 Example:
754 ```python
755 from pydantic import BaseModel, InstanceOf
756
757 class Foo:
758 ...
759
760 class Bar(BaseModel):
761 foo: InstanceOf[Foo]
762
763 Bar(foo=Foo())
764 try:
765 Bar(foo=42)
766 except ValidationError as e:
767 print(e)
768 """
769 [
770 │ {
771 │ │ 'type': 'is_instance_of',
772 │ │ 'loc': ('foo',),
773 │ │ 'msg': 'Input should be an instance of Foo',
774 │ │ 'input': 42,
775 │ │ 'ctx': {'class': 'Foo'},
776 │ │ 'url': 'https://errors.pydantic.dev/0.38.0/v/is_instance_of'
777 │ }
778 ]
779 """
780 ```
781 '''
782
783 @classmethod
784 def __class_getitem__(cls, item: AnyType) -> AnyType:
785 return Annotated[item, cls()]
786
787 @classmethod
788 def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
789 from pydantic import PydanticSchemaGenerationError
790
791 # use the generic _origin_ as the second argument to isinstance when appropriate
792 instance_of_schema = core_schema.is_instance_schema(_generics.get_origin(source) or source)
793
794 try:
795 # Try to generate the "standard" schema, which will be used when loading from JSON
796 original_schema = handler(source)
797 except PydanticSchemaGenerationError:
798 # If that fails, just produce a schema that can validate from python
799 return instance_of_schema
800 else:
801 # Use the "original" approach to serialization
802 instance_of_schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
803 function=lambda v, h: h(v), schema=original_schema
804 )
805 return core_schema.json_or_python_schema(python_schema=instance_of_schema, json_schema=original_schema)
806
807 __hash__ = object.__hash__
808
809
810if TYPE_CHECKING:
811 SkipValidation = Annotated[AnyType, ...] # SkipValidation[list[str]] will be treated by type checkers as list[str]
812else:
813
814 @dataclasses.dataclass(**_internal_dataclass.slots_true)
815 class SkipValidation:
816 """If this is applied as an annotation (e.g., via `x: Annotated[int, SkipValidation]`), validation will be
817 skipped. You can also use `SkipValidation[int]` as a shorthand for `Annotated[int, SkipValidation]`.
818
819 This can be useful if you want to use a type annotation for documentation/IDE/type-checking purposes,
820 and know that it is safe to skip validation for one or more of the fields.
821
822 Because this converts the validation schema to `any_schema`, subsequent annotation-applied transformations
823 may not have the expected effects. Therefore, when used, this annotation should generally be the final
824 annotation applied to a type.
825 """
826
827 def __class_getitem__(cls, item: Any) -> Any:
828 return Annotated[item, SkipValidation()]
829
830 @classmethod
831 def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
832 with warnings.catch_warnings():
833 warnings.simplefilter('ignore', ArbitraryTypeWarning)
834 original_schema = handler(source)
835 metadata = {'pydantic_js_annotation_functions': [lambda _c, h: h(original_schema)]}
836 return core_schema.any_schema(
837 metadata=metadata,
838 serialization=core_schema.wrap_serializer_function_ser_schema(
839 function=lambda v, h: h(v), schema=original_schema
840 ),
841 )
842
843 __hash__ = object.__hash__
844
845
846_FromTypeT = TypeVar('_FromTypeT')
847
848
849class ValidateAs:
850 """A helper class to validate a custom type from a type that is natively supported by Pydantic.
851
852 Args:
853 from_type: The type natively supported by Pydantic to use to perform validation.
854 instantiation_hook: A callable taking the validated type as an argument, and returning
855 the populated custom type.
856
857 Example:
858 ```python {lint="skip"}
859 from typing import Annotated
860
861 from pydantic import BaseModel, TypeAdapter, ValidateAs
862
863 class MyCls:
864 def __init__(self, a: int) -> None:
865 self.a = a
866
867 def __repr__(self) -> str:
868 return f"MyCls(a={self.a})"
869
870 class Model(BaseModel):
871 a: int
872
873
874 ta = TypeAdapter(
875 Annotated[MyCls, ValidateAs(Model, lambda v: MyCls(a=v.a))]
876 )
877
878 print(ta.validate_python({'a': 1}))
879 #> MyCls(a=1)
880 ```
881 """
882
883 # TODO: make use of PEP 747
884 def __init__(self, from_type: type[_FromTypeT], /, instantiation_hook: Callable[[_FromTypeT], Any]) -> None:
885 self.from_type = from_type
886 self.instantiation_hook = instantiation_hook
887
888 def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
889 schema = handler(self.from_type)
890 return core_schema.no_info_after_validator_function(
891 self.instantiation_hook,
892 schema=schema,
893 )