functional_validators.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. """This module contains related classes and functions for validation."""
  2. from __future__ import annotations as _annotations
  3. import dataclasses
  4. import sys
  5. from functools import partialmethod
  6. from types import FunctionType
  7. from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union, cast, overload
  8. from pydantic_core import PydanticUndefined, core_schema
  9. from pydantic_core import core_schema as _core_schema
  10. from typing_extensions import Annotated, Literal, Self, TypeAlias
  11. from ._internal import _decorators, _generics, _internal_dataclass
  12. from .annotated_handlers import GetCoreSchemaHandler
  13. from .errors import PydanticUserError
  14. if sys.version_info < (3, 11):
  15. from typing_extensions import Protocol
  16. else:
  17. from typing import Protocol
  18. _inspect_validator = _decorators.inspect_validator
  19. @dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
  20. class AfterValidator:
  21. """Usage docs: https://docs.pydantic.dev/2.10/concepts/validators/#field-validators
  22. A metadata class that indicates that a validation should be applied **after** the inner validation logic.
  23. Attributes:
  24. func: The validator function.
  25. Example:
  26. ```python
  27. from typing_extensions import Annotated
  28. from pydantic import AfterValidator, BaseModel, ValidationError
  29. MyInt = Annotated[int, AfterValidator(lambda v: v + 1)]
  30. class Model(BaseModel):
  31. a: MyInt
  32. print(Model(a=1).a)
  33. #> 2
  34. try:
  35. Model(a='a')
  36. except ValidationError as e:
  37. print(e.json(indent=2))
  38. '''
  39. [
  40. {
  41. "type": "int_parsing",
  42. "loc": [
  43. "a"
  44. ],
  45. "msg": "Input should be a valid integer, unable to parse string as an integer",
  46. "input": "a",
  47. "url": "https://errors.pydantic.dev/2/v/int_parsing"
  48. }
  49. ]
  50. '''
  51. ```
  52. """
  53. func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
  54. def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
  55. schema = handler(source_type)
  56. info_arg = _inspect_validator(self.func, 'after')
  57. if info_arg:
  58. func = cast(core_schema.WithInfoValidatorFunction, self.func)
  59. return core_schema.with_info_after_validator_function(func, schema=schema, field_name=handler.field_name)
  60. else:
  61. func = cast(core_schema.NoInfoValidatorFunction, self.func)
  62. return core_schema.no_info_after_validator_function(func, schema=schema)
  63. @classmethod
  64. def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
  65. return cls(func=decorator.func)
  66. @dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
  67. class BeforeValidator:
  68. """Usage docs: https://docs.pydantic.dev/2.10/concepts/validators/#field-validators
  69. A metadata class that indicates that a validation should be applied **before** the inner validation logic.
  70. Attributes:
  71. func: The validator function.
  72. json_schema_input_type: The input type of the function. This is only used to generate the appropriate
  73. JSON Schema (in validation mode).
  74. Example:
  75. ```python
  76. from typing_extensions import Annotated
  77. from pydantic import BaseModel, BeforeValidator
  78. MyInt = Annotated[int, BeforeValidator(lambda v: v + 1)]
  79. class Model(BaseModel):
  80. a: MyInt
  81. print(Model(a=1).a)
  82. #> 2
  83. try:
  84. Model(a='a')
  85. except TypeError as e:
  86. print(e)
  87. #> can only concatenate str (not "int") to str
  88. ```
  89. """
  90. func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
  91. json_schema_input_type: Any = PydanticUndefined
  92. def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
  93. schema = handler(source_type)
  94. input_schema = (
  95. None
  96. if self.json_schema_input_type is PydanticUndefined
  97. else handler.generate_schema(self.json_schema_input_type)
  98. )
  99. info_arg = _inspect_validator(self.func, 'before')
  100. if info_arg:
  101. func = cast(core_schema.WithInfoValidatorFunction, self.func)
  102. return core_schema.with_info_before_validator_function(
  103. func,
  104. schema=schema,
  105. field_name=handler.field_name,
  106. json_schema_input_schema=input_schema,
  107. )
  108. else:
  109. func = cast(core_schema.NoInfoValidatorFunction, self.func)
  110. return core_schema.no_info_before_validator_function(
  111. func, schema=schema, json_schema_input_schema=input_schema
  112. )
  113. @classmethod
  114. def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
  115. return cls(
  116. func=decorator.func,
  117. json_schema_input_type=decorator.info.json_schema_input_type,
  118. )
  119. @dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
  120. class PlainValidator:
  121. """Usage docs: https://docs.pydantic.dev/2.10/concepts/validators/#field-validators
  122. A metadata class that indicates that a validation should be applied **instead** of the inner validation logic.
  123. !!! note
  124. Before v2.9, `PlainValidator` wasn't always compatible with JSON Schema generation for `mode='validation'`.
  125. You can now use the `json_schema_input_type` argument to specify the input type of the function
  126. to be used in the JSON schema when `mode='validation'` (the default). See the example below for more details.
  127. Attributes:
  128. func: The validator function.
  129. json_schema_input_type: The input type of the function. This is only used to generate the appropriate
  130. JSON Schema (in validation mode). If not provided, will default to `Any`.
  131. Example:
  132. ```python
  133. from typing import Union
  134. from typing_extensions import Annotated
  135. from pydantic import BaseModel, PlainValidator
  136. MyInt = Annotated[
  137. int,
  138. PlainValidator(
  139. lambda v: int(v) + 1, json_schema_input_type=Union[str, int] # (1)!
  140. ),
  141. ]
  142. class Model(BaseModel):
  143. a: MyInt
  144. print(Model(a='1').a)
  145. #> 2
  146. print(Model(a=1).a)
  147. #> 2
  148. ```
  149. 1. In this example, we've specified the `json_schema_input_type` as `Union[str, int]` which indicates to the JSON schema
  150. generator that in validation mode, the input type for the `a` field can be either a `str` or an `int`.
  151. """
  152. func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
  153. json_schema_input_type: Any = Any
  154. def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
  155. # Note that for some valid uses of PlainValidator, it is not possible to generate a core schema for the
  156. # source_type, so calling `handler(source_type)` will error, which prevents us from generating a proper
  157. # serialization schema. To work around this for use cases that will not involve serialization, we simply
  158. # catch any PydanticSchemaGenerationError that may be raised while attempting to build the serialization schema
  159. # and abort any attempts to handle special serialization.
  160. from pydantic import PydanticSchemaGenerationError
  161. try:
  162. schema = handler(source_type)
  163. # TODO if `schema['serialization']` is one of `'include-exclude-dict/sequence',
  164. # schema validation will fail. That's why we use 'type ignore' comments below.
  165. serialization = schema.get(
  166. 'serialization',
  167. core_schema.wrap_serializer_function_ser_schema(
  168. function=lambda v, h: h(v),
  169. schema=schema,
  170. return_schema=handler.generate_schema(source_type),
  171. ),
  172. )
  173. except PydanticSchemaGenerationError:
  174. serialization = None
  175. input_schema = handler.generate_schema(self.json_schema_input_type)
  176. info_arg = _inspect_validator(self.func, 'plain')
  177. if info_arg:
  178. func = cast(core_schema.WithInfoValidatorFunction, self.func)
  179. return core_schema.with_info_plain_validator_function(
  180. func,
  181. field_name=handler.field_name,
  182. serialization=serialization, # pyright: ignore[reportArgumentType]
  183. json_schema_input_schema=input_schema,
  184. )
  185. else:
  186. func = cast(core_schema.NoInfoValidatorFunction, self.func)
  187. return core_schema.no_info_plain_validator_function(
  188. func,
  189. serialization=serialization, # pyright: ignore[reportArgumentType]
  190. json_schema_input_schema=input_schema,
  191. )
  192. @classmethod
  193. def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
  194. return cls(
  195. func=decorator.func,
  196. json_schema_input_type=decorator.info.json_schema_input_type,
  197. )
  198. @dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
  199. class WrapValidator:
  200. """Usage docs: https://docs.pydantic.dev/2.10/concepts/validators/#field-validators
  201. A metadata class that indicates that a validation should be applied **around** the inner validation logic.
  202. Attributes:
  203. func: The validator function.
  204. json_schema_input_type: The input type of the function. This is only used to generate the appropriate
  205. JSON Schema (in validation mode).
  206. ```python
  207. from datetime import datetime
  208. from typing_extensions import Annotated
  209. from pydantic import BaseModel, ValidationError, WrapValidator
  210. def validate_timestamp(v, handler):
  211. if v == 'now':
  212. # we don't want to bother with further validation, just return the new value
  213. return datetime.now()
  214. try:
  215. return handler(v)
  216. except ValidationError:
  217. # validation failed, in this case we want to return a default value
  218. return datetime(2000, 1, 1)
  219. MyTimestamp = Annotated[datetime, WrapValidator(validate_timestamp)]
  220. class Model(BaseModel):
  221. a: MyTimestamp
  222. print(Model(a='now').a)
  223. #> 2032-01-02 03:04:05.000006
  224. print(Model(a='invalid').a)
  225. #> 2000-01-01 00:00:00
  226. ```
  227. """
  228. func: core_schema.NoInfoWrapValidatorFunction | core_schema.WithInfoWrapValidatorFunction
  229. json_schema_input_type: Any = PydanticUndefined
  230. def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
  231. schema = handler(source_type)
  232. input_schema = (
  233. None
  234. if self.json_schema_input_type is PydanticUndefined
  235. else handler.generate_schema(self.json_schema_input_type)
  236. )
  237. info_arg = _inspect_validator(self.func, 'wrap')
  238. if info_arg:
  239. func = cast(core_schema.WithInfoWrapValidatorFunction, self.func)
  240. return core_schema.with_info_wrap_validator_function(
  241. func,
  242. schema=schema,
  243. field_name=handler.field_name,
  244. json_schema_input_schema=input_schema,
  245. )
  246. else:
  247. func = cast(core_schema.NoInfoWrapValidatorFunction, self.func)
  248. return core_schema.no_info_wrap_validator_function(
  249. func,
  250. schema=schema,
  251. json_schema_input_schema=input_schema,
  252. )
  253. @classmethod
  254. def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
  255. return cls(
  256. func=decorator.func,
  257. json_schema_input_type=decorator.info.json_schema_input_type,
  258. )
  259. if TYPE_CHECKING:
  260. class _OnlyValueValidatorClsMethod(Protocol):
  261. def __call__(self, cls: Any, value: Any, /) -> Any: ...
  262. class _V2ValidatorClsMethod(Protocol):
  263. def __call__(self, cls: Any, value: Any, info: _core_schema.ValidationInfo, /) -> Any: ...
  264. class _OnlyValueWrapValidatorClsMethod(Protocol):
  265. def __call__(self, cls: Any, value: Any, handler: _core_schema.ValidatorFunctionWrapHandler, /) -> Any: ...
  266. class _V2WrapValidatorClsMethod(Protocol):
  267. def __call__(
  268. self,
  269. cls: Any,
  270. value: Any,
  271. handler: _core_schema.ValidatorFunctionWrapHandler,
  272. info: _core_schema.ValidationInfo,
  273. /,
  274. ) -> Any: ...
  275. _V2Validator = Union[
  276. _V2ValidatorClsMethod,
  277. _core_schema.WithInfoValidatorFunction,
  278. _OnlyValueValidatorClsMethod,
  279. _core_schema.NoInfoValidatorFunction,
  280. ]
  281. _V2WrapValidator = Union[
  282. _V2WrapValidatorClsMethod,
  283. _core_schema.WithInfoWrapValidatorFunction,
  284. _OnlyValueWrapValidatorClsMethod,
  285. _core_schema.NoInfoWrapValidatorFunction,
  286. ]
  287. _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
  288. _V2BeforeAfterOrPlainValidatorType = TypeVar(
  289. '_V2BeforeAfterOrPlainValidatorType',
  290. bound=Union[_V2Validator, _PartialClsOrStaticMethod],
  291. )
  292. _V2WrapValidatorType = TypeVar('_V2WrapValidatorType', bound=Union[_V2WrapValidator, _PartialClsOrStaticMethod])
  293. FieldValidatorModes: TypeAlias = Literal['before', 'after', 'wrap', 'plain']
  294. @overload
  295. def field_validator(
  296. field: str,
  297. /,
  298. *fields: str,
  299. mode: Literal['wrap'],
  300. check_fields: bool | None = ...,
  301. json_schema_input_type: Any = ...,
  302. ) -> Callable[[_V2WrapValidatorType], _V2WrapValidatorType]: ...
  303. @overload
  304. def field_validator(
  305. field: str,
  306. /,
  307. *fields: str,
  308. mode: Literal['before', 'plain'],
  309. check_fields: bool | None = ...,
  310. json_schema_input_type: Any = ...,
  311. ) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ...
  312. @overload
  313. def field_validator(
  314. field: str,
  315. /,
  316. *fields: str,
  317. mode: Literal['after'] = ...,
  318. check_fields: bool | None = ...,
  319. ) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ...
  320. def field_validator(
  321. field: str,
  322. /,
  323. *fields: str,
  324. mode: FieldValidatorModes = 'after',
  325. check_fields: bool | None = None,
  326. json_schema_input_type: Any = PydanticUndefined,
  327. ) -> Callable[[Any], Any]:
  328. """Usage docs: https://docs.pydantic.dev/2.10/concepts/validators/#field-validators
  329. Decorate methods on the class indicating that they should be used to validate fields.
  330. Example usage:
  331. ```python
  332. from typing import Any
  333. from pydantic import (
  334. BaseModel,
  335. ValidationError,
  336. field_validator,
  337. )
  338. class Model(BaseModel):
  339. a: str
  340. @field_validator('a')
  341. @classmethod
  342. def ensure_foobar(cls, v: Any):
  343. if 'foobar' not in v:
  344. raise ValueError('"foobar" not found in a')
  345. return v
  346. print(repr(Model(a='this is foobar good')))
  347. #> Model(a='this is foobar good')
  348. try:
  349. Model(a='snap')
  350. except ValidationError as exc_info:
  351. print(exc_info)
  352. '''
  353. 1 validation error for Model
  354. a
  355. Value error, "foobar" not found in a [type=value_error, input_value='snap', input_type=str]
  356. '''
  357. ```
  358. For more in depth examples, see [Field Validators](../concepts/validators.md#field-validators).
  359. Args:
  360. field: The first field the `field_validator` should be called on; this is separate
  361. from `fields` to ensure an error is raised if you don't pass at least one.
  362. *fields: Additional field(s) the `field_validator` should be called on.
  363. mode: Specifies whether to validate the fields before or after validation.
  364. check_fields: Whether to check that the fields actually exist on the model.
  365. json_schema_input_type: The input type of the function. This is only used to generate
  366. the appropriate JSON Schema (in validation mode) and can only specified
  367. when `mode` is either `'before'`, `'plain'` or `'wrap'`.
  368. Returns:
  369. A decorator that can be used to decorate a function to be used as a field_validator.
  370. Raises:
  371. PydanticUserError:
  372. - If `@field_validator` is used bare (with no fields).
  373. - If the args passed to `@field_validator` as fields are not strings.
  374. - If `@field_validator` applied to instance methods.
  375. """
  376. if isinstance(field, FunctionType):
  377. raise PydanticUserError(
  378. '`@field_validator` should be used with fields and keyword arguments, not bare. '
  379. "E.g. usage should be `@validator('<field_name>', ...)`",
  380. code='validator-no-fields',
  381. )
  382. if mode not in ('before', 'plain', 'wrap') and json_schema_input_type is not PydanticUndefined:
  383. raise PydanticUserError(
  384. f"`json_schema_input_type` can't be used when mode is set to {mode!r}",
  385. code='validator-input-type',
  386. )
  387. if json_schema_input_type is PydanticUndefined and mode == 'plain':
  388. json_schema_input_type = Any
  389. fields = field, *fields
  390. if not all(isinstance(field, str) for field in fields):
  391. raise PydanticUserError(
  392. '`@field_validator` fields should be passed as separate string args. '
  393. "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)`",
  394. code='validator-invalid-fields',
  395. )
  396. def dec(
  397. f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any],
  398. ) -> _decorators.PydanticDescriptorProxy[Any]:
  399. if _decorators.is_instance_method_from_sig(f):
  400. raise PydanticUserError(
  401. '`@field_validator` cannot be applied to instance methods', code='validator-instance-method'
  402. )
  403. # auto apply the @classmethod decorator
  404. f = _decorators.ensure_classmethod_based_on_signature(f)
  405. dec_info = _decorators.FieldValidatorDecoratorInfo(
  406. fields=fields, mode=mode, check_fields=check_fields, json_schema_input_type=json_schema_input_type
  407. )
  408. return _decorators.PydanticDescriptorProxy(f, dec_info)
  409. return dec
  410. _ModelType = TypeVar('_ModelType')
  411. _ModelTypeCo = TypeVar('_ModelTypeCo', covariant=True)
  412. class ModelWrapValidatorHandler(_core_schema.ValidatorFunctionWrapHandler, Protocol[_ModelTypeCo]):
  413. """`@model_validator` decorated function handler argument type. This is used when `mode='wrap'`."""
  414. def __call__( # noqa: D102
  415. self,
  416. value: Any,
  417. outer_location: str | int | None = None,
  418. /,
  419. ) -> _ModelTypeCo: # pragma: no cover
  420. ...
  421. class ModelWrapValidatorWithoutInfo(Protocol[_ModelType]):
  422. """A `@model_validator` decorated function signature.
  423. This is used when `mode='wrap'` and the function does not have info argument.
  424. """
  425. def __call__( # noqa: D102
  426. self,
  427. cls: type[_ModelType],
  428. # this can be a dict, a model instance
  429. # or anything else that gets passed to validate_python
  430. # thus validators _must_ handle all cases
  431. value: Any,
  432. handler: ModelWrapValidatorHandler[_ModelType],
  433. /,
  434. ) -> _ModelType: ...
  435. class ModelWrapValidator(Protocol[_ModelType]):
  436. """A `@model_validator` decorated function signature. This is used when `mode='wrap'`."""
  437. def __call__( # noqa: D102
  438. self,
  439. cls: type[_ModelType],
  440. # this can be a dict, a model instance
  441. # or anything else that gets passed to validate_python
  442. # thus validators _must_ handle all cases
  443. value: Any,
  444. handler: ModelWrapValidatorHandler[_ModelType],
  445. info: _core_schema.ValidationInfo,
  446. /,
  447. ) -> _ModelType: ...
  448. class FreeModelBeforeValidatorWithoutInfo(Protocol):
  449. """A `@model_validator` decorated function signature.
  450. This is used when `mode='before'` and the function does not have info argument.
  451. """
  452. def __call__( # noqa: D102
  453. self,
  454. # this can be a dict, a model instance
  455. # or anything else that gets passed to validate_python
  456. # thus validators _must_ handle all cases
  457. value: Any,
  458. /,
  459. ) -> Any: ...
  460. class ModelBeforeValidatorWithoutInfo(Protocol):
  461. """A `@model_validator` decorated function signature.
  462. This is used when `mode='before'` and the function does not have info argument.
  463. """
  464. def __call__( # noqa: D102
  465. self,
  466. cls: Any,
  467. # this can be a dict, a model instance
  468. # or anything else that gets passed to validate_python
  469. # thus validators _must_ handle all cases
  470. value: Any,
  471. /,
  472. ) -> Any: ...
  473. class FreeModelBeforeValidator(Protocol):
  474. """A `@model_validator` decorated function signature. This is used when `mode='before'`."""
  475. def __call__( # noqa: D102
  476. self,
  477. # this can be a dict, a model instance
  478. # or anything else that gets passed to validate_python
  479. # thus validators _must_ handle all cases
  480. value: Any,
  481. info: _core_schema.ValidationInfo,
  482. /,
  483. ) -> Any: ...
  484. class ModelBeforeValidator(Protocol):
  485. """A `@model_validator` decorated function signature. This is used when `mode='before'`."""
  486. def __call__( # noqa: D102
  487. self,
  488. cls: Any,
  489. # this can be a dict, a model instance
  490. # or anything else that gets passed to validate_python
  491. # thus validators _must_ handle all cases
  492. value: Any,
  493. info: _core_schema.ValidationInfo,
  494. /,
  495. ) -> Any: ...
  496. ModelAfterValidatorWithoutInfo = Callable[[_ModelType], _ModelType]
  497. """A `@model_validator` decorated function signature. This is used when `mode='after'` and the function does not
  498. have info argument.
  499. """
  500. ModelAfterValidator = Callable[[_ModelType, _core_schema.ValidationInfo], _ModelType]
  501. """A `@model_validator` decorated function signature. This is used when `mode='after'`."""
  502. _AnyModelWrapValidator = Union[ModelWrapValidator[_ModelType], ModelWrapValidatorWithoutInfo[_ModelType]]
  503. _AnyModelBeforeValidator = Union[
  504. FreeModelBeforeValidator, ModelBeforeValidator, FreeModelBeforeValidatorWithoutInfo, ModelBeforeValidatorWithoutInfo
  505. ]
  506. _AnyModelAfterValidator = Union[ModelAfterValidator[_ModelType], ModelAfterValidatorWithoutInfo[_ModelType]]
  507. @overload
  508. def model_validator(
  509. *,
  510. mode: Literal['wrap'],
  511. ) -> Callable[
  512. [_AnyModelWrapValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
  513. ]: ...
  514. @overload
  515. def model_validator(
  516. *,
  517. mode: Literal['before'],
  518. ) -> Callable[
  519. [_AnyModelBeforeValidator], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
  520. ]: ...
  521. @overload
  522. def model_validator(
  523. *,
  524. mode: Literal['after'],
  525. ) -> Callable[
  526. [_AnyModelAfterValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
  527. ]: ...
  528. def model_validator(
  529. *,
  530. mode: Literal['wrap', 'before', 'after'],
  531. ) -> Any:
  532. """Usage docs: https://docs.pydantic.dev/2.10/concepts/validators/#model-validators
  533. Decorate model methods for validation purposes.
  534. Example usage:
  535. ```python
  536. from typing_extensions import Self
  537. from pydantic import BaseModel, ValidationError, model_validator
  538. class Square(BaseModel):
  539. width: float
  540. height: float
  541. @model_validator(mode='after')
  542. def verify_square(self) -> Self:
  543. if self.width != self.height:
  544. raise ValueError('width and height do not match')
  545. return self
  546. s = Square(width=1, height=1)
  547. print(repr(s))
  548. #> Square(width=1.0, height=1.0)
  549. try:
  550. Square(width=1, height=2)
  551. except ValidationError as e:
  552. print(e)
  553. '''
  554. 1 validation error for Square
  555. Value error, width and height do not match [type=value_error, input_value={'width': 1, 'height': 2}, input_type=dict]
  556. '''
  557. ```
  558. For more in depth examples, see [Model Validators](../concepts/validators.md#model-validators).
  559. Args:
  560. mode: A required string literal that specifies the validation mode.
  561. It can be one of the following: 'wrap', 'before', or 'after'.
  562. Returns:
  563. A decorator that can be used to decorate a function to be used as a model validator.
  564. """
  565. def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
  566. # auto apply the @classmethod decorator
  567. f = _decorators.ensure_classmethod_based_on_signature(f)
  568. dec_info = _decorators.ModelValidatorDecoratorInfo(mode=mode)
  569. return _decorators.PydanticDescriptorProxy(f, dec_info)
  570. return dec
  571. AnyType = TypeVar('AnyType')
  572. if TYPE_CHECKING:
  573. # If we add configurable attributes to IsInstance, we'd probably need to stop hiding it from type checkers like this
  574. InstanceOf = Annotated[AnyType, ...] # `IsInstance[Sequence]` will be recognized by type checkers as `Sequence`
  575. else:
  576. @dataclasses.dataclass(**_internal_dataclass.slots_true)
  577. class InstanceOf:
  578. '''Generic type for annotating a type that is an instance of a given class.
  579. Example:
  580. ```python
  581. from pydantic import BaseModel, InstanceOf
  582. class Foo:
  583. ...
  584. class Bar(BaseModel):
  585. foo: InstanceOf[Foo]
  586. Bar(foo=Foo())
  587. try:
  588. Bar(foo=42)
  589. except ValidationError as e:
  590. print(e)
  591. """
  592. [
  593. │ {
  594. │ │ 'type': 'is_instance_of',
  595. │ │ 'loc': ('foo',),
  596. │ │ 'msg': 'Input should be an instance of Foo',
  597. │ │ 'input': 42,
  598. │ │ 'ctx': {'class': 'Foo'},
  599. │ │ 'url': 'https://errors.pydantic.dev/0.38.0/v/is_instance_of'
  600. │ }
  601. ]
  602. """
  603. ```
  604. '''
  605. @classmethod
  606. def __class_getitem__(cls, item: AnyType) -> AnyType:
  607. return Annotated[item, cls()]
  608. @classmethod
  609. def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
  610. from pydantic import PydanticSchemaGenerationError
  611. # use the generic _origin_ as the second argument to isinstance when appropriate
  612. instance_of_schema = core_schema.is_instance_schema(_generics.get_origin(source) or source)
  613. try:
  614. # Try to generate the "standard" schema, which will be used when loading from JSON
  615. original_schema = handler(source)
  616. except PydanticSchemaGenerationError:
  617. # If that fails, just produce a schema that can validate from python
  618. return instance_of_schema
  619. else:
  620. # Use the "original" approach to serialization
  621. instance_of_schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
  622. function=lambda v, h: h(v), schema=original_schema
  623. )
  624. return core_schema.json_or_python_schema(python_schema=instance_of_schema, json_schema=original_schema)
  625. __hash__ = object.__hash__
  626. if TYPE_CHECKING:
  627. SkipValidation = Annotated[AnyType, ...] # SkipValidation[list[str]] will be treated by type checkers as list[str]
  628. else:
  629. @dataclasses.dataclass(**_internal_dataclass.slots_true)
  630. class SkipValidation:
  631. """If this is applied as an annotation (e.g., via `x: Annotated[int, SkipValidation]`), validation will be
  632. skipped. You can also use `SkipValidation[int]` as a shorthand for `Annotated[int, SkipValidation]`.
  633. This can be useful if you want to use a type annotation for documentation/IDE/type-checking purposes,
  634. and know that it is safe to skip validation for one or more of the fields.
  635. Because this converts the validation schema to `any_schema`, subsequent annotation-applied transformations
  636. may not have the expected effects. Therefore, when used, this annotation should generally be the final
  637. annotation applied to a type.
  638. """
  639. def __class_getitem__(cls, item: Any) -> Any:
  640. return Annotated[item, SkipValidation()]
  641. @classmethod
  642. def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
  643. original_schema = handler(source)
  644. metadata = {'pydantic_js_annotation_functions': [lambda _c, h: h(original_schema)]}
  645. return core_schema.any_schema(
  646. metadata=metadata,
  647. serialization=core_schema.wrap_serializer_function_ser_schema(
  648. function=lambda v, h: h(v), schema=original_schema
  649. ),
  650. )
  651. __hash__ = object.__hash__