|
from decimal import Decimal |
|
from pathlib import Path |
|
from typing import TYPE_CHECKING, Any, Callable, Sequence, Set, Tuple, Type, Union |
|
|
|
from pydantic.v1.typing import display_as_type |
|
|
|
if TYPE_CHECKING: |
|
from pydantic.v1.typing import DictStrAny |
|
|
|
|
|
__all__ = ( |
|
'PydanticTypeError', |
|
'PydanticValueError', |
|
'ConfigError', |
|
'MissingError', |
|
'ExtraError', |
|
'NoneIsNotAllowedError', |
|
'NoneIsAllowedError', |
|
'WrongConstantError', |
|
'NotNoneError', |
|
'BoolError', |
|
'BytesError', |
|
'DictError', |
|
'EmailError', |
|
'UrlError', |
|
'UrlSchemeError', |
|
'UrlSchemePermittedError', |
|
'UrlUserInfoError', |
|
'UrlHostError', |
|
'UrlHostTldError', |
|
'UrlPortError', |
|
'UrlExtraError', |
|
'EnumError', |
|
'IntEnumError', |
|
'EnumMemberError', |
|
'IntegerError', |
|
'FloatError', |
|
'PathError', |
|
'PathNotExistsError', |
|
'PathNotAFileError', |
|
'PathNotADirectoryError', |
|
'PyObjectError', |
|
'SequenceError', |
|
'ListError', |
|
'SetError', |
|
'FrozenSetError', |
|
'TupleError', |
|
'TupleLengthError', |
|
'ListMinLengthError', |
|
'ListMaxLengthError', |
|
'ListUniqueItemsError', |
|
'SetMinLengthError', |
|
'SetMaxLengthError', |
|
'FrozenSetMinLengthError', |
|
'FrozenSetMaxLengthError', |
|
'AnyStrMinLengthError', |
|
'AnyStrMaxLengthError', |
|
'StrError', |
|
'StrRegexError', |
|
'NumberNotGtError', |
|
'NumberNotGeError', |
|
'NumberNotLtError', |
|
'NumberNotLeError', |
|
'NumberNotMultipleError', |
|
'DecimalError', |
|
'DecimalIsNotFiniteError', |
|
'DecimalMaxDigitsError', |
|
'DecimalMaxPlacesError', |
|
'DecimalWholeDigitsError', |
|
'DateTimeError', |
|
'DateError', |
|
'DateNotInThePastError', |
|
'DateNotInTheFutureError', |
|
'TimeError', |
|
'DurationError', |
|
'HashableError', |
|
'UUIDError', |
|
'UUIDVersionError', |
|
'ArbitraryTypeError', |
|
'ClassError', |
|
'SubclassError', |
|
'JsonError', |
|
'JsonTypeError', |
|
'PatternError', |
|
'DataclassTypeError', |
|
'CallableError', |
|
'IPvAnyAddressError', |
|
'IPvAnyInterfaceError', |
|
'IPvAnyNetworkError', |
|
'IPv4AddressError', |
|
'IPv6AddressError', |
|
'IPv4NetworkError', |
|
'IPv6NetworkError', |
|
'IPv4InterfaceError', |
|
'IPv6InterfaceError', |
|
'ColorError', |
|
'StrictBoolError', |
|
'NotDigitError', |
|
'LuhnValidationError', |
|
'InvalidLengthForBrand', |
|
'InvalidByteSize', |
|
'InvalidByteSizeUnit', |
|
'MissingDiscriminator', |
|
'InvalidDiscriminator', |
|
) |
|
|
|
|
|
def cls_kwargs(cls: Type['PydanticErrorMixin'], ctx: 'DictStrAny') -> 'PydanticErrorMixin': |
|
""" |
|
For built-in exceptions like ValueError or TypeError, we need to implement |
|
__reduce__ to override the default behaviour (instead of __getstate__/__setstate__) |
|
By default pickle protocol 2 calls `cls.__new__(cls, *args)`. |
|
Since we only use kwargs, we need a little constructor to change that. |
|
Note: the callable can't be a lambda as pickle looks in the namespace to find it |
|
""" |
|
return cls(**ctx) |
|
|
|
|
|
class PydanticErrorMixin: |
|
code: str |
|
msg_template: str |
|
|
|
def __init__(self, **ctx: Any) -> None: |
|
self.__dict__ = ctx |
|
|
|
def __str__(self) -> str: |
|
return self.msg_template.format(**self.__dict__) |
|
|
|
def __reduce__(self) -> Tuple[Callable[..., 'PydanticErrorMixin'], Tuple[Type['PydanticErrorMixin'], 'DictStrAny']]: |
|
return cls_kwargs, (self.__class__, self.__dict__) |
|
|
|
|
|
class PydanticTypeError(PydanticErrorMixin, TypeError): |
|
pass |
|
|
|
|
|
class PydanticValueError(PydanticErrorMixin, ValueError): |
|
pass |
|
|
|
|
|
class ConfigError(RuntimeError): |
|
pass |
|
|
|
|
|
class MissingError(PydanticValueError): |
|
msg_template = 'field required' |
|
|
|
|
|
class ExtraError(PydanticValueError): |
|
msg_template = 'extra fields not permitted' |
|
|
|
|
|
class NoneIsNotAllowedError(PydanticTypeError): |
|
code = 'none.not_allowed' |
|
msg_template = 'none is not an allowed value' |
|
|
|
|
|
class NoneIsAllowedError(PydanticTypeError): |
|
code = 'none.allowed' |
|
msg_template = 'value is not none' |
|
|
|
|
|
class WrongConstantError(PydanticValueError): |
|
code = 'const' |
|
|
|
def __str__(self) -> str: |
|
permitted = ', '.join(repr(v) for v in self.permitted) |
|
return f'unexpected value; permitted: {permitted}' |
|
|
|
|
|
class NotNoneError(PydanticTypeError): |
|
code = 'not_none' |
|
msg_template = 'value is not None' |
|
|
|
|
|
class BoolError(PydanticTypeError): |
|
msg_template = 'value could not be parsed to a boolean' |
|
|
|
|
|
class BytesError(PydanticTypeError): |
|
msg_template = 'byte type expected' |
|
|
|
|
|
class DictError(PydanticTypeError): |
|
msg_template = 'value is not a valid dict' |
|
|
|
|
|
class EmailError(PydanticValueError): |
|
msg_template = 'value is not a valid email address' |
|
|
|
|
|
class UrlError(PydanticValueError): |
|
code = 'url' |
|
|
|
|
|
class UrlSchemeError(UrlError): |
|
code = 'url.scheme' |
|
msg_template = 'invalid or missing URL scheme' |
|
|
|
|
|
class UrlSchemePermittedError(UrlError): |
|
code = 'url.scheme' |
|
msg_template = 'URL scheme not permitted' |
|
|
|
def __init__(self, allowed_schemes: Set[str]): |
|
super().__init__(allowed_schemes=allowed_schemes) |
|
|
|
|
|
class UrlUserInfoError(UrlError): |
|
code = 'url.userinfo' |
|
msg_template = 'userinfo required in URL but missing' |
|
|
|
|
|
class UrlHostError(UrlError): |
|
code = 'url.host' |
|
msg_template = 'URL host invalid' |
|
|
|
|
|
class UrlHostTldError(UrlError): |
|
code = 'url.host' |
|
msg_template = 'URL host invalid, top level domain required' |
|
|
|
|
|
class UrlPortError(UrlError): |
|
code = 'url.port' |
|
msg_template = 'URL port invalid, port cannot exceed 65535' |
|
|
|
|
|
class UrlExtraError(UrlError): |
|
code = 'url.extra' |
|
msg_template = 'URL invalid, extra characters found after valid URL: {extra!r}' |
|
|
|
|
|
class EnumMemberError(PydanticTypeError): |
|
code = 'enum' |
|
|
|
def __str__(self) -> str: |
|
permitted = ', '.join(repr(v.value) for v in self.enum_values) |
|
return f'value is not a valid enumeration member; permitted: {permitted}' |
|
|
|
|
|
class IntegerError(PydanticTypeError): |
|
msg_template = 'value is not a valid integer' |
|
|
|
|
|
class FloatError(PydanticTypeError): |
|
msg_template = 'value is not a valid float' |
|
|
|
|
|
class PathError(PydanticTypeError): |
|
msg_template = 'value is not a valid path' |
|
|
|
|
|
class _PathValueError(PydanticValueError): |
|
def __init__(self, *, path: Path) -> None: |
|
super().__init__(path=str(path)) |
|
|
|
|
|
class PathNotExistsError(_PathValueError): |
|
code = 'path.not_exists' |
|
msg_template = 'file or directory at path "{path}" does not exist' |
|
|
|
|
|
class PathNotAFileError(_PathValueError): |
|
code = 'path.not_a_file' |
|
msg_template = 'path "{path}" does not point to a file' |
|
|
|
|
|
class PathNotADirectoryError(_PathValueError): |
|
code = 'path.not_a_directory' |
|
msg_template = 'path "{path}" does not point to a directory' |
|
|
|
|
|
class PyObjectError(PydanticTypeError): |
|
msg_template = 'ensure this value contains valid import path or valid callable: {error_message}' |
|
|
|
|
|
class SequenceError(PydanticTypeError): |
|
msg_template = 'value is not a valid sequence' |
|
|
|
|
|
class IterableError(PydanticTypeError): |
|
msg_template = 'value is not a valid iterable' |
|
|
|
|
|
class ListError(PydanticTypeError): |
|
msg_template = 'value is not a valid list' |
|
|
|
|
|
class SetError(PydanticTypeError): |
|
msg_template = 'value is not a valid set' |
|
|
|
|
|
class FrozenSetError(PydanticTypeError): |
|
msg_template = 'value is not a valid frozenset' |
|
|
|
|
|
class DequeError(PydanticTypeError): |
|
msg_template = 'value is not a valid deque' |
|
|
|
|
|
class TupleError(PydanticTypeError): |
|
msg_template = 'value is not a valid tuple' |
|
|
|
|
|
class TupleLengthError(PydanticValueError): |
|
code = 'tuple.length' |
|
msg_template = 'wrong tuple length {actual_length}, expected {expected_length}' |
|
|
|
def __init__(self, *, actual_length: int, expected_length: int) -> None: |
|
super().__init__(actual_length=actual_length, expected_length=expected_length) |
|
|
|
|
|
class ListMinLengthError(PydanticValueError): |
|
code = 'list.min_items' |
|
msg_template = 'ensure this value has at least {limit_value} items' |
|
|
|
def __init__(self, *, limit_value: int) -> None: |
|
super().__init__(limit_value=limit_value) |
|
|
|
|
|
class ListMaxLengthError(PydanticValueError): |
|
code = 'list.max_items' |
|
msg_template = 'ensure this value has at most {limit_value} items' |
|
|
|
def __init__(self, *, limit_value: int) -> None: |
|
super().__init__(limit_value=limit_value) |
|
|
|
|
|
class ListUniqueItemsError(PydanticValueError): |
|
code = 'list.unique_items' |
|
msg_template = 'the list has duplicated items' |
|
|
|
|
|
class SetMinLengthError(PydanticValueError): |
|
code = 'set.min_items' |
|
msg_template = 'ensure this value has at least {limit_value} items' |
|
|
|
def __init__(self, *, limit_value: int) -> None: |
|
super().__init__(limit_value=limit_value) |
|
|
|
|
|
class SetMaxLengthError(PydanticValueError): |
|
code = 'set.max_items' |
|
msg_template = 'ensure this value has at most {limit_value} items' |
|
|
|
def __init__(self, *, limit_value: int) -> None: |
|
super().__init__(limit_value=limit_value) |
|
|
|
|
|
class FrozenSetMinLengthError(PydanticValueError): |
|
code = 'frozenset.min_items' |
|
msg_template = 'ensure this value has at least {limit_value} items' |
|
|
|
def __init__(self, *, limit_value: int) -> None: |
|
super().__init__(limit_value=limit_value) |
|
|
|
|
|
class FrozenSetMaxLengthError(PydanticValueError): |
|
code = 'frozenset.max_items' |
|
msg_template = 'ensure this value has at most {limit_value} items' |
|
|
|
def __init__(self, *, limit_value: int) -> None: |
|
super().__init__(limit_value=limit_value) |
|
|
|
|
|
class AnyStrMinLengthError(PydanticValueError): |
|
code = 'any_str.min_length' |
|
msg_template = 'ensure this value has at least {limit_value} characters' |
|
|
|
def __init__(self, *, limit_value: int) -> None: |
|
super().__init__(limit_value=limit_value) |
|
|
|
|
|
class AnyStrMaxLengthError(PydanticValueError): |
|
code = 'any_str.max_length' |
|
msg_template = 'ensure this value has at most {limit_value} characters' |
|
|
|
def __init__(self, *, limit_value: int) -> None: |
|
super().__init__(limit_value=limit_value) |
|
|
|
|
|
class StrError(PydanticTypeError): |
|
msg_template = 'str type expected' |
|
|
|
|
|
class StrRegexError(PydanticValueError): |
|
code = 'str.regex' |
|
msg_template = 'string does not match regex "{pattern}"' |
|
|
|
def __init__(self, *, pattern: str) -> None: |
|
super().__init__(pattern=pattern) |
|
|
|
|
|
class _NumberBoundError(PydanticValueError): |
|
def __init__(self, *, limit_value: Union[int, float, Decimal]) -> None: |
|
super().__init__(limit_value=limit_value) |
|
|
|
|
|
class NumberNotGtError(_NumberBoundError): |
|
code = 'number.not_gt' |
|
msg_template = 'ensure this value is greater than {limit_value}' |
|
|
|
|
|
class NumberNotGeError(_NumberBoundError): |
|
code = 'number.not_ge' |
|
msg_template = 'ensure this value is greater than or equal to {limit_value}' |
|
|
|
|
|
class NumberNotLtError(_NumberBoundError): |
|
code = 'number.not_lt' |
|
msg_template = 'ensure this value is less than {limit_value}' |
|
|
|
|
|
class NumberNotLeError(_NumberBoundError): |
|
code = 'number.not_le' |
|
msg_template = 'ensure this value is less than or equal to {limit_value}' |
|
|
|
|
|
class NumberNotFiniteError(PydanticValueError): |
|
code = 'number.not_finite_number' |
|
msg_template = 'ensure this value is a finite number' |
|
|
|
|
|
class NumberNotMultipleError(PydanticValueError): |
|
code = 'number.not_multiple' |
|
msg_template = 'ensure this value is a multiple of {multiple_of}' |
|
|
|
def __init__(self, *, multiple_of: Union[int, float, Decimal]) -> None: |
|
super().__init__(multiple_of=multiple_of) |
|
|
|
|
|
class DecimalError(PydanticTypeError): |
|
msg_template = 'value is not a valid decimal' |
|
|
|
|
|
class DecimalIsNotFiniteError(PydanticValueError): |
|
code = 'decimal.not_finite' |
|
msg_template = 'value is not a valid decimal' |
|
|
|
|
|
class DecimalMaxDigitsError(PydanticValueError): |
|
code = 'decimal.max_digits' |
|
msg_template = 'ensure that there are no more than {max_digits} digits in total' |
|
|
|
def __init__(self, *, max_digits: int) -> None: |
|
super().__init__(max_digits=max_digits) |
|
|
|
|
|
class DecimalMaxPlacesError(PydanticValueError): |
|
code = 'decimal.max_places' |
|
msg_template = 'ensure that there are no more than {decimal_places} decimal places' |
|
|
|
def __init__(self, *, decimal_places: int) -> None: |
|
super().__init__(decimal_places=decimal_places) |
|
|
|
|
|
class DecimalWholeDigitsError(PydanticValueError): |
|
code = 'decimal.whole_digits' |
|
msg_template = 'ensure that there are no more than {whole_digits} digits before the decimal point' |
|
|
|
def __init__(self, *, whole_digits: int) -> None: |
|
super().__init__(whole_digits=whole_digits) |
|
|
|
|
|
class DateTimeError(PydanticValueError): |
|
msg_template = 'invalid datetime format' |
|
|
|
|
|
class DateError(PydanticValueError): |
|
msg_template = 'invalid date format' |
|
|
|
|
|
class DateNotInThePastError(PydanticValueError): |
|
code = 'date.not_in_the_past' |
|
msg_template = 'date is not in the past' |
|
|
|
|
|
class DateNotInTheFutureError(PydanticValueError): |
|
code = 'date.not_in_the_future' |
|
msg_template = 'date is not in the future' |
|
|
|
|
|
class TimeError(PydanticValueError): |
|
msg_template = 'invalid time format' |
|
|
|
|
|
class DurationError(PydanticValueError): |
|
msg_template = 'invalid duration format' |
|
|
|
|
|
class HashableError(PydanticTypeError): |
|
msg_template = 'value is not a valid hashable' |
|
|
|
|
|
class UUIDError(PydanticTypeError): |
|
msg_template = 'value is not a valid uuid' |
|
|
|
|
|
class UUIDVersionError(PydanticValueError): |
|
code = 'uuid.version' |
|
msg_template = 'uuid version {required_version} expected' |
|
|
|
def __init__(self, *, required_version: int) -> None: |
|
super().__init__(required_version=required_version) |
|
|
|
|
|
class ArbitraryTypeError(PydanticTypeError): |
|
code = 'arbitrary_type' |
|
msg_template = 'instance of {expected_arbitrary_type} expected' |
|
|
|
def __init__(self, *, expected_arbitrary_type: Type[Any]) -> None: |
|
super().__init__(expected_arbitrary_type=display_as_type(expected_arbitrary_type)) |
|
|
|
|
|
class ClassError(PydanticTypeError): |
|
code = 'class' |
|
msg_template = 'a class is expected' |
|
|
|
|
|
class SubclassError(PydanticTypeError): |
|
code = 'subclass' |
|
msg_template = 'subclass of {expected_class} expected' |
|
|
|
def __init__(self, *, expected_class: Type[Any]) -> None: |
|
super().__init__(expected_class=display_as_type(expected_class)) |
|
|
|
|
|
class JsonError(PydanticValueError): |
|
msg_template = 'Invalid JSON' |
|
|
|
|
|
class JsonTypeError(PydanticTypeError): |
|
code = 'json' |
|
msg_template = 'JSON object must be str, bytes or bytearray' |
|
|
|
|
|
class PatternError(PydanticValueError): |
|
code = 'regex_pattern' |
|
msg_template = 'Invalid regular expression' |
|
|
|
|
|
class DataclassTypeError(PydanticTypeError): |
|
code = 'dataclass' |
|
msg_template = 'instance of {class_name}, tuple or dict expected' |
|
|
|
|
|
class CallableError(PydanticTypeError): |
|
msg_template = '{value} is not callable' |
|
|
|
|
|
class EnumError(PydanticTypeError): |
|
code = 'enum_instance' |
|
msg_template = '{value} is not a valid Enum instance' |
|
|
|
|
|
class IntEnumError(PydanticTypeError): |
|
code = 'int_enum_instance' |
|
msg_template = '{value} is not a valid IntEnum instance' |
|
|
|
|
|
class IPvAnyAddressError(PydanticValueError): |
|
msg_template = 'value is not a valid IPv4 or IPv6 address' |
|
|
|
|
|
class IPvAnyInterfaceError(PydanticValueError): |
|
msg_template = 'value is not a valid IPv4 or IPv6 interface' |
|
|
|
|
|
class IPvAnyNetworkError(PydanticValueError): |
|
msg_template = 'value is not a valid IPv4 or IPv6 network' |
|
|
|
|
|
class IPv4AddressError(PydanticValueError): |
|
msg_template = 'value is not a valid IPv4 address' |
|
|
|
|
|
class IPv6AddressError(PydanticValueError): |
|
msg_template = 'value is not a valid IPv6 address' |
|
|
|
|
|
class IPv4NetworkError(PydanticValueError): |
|
msg_template = 'value is not a valid IPv4 network' |
|
|
|
|
|
class IPv6NetworkError(PydanticValueError): |
|
msg_template = 'value is not a valid IPv6 network' |
|
|
|
|
|
class IPv4InterfaceError(PydanticValueError): |
|
msg_template = 'value is not a valid IPv4 interface' |
|
|
|
|
|
class IPv6InterfaceError(PydanticValueError): |
|
msg_template = 'value is not a valid IPv6 interface' |
|
|
|
|
|
class ColorError(PydanticValueError): |
|
msg_template = 'value is not a valid color: {reason}' |
|
|
|
|
|
class StrictBoolError(PydanticValueError): |
|
msg_template = 'value is not a valid boolean' |
|
|
|
|
|
class NotDigitError(PydanticValueError): |
|
code = 'payment_card_number.digits' |
|
msg_template = 'card number is not all digits' |
|
|
|
|
|
class LuhnValidationError(PydanticValueError): |
|
code = 'payment_card_number.luhn_check' |
|
msg_template = 'card number is not luhn valid' |
|
|
|
|
|
class InvalidLengthForBrand(PydanticValueError): |
|
code = 'payment_card_number.invalid_length_for_brand' |
|
msg_template = 'Length for a {brand} card must be {required_length}' |
|
|
|
|
|
class InvalidByteSize(PydanticValueError): |
|
msg_template = 'could not parse value and unit from byte string' |
|
|
|
|
|
class InvalidByteSizeUnit(PydanticValueError): |
|
msg_template = 'could not interpret byte unit: {unit}' |
|
|
|
|
|
class MissingDiscriminator(PydanticValueError): |
|
code = 'discriminated_union.missing_discriminator' |
|
msg_template = 'Discriminator {discriminator_key!r} is missing in value' |
|
|
|
|
|
class InvalidDiscriminator(PydanticValueError): |
|
code = 'discriminated_union.invalid_discriminator' |
|
msg_template = ( |
|
'No match for discriminator {discriminator_key!r} and value {discriminator_value!r} ' |
|
'(allowed values: {allowed_values})' |
|
) |
|
|
|
def __init__(self, *, discriminator_key: str, discriminator_value: Any, allowed_values: Sequence[Any]) -> None: |
|
super().__init__( |
|
discriminator_key=discriminator_key, |
|
discriminator_value=discriminator_value, |
|
allowed_values=', '.join(map(repr, allowed_values)), |
|
) |
|
|