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