functional_validators.py 30 KB

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