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