functional_validators.py 31 KB

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