| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- """Old `@validator` and `@root_validator` function validators from V1."""
- from __future__ import annotations as _annotations
- from functools import partial, partialmethod
- from types import FunctionType
- from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, Union, overload
- from warnings import warn
- from typing_extensions import Protocol, TypeAlias, deprecated
- from .._internal import _decorators, _decorators_v1
- from ..errors import PydanticUserError
- from ..warnings import PydanticDeprecatedSince20
- _ALLOW_REUSE_WARNING_MESSAGE = '`allow_reuse` is deprecated and will be ignored; it should no longer be necessary'
- if TYPE_CHECKING:
- class _OnlyValueValidatorClsMethod(Protocol):
- def __call__(self, __cls: Any, __value: Any) -> Any: ...
- class _V1ValidatorWithValuesClsMethod(Protocol):
- def __call__(self, __cls: Any, __value: Any, values: dict[str, Any]) -> Any: ...
- class _V1ValidatorWithValuesKwOnlyClsMethod(Protocol):
- def __call__(self, __cls: Any, __value: Any, *, values: dict[str, Any]) -> Any: ...
- class _V1ValidatorWithKwargsClsMethod(Protocol):
- def __call__(self, __cls: Any, **kwargs: Any) -> Any: ...
- class _V1ValidatorWithValuesAndKwargsClsMethod(Protocol):
- def __call__(self, __cls: Any, values: dict[str, Any], **kwargs: Any) -> Any: ...
- class _V1RootValidatorClsMethod(Protocol):
- def __call__(
- self, __cls: Any, __values: _decorators_v1.RootValidatorValues
- ) -> _decorators_v1.RootValidatorValues: ...
- V1Validator = Union[
- _OnlyValueValidatorClsMethod,
- _V1ValidatorWithValuesClsMethod,
- _V1ValidatorWithValuesKwOnlyClsMethod,
- _V1ValidatorWithKwargsClsMethod,
- _V1ValidatorWithValuesAndKwargsClsMethod,
- _decorators_v1.V1ValidatorWithValues,
- _decorators_v1.V1ValidatorWithValuesKwOnly,
- _decorators_v1.V1ValidatorWithKwargs,
- _decorators_v1.V1ValidatorWithValuesAndKwargs,
- ]
- V1RootValidator = Union[
- _V1RootValidatorClsMethod,
- _decorators_v1.V1RootValidatorFunction,
- ]
- _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
- # Allow both a V1 (assumed pre=False) or V2 (assumed mode='after') validator
- # We lie to type checkers and say we return the same thing we get
- # but in reality we return a proxy object that _mostly_ behaves like the wrapped thing
- _V1ValidatorType = TypeVar('_V1ValidatorType', V1Validator, _PartialClsOrStaticMethod)
- _V1RootValidatorFunctionType = TypeVar(
- '_V1RootValidatorFunctionType',
- _decorators_v1.V1RootValidatorFunction,
- _V1RootValidatorClsMethod,
- _PartialClsOrStaticMethod,
- )
- else:
- # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
- # and https://youtrack.jetbrains.com/issue/PY-51428
- DeprecationWarning = PydanticDeprecatedSince20
- @deprecated(
- 'Pydantic V1 style `@validator` validators are deprecated.'
- ' You should migrate to Pydantic V2 style `@field_validator` validators,'
- ' see the migration guide for more details',
- category=None,
- )
- def validator(
- __field: str,
- *fields: str,
- pre: bool = False,
- each_item: bool = False,
- always: bool = False,
- check_fields: bool | None = None,
- allow_reuse: bool = False,
- ) -> Callable[[_V1ValidatorType], _V1ValidatorType]:
- """Decorate methods on the class indicating that they should be used to validate fields.
- Args:
- __field (str): The first field the validator should be called on; this is separate
- from `fields` to ensure an error is raised if you don't pass at least one.
- *fields (str): Additional field(s) the validator should be called on.
- pre (bool, optional): Whether this validator should be called before the standard
- validators (else after). Defaults to False.
- each_item (bool, optional): For complex objects (sets, lists etc.) whether to validate
- individual elements rather than the whole object. Defaults to False.
- always (bool, optional): Whether this method and other validators should be called even if
- the value is missing. Defaults to False.
- check_fields (bool | None, optional): Whether to check that the fields actually exist on the model.
- Defaults to None.
- allow_reuse (bool, optional): Whether to track and raise an error if another validator refers to
- the decorated function. Defaults to False.
- Returns:
- Callable: A decorator that can be used to decorate a
- function to be used as a validator.
- """
- warn(
- 'Pydantic V1 style `@validator` validators are deprecated.'
- ' You should migrate to Pydantic V2 style `@field_validator` validators,'
- ' see the migration guide for more details',
- DeprecationWarning,
- stacklevel=2,
- )
- if allow_reuse is True: # pragma: no cover
- warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning, stacklevel=2)
- fields = __field, *fields
- if isinstance(fields[0], FunctionType):
- raise PydanticUserError(
- '`@validator` should be used with fields and keyword arguments, not bare. '
- "E.g. usage should be `@validator('<field_name>', ...)`",
- code='validator-no-fields',
- )
- elif not all(isinstance(field, str) for field in fields):
- raise PydanticUserError(
- '`@validator` fields should be passed as separate string args. '
- "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)`",
- code='validator-invalid-fields',
- )
- mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
- def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
- if _decorators.is_instance_method_from_sig(f):
- raise PydanticUserError(
- '`@validator` cannot be applied to instance methods', code='validator-instance-method'
- )
- # auto apply the @classmethod decorator
- f = _decorators.ensure_classmethod_based_on_signature(f)
- wrap = _decorators_v1.make_generic_v1_field_validator
- validator_wrapper_info = _decorators.ValidatorDecoratorInfo(
- fields=fields,
- mode=mode,
- each_item=each_item,
- always=always,
- check_fields=check_fields,
- )
- return _decorators.PydanticDescriptorProxy(f, validator_wrapper_info, shim=wrap)
- return dec # type: ignore[return-value]
- @overload
- def root_validator(
- *,
- # if you don't specify `pre` the default is `pre=False`
- # which means you need to specify `skip_on_failure=True`
- skip_on_failure: Literal[True],
- allow_reuse: bool = ...,
- ) -> Callable[
- [_V1RootValidatorFunctionType],
- _V1RootValidatorFunctionType,
- ]: ...
- @overload
- def root_validator(
- *,
- # if you specify `pre=True` then you don't need to specify
- # `skip_on_failure`, in fact it is not allowed as an argument!
- pre: Literal[True],
- allow_reuse: bool = ...,
- ) -> Callable[
- [_V1RootValidatorFunctionType],
- _V1RootValidatorFunctionType,
- ]: ...
- @overload
- def root_validator(
- *,
- # if you explicitly specify `pre=False` then you
- # MUST specify `skip_on_failure=True`
- pre: Literal[False],
- skip_on_failure: Literal[True],
- allow_reuse: bool = ...,
- ) -> Callable[
- [_V1RootValidatorFunctionType],
- _V1RootValidatorFunctionType,
- ]: ...
- @deprecated(
- 'Pydantic V1 style `@root_validator` validators are deprecated.'
- ' You should migrate to Pydantic V2 style `@model_validator` validators,'
- ' see the migration guide for more details',
- category=None,
- )
- def root_validator(
- *__args,
- pre: bool = False,
- skip_on_failure: bool = False,
- allow_reuse: bool = False,
- ) -> Any:
- """Decorate methods on a model indicating that they should be used to validate (and perhaps
- modify) data either before or after standard model parsing/validation is performed.
- Args:
- pre (bool, optional): Whether this validator should be called before the standard
- validators (else after). Defaults to False.
- skip_on_failure (bool, optional): Whether to stop validation and return as soon as a
- failure is encountered. Defaults to False.
- allow_reuse (bool, optional): Whether to track and raise an error if another validator
- refers to the decorated function. Defaults to False.
- Returns:
- Any: A decorator that can be used to decorate a function to be used as a root_validator.
- """
- warn(
- 'Pydantic V1 style `@root_validator` validators are deprecated.'
- ' You should migrate to Pydantic V2 style `@model_validator` validators,'
- ' see the migration guide for more details',
- DeprecationWarning,
- stacklevel=2,
- )
- if __args:
- # Ensure a nice error is raised if someone attempts to use the bare decorator
- return root_validator()(*__args) # type: ignore
- if allow_reuse is True: # pragma: no cover
- warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning, stacklevel=2)
- mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
- if pre is False and skip_on_failure is not True:
- raise PydanticUserError(
- 'If you use `@root_validator` with pre=False (the default) you MUST specify `skip_on_failure=True`.'
- ' Note that `@root_validator` is deprecated and should be replaced with `@model_validator`.',
- code='root-validator-pre-skip',
- )
- wrap = partial(_decorators_v1.make_v1_generic_root_validator, pre=pre)
- def dec(f: Callable[..., Any] | classmethod[Any, Any, Any] | staticmethod[Any, Any]) -> Any:
- if _decorators.is_instance_method_from_sig(f):
- raise TypeError('`@root_validator` cannot be applied to instance methods')
- # auto apply the @classmethod decorator
- res = _decorators.ensure_classmethod_based_on_signature(f)
- dec_info = _decorators.RootValidatorDecoratorInfo(mode=mode)
- return _decorators.PydanticDescriptorProxy(res, dec_info, shim=wrap)
- return dec
|