class_validators.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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, Literal, TypeVar, Union, overload
  6. from warnings import warn
  7. from typing_extensions import Protocol, TypeAlias, deprecated
  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. class _V1ValidatorWithValuesClsMethod(Protocol):
  16. def __call__(self, __cls: Any, __value: Any, values: dict[str, Any]) -> Any: ...
  17. class _V1ValidatorWithValuesKwOnlyClsMethod(Protocol):
  18. def __call__(self, __cls: Any, __value: Any, *, values: dict[str, Any]) -> Any: ...
  19. class _V1ValidatorWithKwargsClsMethod(Protocol):
  20. def __call__(self, __cls: Any, **kwargs: Any) -> Any: ...
  21. class _V1ValidatorWithValuesAndKwargsClsMethod(Protocol):
  22. def __call__(self, __cls: Any, values: dict[str, Any], **kwargs: Any) -> Any: ...
  23. class _V1RootValidatorClsMethod(Protocol):
  24. def __call__(
  25. self, __cls: Any, __values: _decorators_v1.RootValidatorValues
  26. ) -> _decorators_v1.RootValidatorValues: ...
  27. V1Validator = Union[
  28. _OnlyValueValidatorClsMethod,
  29. _V1ValidatorWithValuesClsMethod,
  30. _V1ValidatorWithValuesKwOnlyClsMethod,
  31. _V1ValidatorWithKwargsClsMethod,
  32. _V1ValidatorWithValuesAndKwargsClsMethod,
  33. _decorators_v1.V1ValidatorWithValues,
  34. _decorators_v1.V1ValidatorWithValuesKwOnly,
  35. _decorators_v1.V1ValidatorWithKwargs,
  36. _decorators_v1.V1ValidatorWithValuesAndKwargs,
  37. ]
  38. V1RootValidator = Union[
  39. _V1RootValidatorClsMethod,
  40. _decorators_v1.V1RootValidatorFunction,
  41. ]
  42. _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
  43. # Allow both a V1 (assumed pre=False) or V2 (assumed mode='after') validator
  44. # We lie to type checkers and say we return the same thing we get
  45. # but in reality we return a proxy object that _mostly_ behaves like the wrapped thing
  46. _V1ValidatorType = TypeVar('_V1ValidatorType', V1Validator, _PartialClsOrStaticMethod)
  47. _V1RootValidatorFunctionType = TypeVar(
  48. '_V1RootValidatorFunctionType',
  49. _decorators_v1.V1RootValidatorFunction,
  50. _V1RootValidatorClsMethod,
  51. _PartialClsOrStaticMethod,
  52. )
  53. else:
  54. # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
  55. # and https://youtrack.jetbrains.com/issue/PY-51428
  56. DeprecationWarning = PydanticDeprecatedSince20
  57. @deprecated(
  58. 'Pydantic V1 style `@validator` validators are deprecated.'
  59. ' You should migrate to Pydantic V2 style `@field_validator` validators,'
  60. ' see the migration guide for more details',
  61. category=None,
  62. )
  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 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. warn(
  92. 'Pydantic V1 style `@validator` validators are deprecated.'
  93. ' You should migrate to Pydantic V2 style `@field_validator` validators,'
  94. ' see the migration guide for more details',
  95. DeprecationWarning,
  96. stacklevel=2,
  97. )
  98. if allow_reuse is True: # pragma: no cover
  99. warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning, stacklevel=2)
  100. fields = __field, *fields
  101. if isinstance(fields[0], FunctionType):
  102. raise PydanticUserError(
  103. '`@validator` should be used with fields and keyword arguments, not bare. '
  104. "E.g. usage should be `@validator('<field_name>', ...)`",
  105. code='validator-no-fields',
  106. )
  107. elif not all(isinstance(field, str) for field in fields):
  108. raise PydanticUserError(
  109. '`@validator` fields should be passed as separate string args. '
  110. "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)`",
  111. code='validator-invalid-fields',
  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. @overload
  143. def root_validator(
  144. *,
  145. # if you specify `pre=True` then you don't need to specify
  146. # `skip_on_failure`, in fact it is not allowed as an argument!
  147. pre: Literal[True],
  148. allow_reuse: bool = ...,
  149. ) -> Callable[
  150. [_V1RootValidatorFunctionType],
  151. _V1RootValidatorFunctionType,
  152. ]: ...
  153. @overload
  154. def root_validator(
  155. *,
  156. # if you explicitly specify `pre=False` then you
  157. # MUST specify `skip_on_failure=True`
  158. pre: Literal[False],
  159. skip_on_failure: Literal[True],
  160. allow_reuse: bool = ...,
  161. ) -> Callable[
  162. [_V1RootValidatorFunctionType],
  163. _V1RootValidatorFunctionType,
  164. ]: ...
  165. @deprecated(
  166. 'Pydantic V1 style `@root_validator` validators are deprecated.'
  167. ' You should migrate to Pydantic V2 style `@model_validator` validators,'
  168. ' see the migration guide for more details',
  169. category=None,
  170. )
  171. def root_validator(
  172. *__args,
  173. pre: bool = False,
  174. skip_on_failure: bool = False,
  175. allow_reuse: bool = False,
  176. ) -> Any:
  177. """Decorate methods on a model indicating that they should be used to validate (and perhaps
  178. modify) data either before or after standard model parsing/validation is performed.
  179. Args:
  180. pre (bool, optional): Whether this validator should be called before the standard
  181. validators (else after). Defaults to False.
  182. skip_on_failure (bool, optional): Whether to stop validation and return as soon as a
  183. failure is encountered. Defaults to False.
  184. allow_reuse (bool, optional): Whether to track and raise an error if another validator
  185. refers to the decorated function. Defaults to False.
  186. Returns:
  187. Any: A decorator that can be used to decorate a function to be used as a root_validator.
  188. """
  189. warn(
  190. 'Pydantic V1 style `@root_validator` validators are deprecated.'
  191. ' You should migrate to Pydantic V2 style `@model_validator` validators,'
  192. ' see the migration guide for more details',
  193. DeprecationWarning,
  194. stacklevel=2,
  195. )
  196. if __args:
  197. # Ensure a nice error is raised if someone attempts to use the bare decorator
  198. return root_validator()(*__args) # type: ignore
  199. if allow_reuse is True: # pragma: no cover
  200. warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning, stacklevel=2)
  201. mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
  202. if pre is False and skip_on_failure is not True:
  203. raise PydanticUserError(
  204. 'If you use `@root_validator` with pre=False (the default) you MUST specify `skip_on_failure=True`.'
  205. ' Note that `@root_validator` is deprecated and should be replaced with `@model_validator`.',
  206. code='root-validator-pre-skip',
  207. )
  208. wrap = partial(_decorators_v1.make_v1_generic_root_validator, pre=pre)
  209. def dec(f: Callable[..., Any] | classmethod[Any, Any, Any] | staticmethod[Any, Any]) -> Any:
  210. if _decorators.is_instance_method_from_sig(f):
  211. raise TypeError('`@root_validator` cannot be applied to instance methods')
  212. # auto apply the @classmethod decorator
  213. res = _decorators.ensure_classmethod_based_on_signature(f)
  214. dec_info = _decorators.RootValidatorDecoratorInfo(mode=mode)
  215. return _decorators.PydanticDescriptorProxy(res, dec_info, shim=wrap)
  216. return dec