class_validators.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. """Old `@validator` and `@root_validator` function validators from V1."""
  2. from __future__ import annotations as _annotations
  3. from functools import partial, partialmethod
  4. from types import FunctionType
  5. from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union, overload
  6. from warnings import warn
  7. from typing_extensions import Literal, Protocol, TypeAlias
  8. from .._internal import _decorators, _decorators_v1
  9. from ..errors import PydanticUserError
  10. from ..warnings import PydanticDeprecatedSince20
  11. _ALLOW_REUSE_WARNING_MESSAGE = '`allow_reuse` is deprecated and will be ignored; it should no longer be necessary'
  12. if TYPE_CHECKING:
  13. class _OnlyValueValidatorClsMethod(Protocol):
  14. def __call__(self, __cls: Any, __value: Any) -> Any:
  15. ...
  16. class _V1ValidatorWithValuesClsMethod(Protocol):
  17. def __call__(self, __cls: Any, __value: Any, values: dict[str, Any]) -> Any:
  18. ...
  19. class _V1ValidatorWithValuesKwOnlyClsMethod(Protocol):
  20. def __call__(self, __cls: Any, __value: Any, *, values: dict[str, Any]) -> Any:
  21. ...
  22. class _V1ValidatorWithKwargsClsMethod(Protocol):
  23. def __call__(self, __cls: Any, **kwargs: Any) -> Any:
  24. ...
  25. class _V1ValidatorWithValuesAndKwargsClsMethod(Protocol):
  26. def __call__(self, __cls: Any, values: dict[str, Any], **kwargs: Any) -> Any:
  27. ...
  28. class _V1RootValidatorClsMethod(Protocol):
  29. def __call__(
  30. self, __cls: Any, __values: _decorators_v1.RootValidatorValues
  31. ) -> _decorators_v1.RootValidatorValues:
  32. ...
  33. V1Validator = Union[
  34. _OnlyValueValidatorClsMethod,
  35. _V1ValidatorWithValuesClsMethod,
  36. _V1ValidatorWithValuesKwOnlyClsMethod,
  37. _V1ValidatorWithKwargsClsMethod,
  38. _V1ValidatorWithValuesAndKwargsClsMethod,
  39. _decorators_v1.V1ValidatorWithValues,
  40. _decorators_v1.V1ValidatorWithValuesKwOnly,
  41. _decorators_v1.V1ValidatorWithKwargs,
  42. _decorators_v1.V1ValidatorWithValuesAndKwargs,
  43. ]
  44. V1RootValidator = Union[
  45. _V1RootValidatorClsMethod,
  46. _decorators_v1.V1RootValidatorFunction,
  47. ]
  48. _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
  49. # Allow both a V1 (assumed pre=False) or V2 (assumed mode='after') validator
  50. # We lie to type checkers and say we return the same thing we get
  51. # but in reality we return a proxy object that _mostly_ behaves like the wrapped thing
  52. _V1ValidatorType = TypeVar('_V1ValidatorType', V1Validator, _PartialClsOrStaticMethod)
  53. _V1RootValidatorFunctionType = TypeVar(
  54. '_V1RootValidatorFunctionType',
  55. _decorators_v1.V1RootValidatorFunction,
  56. _V1RootValidatorClsMethod,
  57. _PartialClsOrStaticMethod,
  58. )
  59. else:
  60. # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
  61. # and https://youtrack.jetbrains.com/issue/PY-51428
  62. DeprecationWarning = PydanticDeprecatedSince20
  63. def validator(
  64. __field: str,
  65. *fields: str,
  66. pre: bool = False,
  67. each_item: bool = False,
  68. always: bool = False,
  69. check_fields: bool | None = None,
  70. allow_reuse: bool = False,
  71. ) -> Callable[[_V1ValidatorType], _V1ValidatorType]:
  72. """Decorate methods on the class indicating that they should be used to validate fields.
  73. Args:
  74. __field (str): The first field the validator should be called on; this is separate
  75. from `fields` to ensure an error is raised if you don't pass at least one.
  76. *fields (str): Additional field(s) the validator should be called on.
  77. pre (bool, optional): Whether or not this validator should be called before the standard
  78. validators (else after). Defaults to False.
  79. each_item (bool, optional): For complex objects (sets, lists etc.) whether to validate
  80. individual elements rather than the whole object. Defaults to False.
  81. always (bool, optional): Whether this method and other validators should be called even if
  82. the value is missing. Defaults to False.
  83. check_fields (bool | None, optional): Whether to check that the fields actually exist on the model.
  84. Defaults to None.
  85. allow_reuse (bool, optional): Whether to track and raise an error if another validator refers to
  86. the decorated function. Defaults to False.
  87. Returns:
  88. Callable: A decorator that can be used to decorate a
  89. function to be used as a validator.
  90. """
  91. if allow_reuse is True: # pragma: no cover
  92. warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning)
  93. fields = tuple((__field, *fields))
  94. if isinstance(fields[0], FunctionType):
  95. raise PydanticUserError(
  96. '`@validator` should be used with fields and keyword arguments, not bare. '
  97. "E.g. usage should be `@validator('<field_name>', ...)`",
  98. code='validator-no-fields',
  99. )
  100. elif not all(isinstance(field, str) for field in fields):
  101. raise PydanticUserError(
  102. '`@validator` fields should be passed as separate string args. '
  103. "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)`",
  104. code='validator-invalid-fields',
  105. )
  106. warn(
  107. 'Pydantic V1 style `@validator` validators are deprecated.'
  108. ' You should migrate to Pydantic V2 style `@field_validator` validators,'
  109. ' see the migration guide for more details',
  110. DeprecationWarning,
  111. stacklevel=2,
  112. )
  113. mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
  114. def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
  115. if _decorators.is_instance_method_from_sig(f):
  116. raise PydanticUserError(
  117. '`@validator` cannot be applied to instance methods', code='validator-instance-method'
  118. )
  119. # auto apply the @classmethod decorator
  120. f = _decorators.ensure_classmethod_based_on_signature(f)
  121. wrap = _decorators_v1.make_generic_v1_field_validator
  122. validator_wrapper_info = _decorators.ValidatorDecoratorInfo(
  123. fields=fields,
  124. mode=mode,
  125. each_item=each_item,
  126. always=always,
  127. check_fields=check_fields,
  128. )
  129. return _decorators.PydanticDescriptorProxy(f, validator_wrapper_info, shim=wrap)
  130. return dec # type: ignore[return-value]
  131. @overload
  132. def root_validator(
  133. *,
  134. # if you don't specify `pre` the default is `pre=False`
  135. # which means you need to specify `skip_on_failure=True`
  136. skip_on_failure: Literal[True],
  137. allow_reuse: bool = ...,
  138. ) -> Callable[
  139. [_V1RootValidatorFunctionType],
  140. _V1RootValidatorFunctionType,
  141. ]:
  142. ...
  143. @overload
  144. def root_validator(
  145. *,
  146. # if you specify `pre=True` then you don't need to specify
  147. # `skip_on_failure`, in fact it is not allowed as an argument!
  148. pre: Literal[True],
  149. allow_reuse: bool = ...,
  150. ) -> Callable[
  151. [_V1RootValidatorFunctionType],
  152. _V1RootValidatorFunctionType,
  153. ]:
  154. ...
  155. @overload
  156. def root_validator(
  157. *,
  158. # if you explicitly specify `pre=False` then you
  159. # MUST specify `skip_on_failure=True`
  160. pre: Literal[False],
  161. skip_on_failure: Literal[True],
  162. allow_reuse: bool = ...,
  163. ) -> Callable[
  164. [_V1RootValidatorFunctionType],
  165. _V1RootValidatorFunctionType,
  166. ]:
  167. ...
  168. def root_validator(
  169. *__args,
  170. pre: bool = False,
  171. skip_on_failure: bool = False,
  172. allow_reuse: bool = False,
  173. ) -> Any:
  174. """Decorate methods on a model indicating that they should be used to validate (and perhaps
  175. modify) data either before or after standard model parsing/validation is performed.
  176. Args:
  177. pre (bool, optional): Whether this validator should be called before the standard
  178. validators (else after). Defaults to False.
  179. skip_on_failure (bool, optional): Whether to stop validation and return as soon as a
  180. failure is encountered. Defaults to False.
  181. allow_reuse (bool, optional): Whether to track and raise an error if another validator
  182. refers to the decorated function. Defaults to False.
  183. Returns:
  184. Any: A decorator that can be used to decorate a function to be used as a root_validator.
  185. """
  186. warn(
  187. 'Pydantic V1 style `@root_validator` validators are deprecated.'
  188. ' You should migrate to Pydantic V2 style `@model_validator` validators,'
  189. ' see the migration guide for more details',
  190. DeprecationWarning,
  191. stacklevel=2,
  192. )
  193. if __args:
  194. # Ensure a nice error is raised if someone attempts to use the bare decorator
  195. return root_validator()(*__args) # type: ignore
  196. if allow_reuse is True: # pragma: no cover
  197. warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning)
  198. mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
  199. if pre is False and skip_on_failure is not True:
  200. raise PydanticUserError(
  201. 'If you use `@root_validator` with pre=False (the default) you MUST specify `skip_on_failure=True`.'
  202. ' Note that `@root_validator` is deprecated and should be replaced with `@model_validator`.',
  203. code='root-validator-pre-skip',
  204. )
  205. wrap = partial(_decorators_v1.make_v1_generic_root_validator, pre=pre)
  206. def dec(f: Callable[..., Any] | classmethod[Any, Any, Any] | staticmethod[Any, Any]) -> Any:
  207. if _decorators.is_instance_method_from_sig(f):
  208. raise TypeError('`@root_validator` cannot be applied to instance methods')
  209. # auto apply the @classmethod decorator
  210. res = _decorators.ensure_classmethod_based_on_signature(f)
  211. dec_info = _decorators.RootValidatorDecoratorInfo(mode=mode)
  212. return _decorators.PydanticDescriptorProxy(res, dec_info, shim=wrap)
  213. return dec