functional_validators.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  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 core_schema
  9. from pydantic_core import core_schema as _core_schema
  10. from typing_extensions import Annotated, Literal, TypeAlias
  11. from . import GetCoreSchemaHandler as _GetCoreSchemaHandler
  12. from ._internal import _core_metadata, _decorators, _generics, _internal_dataclass
  13. from .annotated_handlers import GetCoreSchemaHandler
  14. from .errors import PydanticUserError
  15. if sys.version_info < (3, 11):
  16. from typing_extensions import Protocol
  17. else:
  18. from typing import Protocol
  19. _inspect_validator = _decorators.inspect_validator
  20. @dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
  21. class AfterValidator:
  22. '''Usage docs: https://docs.pydantic.dev/2.2/concepts/validators/#annotated-validators
  23. A metadata class that indicates that a validation should be applied **after** the inner validation logic.
  24. Attributes:
  25. func: The validator function.
  26. Example:
  27. ```py
  28. from typing import Annotated
  29. from pydantic import BaseModel, AfterValidator, ValidationError
  30. MyInt = Annotated[int, AfterValidator(lambda v: v + 1)]
  31. class Model(BaseModel):
  32. a: MyInt
  33. print(Model(a=1).a)
  34. # > 2
  35. try:
  36. Model(a='a')
  37. except ValidationError as e:
  38. print(e.json(indent=2))
  39. """
  40. [
  41. {
  42. "type": "int_parsing",
  43. "loc": [
  44. "a"
  45. ],
  46. "msg": "Input should be a valid integer, unable to parse string as an integer",
  47. "input": "a",
  48. "url": "https://errors.pydantic.dev/0.38.0/v/int_parsing"
  49. }
  50. ]
  51. """
  52. ```
  53. '''
  54. func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
  55. def __get_pydantic_core_schema__(self, source_type: Any, handler: _GetCoreSchemaHandler) -> core_schema.CoreSchema:
  56. schema = handler(source_type)
  57. info_arg = _inspect_validator(self.func, 'after')
  58. if info_arg:
  59. func = cast(core_schema.WithInfoValidatorFunction, self.func)
  60. return core_schema.with_info_after_validator_function(func, schema=schema, field_name=handler.field_name)
  61. else:
  62. func = cast(core_schema.NoInfoValidatorFunction, self.func)
  63. return core_schema.no_info_after_validator_function(func, schema=schema)
  64. @dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
  65. class BeforeValidator:
  66. """Usage docs: https://docs.pydantic.dev/2.4/concepts/validators/#annotated-validators
  67. A metadata class that indicates that a validation should be applied **before** the inner validation logic.
  68. Attributes:
  69. func: The validator function.
  70. Example:
  71. ```py
  72. from typing_extensions import Annotated
  73. from pydantic import BaseModel, BeforeValidator
  74. MyInt = Annotated[int, BeforeValidator(lambda v: v + 1)]
  75. class Model(BaseModel):
  76. a: MyInt
  77. print(Model(a=1).a)
  78. #> 2
  79. try:
  80. Model(a='a')
  81. except TypeError as e:
  82. print(e)
  83. #> can only concatenate str (not "int") to str
  84. ```
  85. """
  86. func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
  87. def __get_pydantic_core_schema__(self, source_type: Any, handler: _GetCoreSchemaHandler) -> core_schema.CoreSchema:
  88. schema = handler(source_type)
  89. info_arg = _inspect_validator(self.func, 'before')
  90. if info_arg:
  91. func = cast(core_schema.WithInfoValidatorFunction, self.func)
  92. return core_schema.with_info_before_validator_function(func, schema=schema, field_name=handler.field_name)
  93. else:
  94. func = cast(core_schema.NoInfoValidatorFunction, self.func)
  95. return core_schema.no_info_before_validator_function(func, schema=schema)
  96. @dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
  97. class PlainValidator:
  98. """Usage docs: https://docs.pydantic.dev/2.4/concepts/validators/#annotated-validators
  99. A metadata class that indicates that a validation should be applied **instead** of the inner validation logic.
  100. Attributes:
  101. func: The validator function.
  102. Example:
  103. ```py
  104. from typing_extensions import Annotated
  105. from pydantic import BaseModel, PlainValidator
  106. MyInt = Annotated[int, PlainValidator(lambda v: int(v) + 1)]
  107. class Model(BaseModel):
  108. a: MyInt
  109. print(Model(a='1').a)
  110. #> 2
  111. ```
  112. """
  113. func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
  114. def __get_pydantic_core_schema__(self, source_type: Any, handler: _GetCoreSchemaHandler) -> core_schema.CoreSchema:
  115. info_arg = _inspect_validator(self.func, 'plain')
  116. if info_arg:
  117. func = cast(core_schema.WithInfoValidatorFunction, self.func)
  118. return core_schema.with_info_plain_validator_function(func, field_name=handler.field_name)
  119. else:
  120. func = cast(core_schema.NoInfoValidatorFunction, self.func)
  121. return core_schema.no_info_plain_validator_function(func)
  122. @dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
  123. class WrapValidator:
  124. """Usage docs: https://docs.pydantic.dev/2.4/concepts/validators/#annotated-validators
  125. A metadata class that indicates that a validation should be applied **around** the inner validation logic.
  126. Attributes:
  127. func: The validator function.
  128. ```py
  129. from datetime import datetime
  130. from typing_extensions import Annotated
  131. from pydantic import BaseModel, ValidationError, WrapValidator
  132. def validate_timestamp(v, handler):
  133. if v == 'now':
  134. # we don't want to bother with further validation, just return the new value
  135. return datetime.now()
  136. try:
  137. return handler(v)
  138. except ValidationError:
  139. # validation failed, in this case we want to return a default value
  140. return datetime(2000, 1, 1)
  141. MyTimestamp = Annotated[datetime, WrapValidator(validate_timestamp)]
  142. class Model(BaseModel):
  143. a: MyTimestamp
  144. print(Model(a='now').a)
  145. #> 2032-01-02 03:04:05.000006
  146. print(Model(a='invalid').a)
  147. #> 2000-01-01 00:00:00
  148. ```
  149. """
  150. func: core_schema.NoInfoWrapValidatorFunction | core_schema.WithInfoWrapValidatorFunction
  151. def __get_pydantic_core_schema__(self, source_type: Any, handler: _GetCoreSchemaHandler) -> core_schema.CoreSchema:
  152. schema = handler(source_type)
  153. info_arg = _inspect_validator(self.func, 'wrap')
  154. if info_arg:
  155. func = cast(core_schema.WithInfoWrapValidatorFunction, self.func)
  156. return core_schema.with_info_wrap_validator_function(func, schema=schema, field_name=handler.field_name)
  157. else:
  158. func = cast(core_schema.NoInfoWrapValidatorFunction, self.func)
  159. return core_schema.no_info_wrap_validator_function(func, schema=schema)
  160. if TYPE_CHECKING:
  161. class _OnlyValueValidatorClsMethod(Protocol):
  162. def __call__(self, __cls: Any, __value: Any) -> Any:
  163. ...
  164. class _V2ValidatorClsMethod(Protocol):
  165. def __call__(self, __cls: Any, __input_value: Any, __info: _core_schema.ValidationInfo) -> Any:
  166. ...
  167. class _V2WrapValidatorClsMethod(Protocol):
  168. def __call__(
  169. self,
  170. __cls: Any,
  171. __input_value: Any,
  172. __validator: _core_schema.ValidatorFunctionWrapHandler,
  173. __info: _core_schema.ValidationInfo,
  174. ) -> Any:
  175. ...
  176. _V2Validator = Union[
  177. _V2ValidatorClsMethod,
  178. _core_schema.WithInfoValidatorFunction,
  179. _OnlyValueValidatorClsMethod,
  180. _core_schema.NoInfoValidatorFunction,
  181. ]
  182. _V2WrapValidator = Union[
  183. _V2WrapValidatorClsMethod,
  184. _core_schema.WithInfoWrapValidatorFunction,
  185. ]
  186. _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
  187. _V2BeforeAfterOrPlainValidatorType = TypeVar(
  188. '_V2BeforeAfterOrPlainValidatorType',
  189. _V2Validator,
  190. _PartialClsOrStaticMethod,
  191. )
  192. _V2WrapValidatorType = TypeVar('_V2WrapValidatorType', _V2WrapValidator, _PartialClsOrStaticMethod)
  193. @overload
  194. def field_validator(
  195. __field: str,
  196. *fields: str,
  197. mode: Literal['before', 'after', 'plain'] = ...,
  198. check_fields: bool | None = ...,
  199. ) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]:
  200. ...
  201. @overload
  202. def field_validator(
  203. __field: str,
  204. *fields: str,
  205. mode: Literal['wrap'],
  206. check_fields: bool | None = ...,
  207. ) -> Callable[[_V2WrapValidatorType], _V2WrapValidatorType]:
  208. ...
  209. FieldValidatorModes: TypeAlias = Literal['before', 'after', 'wrap', 'plain']
  210. def field_validator(
  211. __field: str,
  212. *fields: str,
  213. mode: FieldValidatorModes = 'after',
  214. check_fields: bool | None = None,
  215. ) -> Callable[[Any], Any]:
  216. """Usage docs: https://docs.pydantic.dev/2.4/concepts/validators/#field-validators
  217. Decorate methods on the class indicating that they should be used to validate fields.
  218. Args:
  219. __field: The first field the `field_validator` should be called on; this is separate
  220. from `fields` to ensure an error is raised if you don't pass at least one.
  221. *fields: Additional field(s) the `field_validator` should be called on.
  222. mode: Specifies whether to validate the fields before or after validation.
  223. check_fields: Whether to check that the fields actually exist on the model.
  224. Returns:
  225. A decorator that can be used to decorate a function to be used as a field_validator.
  226. Raises:
  227. PydanticUserError:
  228. - If `@field_validator` is used bare (with no fields).
  229. - If the args passed to `@field_validator` as fields are not strings.
  230. - If `@field_validator` applied to instance methods.
  231. """
  232. if isinstance(__field, FunctionType):
  233. raise PydanticUserError(
  234. '`@field_validator` should be used with fields and keyword arguments, not bare. '
  235. "E.g. usage should be `@validator('<field_name>', ...)`",
  236. code='validator-no-fields',
  237. )
  238. fields = __field, *fields
  239. if not all(isinstance(field, str) for field in fields):
  240. raise PydanticUserError(
  241. '`@field_validator` fields should be passed as separate string args. '
  242. "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)`",
  243. code='validator-invalid-fields',
  244. )
  245. def dec(
  246. f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any]
  247. ) -> _decorators.PydanticDescriptorProxy[Any]:
  248. if _decorators.is_instance_method_from_sig(f):
  249. raise PydanticUserError(
  250. '`@field_validator` cannot be applied to instance methods', code='validator-instance-method'
  251. )
  252. # auto apply the @classmethod decorator
  253. f = _decorators.ensure_classmethod_based_on_signature(f)
  254. dec_info = _decorators.FieldValidatorDecoratorInfo(fields=fields, mode=mode, check_fields=check_fields)
  255. return _decorators.PydanticDescriptorProxy(f, dec_info)
  256. return dec
  257. _ModelType = TypeVar('_ModelType')
  258. _ModelTypeCo = TypeVar('_ModelTypeCo', covariant=True)
  259. class ModelWrapValidatorHandler(_core_schema.ValidatorFunctionWrapHandler, Protocol[_ModelTypeCo]):
  260. """@model_validator decorated function handler argument type. This is used when `mode='wrap'`."""
  261. def __call__( # noqa: D102
  262. self, input_value: Any, outer_location: str | int | None = None
  263. ) -> _ModelTypeCo: # pragma: no cover
  264. ...
  265. class ModelWrapValidatorWithoutInfo(Protocol[_ModelType]):
  266. """A @model_validator decorated function signature.
  267. This is used when `mode='wrap'` and the function does not have info argument.
  268. """
  269. def __call__( # noqa: D102
  270. self,
  271. cls: type[_ModelType],
  272. # this can be a dict, a model instance
  273. # or anything else that gets passed to validate_python
  274. # thus validators _must_ handle all cases
  275. __value: Any,
  276. __handler: ModelWrapValidatorHandler[_ModelType],
  277. ) -> _ModelType:
  278. ...
  279. class ModelWrapValidator(Protocol[_ModelType]):
  280. """A @model_validator decorated function signature. This is used when `mode='wrap'`."""
  281. def __call__( # noqa: D102
  282. self,
  283. cls: type[_ModelType],
  284. # this can be a dict, a model instance
  285. # or anything else that gets passed to validate_python
  286. # thus validators _must_ handle all cases
  287. __value: Any,
  288. __handler: ModelWrapValidatorHandler[_ModelType],
  289. __info: _core_schema.ValidationInfo,
  290. ) -> _ModelType:
  291. ...
  292. class ModelBeforeValidatorWithoutInfo(Protocol):
  293. """A @model_validator decorated function signature.
  294. This is used when `mode='before'` and the function does not have info argument.
  295. """
  296. def __call__( # noqa: D102
  297. self,
  298. cls: Any,
  299. # this can be a dict, a model instance
  300. # or anything else that gets passed to validate_python
  301. # thus validators _must_ handle all cases
  302. __value: Any,
  303. ) -> Any:
  304. ...
  305. class ModelBeforeValidator(Protocol):
  306. """A `@model_validator` decorated function signature. This is used when `mode='before'`."""
  307. def __call__( # noqa: D102
  308. self,
  309. cls: Any,
  310. # this can be a dict, a model instance
  311. # or anything else that gets passed to validate_python
  312. # thus validators _must_ handle all cases
  313. __value: Any,
  314. __info: _core_schema.ValidationInfo,
  315. ) -> Any:
  316. ...
  317. ModelAfterValidatorWithoutInfo = Callable[[_ModelType], _ModelType]
  318. """A `@model_validator` decorated function signature. This is used when `mode='after'` and the function does not
  319. have info argument.
  320. """
  321. ModelAfterValidator = Callable[[_ModelType, _core_schema.ValidationInfo], _ModelType]
  322. """A `@model_validator` decorated function signature. This is used when `mode='after'`."""
  323. _AnyModelWrapValidator = Union[ModelWrapValidator[_ModelType], ModelWrapValidatorWithoutInfo[_ModelType]]
  324. _AnyModeBeforeValidator = Union[ModelBeforeValidator, ModelBeforeValidatorWithoutInfo]
  325. _AnyModelAfterValidator = Union[ModelAfterValidator[_ModelType], ModelAfterValidatorWithoutInfo[_ModelType]]
  326. @overload
  327. def model_validator(
  328. *,
  329. mode: Literal['wrap'],
  330. ) -> Callable[
  331. [_AnyModelWrapValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
  332. ]:
  333. ...
  334. @overload
  335. def model_validator(
  336. *,
  337. mode: Literal['before'],
  338. ) -> Callable[[_AnyModeBeforeValidator], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]]:
  339. ...
  340. @overload
  341. def model_validator(
  342. *,
  343. mode: Literal['after'],
  344. ) -> Callable[
  345. [_AnyModelAfterValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
  346. ]:
  347. ...
  348. def model_validator(
  349. *,
  350. mode: Literal['wrap', 'before', 'after'],
  351. ) -> Any:
  352. """Decorate model methods for validation purposes.
  353. Args:
  354. mode: A required string literal that specifies the validation mode.
  355. It can be one of the following: 'wrap', 'before', or 'after'.
  356. Returns:
  357. A decorator that can be used to decorate a function to be used as a model validator.
  358. """
  359. def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
  360. # auto apply the @classmethod decorator
  361. f = _decorators.ensure_classmethod_based_on_signature(f)
  362. dec_info = _decorators.ModelValidatorDecoratorInfo(mode=mode)
  363. return _decorators.PydanticDescriptorProxy(f, dec_info)
  364. return dec
  365. AnyType = TypeVar('AnyType')
  366. if TYPE_CHECKING:
  367. # If we add configurable attributes to IsInstance, we'd probably need to stop hiding it from type checkers like this
  368. InstanceOf = Annotated[AnyType, ...] # `IsInstance[Sequence]` will be recognized by type checkers as `Sequence`
  369. else:
  370. @dataclasses.dataclass(**_internal_dataclass.slots_true)
  371. class InstanceOf:
  372. '''Generic type for annotating a type that is an instance of a given class.
  373. Example:
  374. ```py
  375. from pydantic import BaseModel, InstanceOf
  376. class Foo:
  377. ...
  378. class Bar(BaseModel):
  379. foo: InstanceOf[Foo]
  380. Bar(foo=Foo())
  381. try:
  382. Bar(foo=42)
  383. except ValidationError as e:
  384. print(e)
  385. """
  386. [
  387. │ {
  388. │ │ 'type': 'is_instance_of',
  389. │ │ 'loc': ('foo',),
  390. │ │ 'msg': 'Input should be an instance of Foo',
  391. │ │ 'input': 42,
  392. │ │ 'ctx': {'class': 'Foo'},
  393. │ │ 'url': 'https://errors.pydantic.dev/0.38.0/v/is_instance_of'
  394. │ }
  395. ]
  396. """
  397. ```
  398. '''
  399. @classmethod
  400. def __class_getitem__(cls, item: AnyType) -> AnyType:
  401. return Annotated[item, cls()]
  402. @classmethod
  403. def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
  404. from pydantic import PydanticSchemaGenerationError
  405. # use the generic _origin_ as the second argument to isinstance when appropriate
  406. instance_of_schema = core_schema.is_instance_schema(_generics.get_origin(source) or source)
  407. try:
  408. # Try to generate the "standard" schema, which will be used when loading from JSON
  409. original_schema = handler(source)
  410. except PydanticSchemaGenerationError:
  411. # If that fails, just produce a schema that can validate from python
  412. return instance_of_schema
  413. else:
  414. # Use the "original" approach to serialization
  415. instance_of_schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
  416. function=lambda v, h: h(v), schema=original_schema
  417. )
  418. return core_schema.json_or_python_schema(python_schema=instance_of_schema, json_schema=original_schema)
  419. __hash__ = object.__hash__
  420. if TYPE_CHECKING:
  421. SkipValidation = Annotated[AnyType, ...] # SkipValidation[list[str]] will be treated by type checkers as list[str]
  422. else:
  423. @dataclasses.dataclass(**_internal_dataclass.slots_true)
  424. class SkipValidation:
  425. """If this is applied as an annotation (e.g., via `x: Annotated[int, SkipValidation]`), validation will be
  426. skipped. You can also use `SkipValidation[int]` as a shorthand for `Annotated[int, SkipValidation]`.
  427. This can be useful if you want to use a type annotation for documentation/IDE/type-checking purposes,
  428. and know that it is safe to skip validation for one or more of the fields.
  429. Because this converts the validation schema to `any_schema`, subsequent annotation-applied transformations
  430. may not have the expected effects. Therefore, when used, this annotation should generally be the final
  431. annotation applied to a type.
  432. """
  433. def __class_getitem__(cls, item: Any) -> Any:
  434. return Annotated[item, SkipValidation()]
  435. @classmethod
  436. def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
  437. original_schema = handler(source)
  438. metadata = _core_metadata.build_metadata_dict(js_annotation_functions=[lambda _c, h: h(original_schema)])
  439. return core_schema.any_schema(
  440. metadata=metadata,
  441. serialization=core_schema.wrap_serializer_function_ser_schema(
  442. function=lambda v, h: h(v), schema=original_schema
  443. ),
  444. )
  445. __hash__ = object.__hash__