You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

642 lines
17 KiB

  1. from decimal import Decimal
  2. from pathlib import Path
  3. from typing import TYPE_CHECKING, Any, Callable, Sequence, Set, Tuple, Type, Union
  4. from .typing import display_as_type
  5. if TYPE_CHECKING:
  6. from .typing import DictStrAny
  7. # explicitly state exports to avoid "from .errors import *" also importing Decimal, Path etc.
  8. __all__ = (
  9. 'PydanticTypeError',
  10. 'PydanticValueError',
  11. 'ConfigError',
  12. 'MissingError',
  13. 'ExtraError',
  14. 'NoneIsNotAllowedError',
  15. 'NoneIsAllowedError',
  16. 'WrongConstantError',
  17. 'NotNoneError',
  18. 'BoolError',
  19. 'BytesError',
  20. 'DictError',
  21. 'EmailError',
  22. 'UrlError',
  23. 'UrlSchemeError',
  24. 'UrlSchemePermittedError',
  25. 'UrlUserInfoError',
  26. 'UrlHostError',
  27. 'UrlHostTldError',
  28. 'UrlPortError',
  29. 'UrlExtraError',
  30. 'EnumError',
  31. 'IntEnumError',
  32. 'EnumMemberError',
  33. 'IntegerError',
  34. 'FloatError',
  35. 'PathError',
  36. 'PathNotExistsError',
  37. 'PathNotAFileError',
  38. 'PathNotADirectoryError',
  39. 'PyObjectError',
  40. 'SequenceError',
  41. 'ListError',
  42. 'SetError',
  43. 'FrozenSetError',
  44. 'TupleError',
  45. 'TupleLengthError',
  46. 'ListMinLengthError',
  47. 'ListMaxLengthError',
  48. 'ListUniqueItemsError',
  49. 'SetMinLengthError',
  50. 'SetMaxLengthError',
  51. 'FrozenSetMinLengthError',
  52. 'FrozenSetMaxLengthError',
  53. 'AnyStrMinLengthError',
  54. 'AnyStrMaxLengthError',
  55. 'StrError',
  56. 'StrRegexError',
  57. 'NumberNotGtError',
  58. 'NumberNotGeError',
  59. 'NumberNotLtError',
  60. 'NumberNotLeError',
  61. 'NumberNotMultipleError',
  62. 'DecimalError',
  63. 'DecimalIsNotFiniteError',
  64. 'DecimalMaxDigitsError',
  65. 'DecimalMaxPlacesError',
  66. 'DecimalWholeDigitsError',
  67. 'DateTimeError',
  68. 'DateError',
  69. 'DateNotInThePastError',
  70. 'DateNotInTheFutureError',
  71. 'TimeError',
  72. 'DurationError',
  73. 'HashableError',
  74. 'UUIDError',
  75. 'UUIDVersionError',
  76. 'ArbitraryTypeError',
  77. 'ClassError',
  78. 'SubclassError',
  79. 'JsonError',
  80. 'JsonTypeError',
  81. 'PatternError',
  82. 'DataclassTypeError',
  83. 'CallableError',
  84. 'IPvAnyAddressError',
  85. 'IPvAnyInterfaceError',
  86. 'IPvAnyNetworkError',
  87. 'IPv4AddressError',
  88. 'IPv6AddressError',
  89. 'IPv4NetworkError',
  90. 'IPv6NetworkError',
  91. 'IPv4InterfaceError',
  92. 'IPv6InterfaceError',
  93. 'ColorError',
  94. 'StrictBoolError',
  95. 'NotDigitError',
  96. 'LuhnValidationError',
  97. 'InvalidLengthForBrand',
  98. 'InvalidByteSize',
  99. 'InvalidByteSizeUnit',
  100. 'MissingDiscriminator',
  101. 'InvalidDiscriminator',
  102. )
  103. def cls_kwargs(cls: Type['PydanticErrorMixin'], ctx: 'DictStrAny') -> 'PydanticErrorMixin':
  104. """
  105. For built-in exceptions like ValueError or TypeError, we need to implement
  106. __reduce__ to override the default behaviour (instead of __getstate__/__setstate__)
  107. By default pickle protocol 2 calls `cls.__new__(cls, *args)`.
  108. Since we only use kwargs, we need a little constructor to change that.
  109. Note: the callable can't be a lambda as pickle looks in the namespace to find it
  110. """
  111. return cls(**ctx)
  112. class PydanticErrorMixin:
  113. code: str
  114. msg_template: str
  115. def __init__(self, **ctx: Any) -> None:
  116. self.__dict__ = ctx
  117. def __str__(self) -> str:
  118. return self.msg_template.format(**self.__dict__)
  119. def __reduce__(self) -> Tuple[Callable[..., 'PydanticErrorMixin'], Tuple[Type['PydanticErrorMixin'], 'DictStrAny']]:
  120. return cls_kwargs, (self.__class__, self.__dict__)
  121. class PydanticTypeError(PydanticErrorMixin, TypeError):
  122. pass
  123. class PydanticValueError(PydanticErrorMixin, ValueError):
  124. pass
  125. class ConfigError(RuntimeError):
  126. pass
  127. class MissingError(PydanticValueError):
  128. msg_template = 'field required'
  129. class ExtraError(PydanticValueError):
  130. msg_template = 'extra fields not permitted'
  131. class NoneIsNotAllowedError(PydanticTypeError):
  132. code = 'none.not_allowed'
  133. msg_template = 'none is not an allowed value'
  134. class NoneIsAllowedError(PydanticTypeError):
  135. code = 'none.allowed'
  136. msg_template = 'value is not none'
  137. class WrongConstantError(PydanticValueError):
  138. code = 'const'
  139. def __str__(self) -> str:
  140. permitted = ', '.join(repr(v) for v in self.permitted) # type: ignore
  141. return f'unexpected value; permitted: {permitted}'
  142. class NotNoneError(PydanticTypeError):
  143. code = 'not_none'
  144. msg_template = 'value is not None'
  145. class BoolError(PydanticTypeError):
  146. msg_template = 'value could not be parsed to a boolean'
  147. class BytesError(PydanticTypeError):
  148. msg_template = 'byte type expected'
  149. class DictError(PydanticTypeError):
  150. msg_template = 'value is not a valid dict'
  151. class EmailError(PydanticValueError):
  152. msg_template = 'value is not a valid email address'
  153. class UrlError(PydanticValueError):
  154. code = 'url'
  155. class UrlSchemeError(UrlError):
  156. code = 'url.scheme'
  157. msg_template = 'invalid or missing URL scheme'
  158. class UrlSchemePermittedError(UrlError):
  159. code = 'url.scheme'
  160. msg_template = 'URL scheme not permitted'
  161. def __init__(self, allowed_schemes: Set[str]):
  162. super().__init__(allowed_schemes=allowed_schemes)
  163. class UrlUserInfoError(UrlError):
  164. code = 'url.userinfo'
  165. msg_template = 'userinfo required in URL but missing'
  166. class UrlHostError(UrlError):
  167. code = 'url.host'
  168. msg_template = 'URL host invalid'
  169. class UrlHostTldError(UrlError):
  170. code = 'url.host'
  171. msg_template = 'URL host invalid, top level domain required'
  172. class UrlPortError(UrlError):
  173. code = 'url.port'
  174. msg_template = 'URL port invalid, port cannot exceed 65535'
  175. class UrlExtraError(UrlError):
  176. code = 'url.extra'
  177. msg_template = 'URL invalid, extra characters found after valid URL: {extra!r}'
  178. class EnumMemberError(PydanticTypeError):
  179. code = 'enum'
  180. def __str__(self) -> str:
  181. permitted = ', '.join(repr(v.value) for v in self.enum_values) # type: ignore
  182. return f'value is not a valid enumeration member; permitted: {permitted}'
  183. class IntegerError(PydanticTypeError):
  184. msg_template = 'value is not a valid integer'
  185. class FloatError(PydanticTypeError):
  186. msg_template = 'value is not a valid float'
  187. class PathError(PydanticTypeError):
  188. msg_template = 'value is not a valid path'
  189. class _PathValueError(PydanticValueError):
  190. def __init__(self, *, path: Path) -> None:
  191. super().__init__(path=str(path))
  192. class PathNotExistsError(_PathValueError):
  193. code = 'path.not_exists'
  194. msg_template = 'file or directory at path "{path}" does not exist'
  195. class PathNotAFileError(_PathValueError):
  196. code = 'path.not_a_file'
  197. msg_template = 'path "{path}" does not point to a file'
  198. class PathNotADirectoryError(_PathValueError):
  199. code = 'path.not_a_directory'
  200. msg_template = 'path "{path}" does not point to a directory'
  201. class PyObjectError(PydanticTypeError):
  202. msg_template = 'ensure this value contains valid import path or valid callable: {error_message}'
  203. class SequenceError(PydanticTypeError):
  204. msg_template = 'value is not a valid sequence'
  205. class IterableError(PydanticTypeError):
  206. msg_template = 'value is not a valid iterable'
  207. class ListError(PydanticTypeError):
  208. msg_template = 'value is not a valid list'
  209. class SetError(PydanticTypeError):
  210. msg_template = 'value is not a valid set'
  211. class FrozenSetError(PydanticTypeError):
  212. msg_template = 'value is not a valid frozenset'
  213. class DequeError(PydanticTypeError):
  214. msg_template = 'value is not a valid deque'
  215. class TupleError(PydanticTypeError):
  216. msg_template = 'value is not a valid tuple'
  217. class TupleLengthError(PydanticValueError):
  218. code = 'tuple.length'
  219. msg_template = 'wrong tuple length {actual_length}, expected {expected_length}'
  220. def __init__(self, *, actual_length: int, expected_length: int) -> None:
  221. super().__init__(actual_length=actual_length, expected_length=expected_length)
  222. class ListMinLengthError(PydanticValueError):
  223. code = 'list.min_items'
  224. msg_template = 'ensure this value has at least {limit_value} items'
  225. def __init__(self, *, limit_value: int) -> None:
  226. super().__init__(limit_value=limit_value)
  227. class ListMaxLengthError(PydanticValueError):
  228. code = 'list.max_items'
  229. msg_template = 'ensure this value has at most {limit_value} items'
  230. def __init__(self, *, limit_value: int) -> None:
  231. super().__init__(limit_value=limit_value)
  232. class ListUniqueItemsError(PydanticValueError):
  233. code = 'list.unique_items'
  234. msg_template = 'the list has duplicated items'
  235. class SetMinLengthError(PydanticValueError):
  236. code = 'set.min_items'
  237. msg_template = 'ensure this value has at least {limit_value} items'
  238. def __init__(self, *, limit_value: int) -> None:
  239. super().__init__(limit_value=limit_value)
  240. class SetMaxLengthError(PydanticValueError):
  241. code = 'set.max_items'
  242. msg_template = 'ensure this value has at most {limit_value} items'
  243. def __init__(self, *, limit_value: int) -> None:
  244. super().__init__(limit_value=limit_value)
  245. class FrozenSetMinLengthError(PydanticValueError):
  246. code = 'frozenset.min_items'
  247. msg_template = 'ensure this value has at least {limit_value} items'
  248. def __init__(self, *, limit_value: int) -> None:
  249. super().__init__(limit_value=limit_value)
  250. class FrozenSetMaxLengthError(PydanticValueError):
  251. code = 'frozenset.max_items'
  252. msg_template = 'ensure this value has at most {limit_value} items'
  253. def __init__(self, *, limit_value: int) -> None:
  254. super().__init__(limit_value=limit_value)
  255. class AnyStrMinLengthError(PydanticValueError):
  256. code = 'any_str.min_length'
  257. msg_template = 'ensure this value has at least {limit_value} characters'
  258. def __init__(self, *, limit_value: int) -> None:
  259. super().__init__(limit_value=limit_value)
  260. class AnyStrMaxLengthError(PydanticValueError):
  261. code = 'any_str.max_length'
  262. msg_template = 'ensure this value has at most {limit_value} characters'
  263. def __init__(self, *, limit_value: int) -> None:
  264. super().__init__(limit_value=limit_value)
  265. class StrError(PydanticTypeError):
  266. msg_template = 'str type expected'
  267. class StrRegexError(PydanticValueError):
  268. code = 'str.regex'
  269. msg_template = 'string does not match regex "{pattern}"'
  270. def __init__(self, *, pattern: str) -> None:
  271. super().__init__(pattern=pattern)
  272. class _NumberBoundError(PydanticValueError):
  273. def __init__(self, *, limit_value: Union[int, float, Decimal]) -> None:
  274. super().__init__(limit_value=limit_value)
  275. class NumberNotGtError(_NumberBoundError):
  276. code = 'number.not_gt'
  277. msg_template = 'ensure this value is greater than {limit_value}'
  278. class NumberNotGeError(_NumberBoundError):
  279. code = 'number.not_ge'
  280. msg_template = 'ensure this value is greater than or equal to {limit_value}'
  281. class NumberNotLtError(_NumberBoundError):
  282. code = 'number.not_lt'
  283. msg_template = 'ensure this value is less than {limit_value}'
  284. class NumberNotLeError(_NumberBoundError):
  285. code = 'number.not_le'
  286. msg_template = 'ensure this value is less than or equal to {limit_value}'
  287. class NumberNotMultipleError(PydanticValueError):
  288. code = 'number.not_multiple'
  289. msg_template = 'ensure this value is a multiple of {multiple_of}'
  290. def __init__(self, *, multiple_of: Union[int, float, Decimal]) -> None:
  291. super().__init__(multiple_of=multiple_of)
  292. class DecimalError(PydanticTypeError):
  293. msg_template = 'value is not a valid decimal'
  294. class DecimalIsNotFiniteError(PydanticValueError):
  295. code = 'decimal.not_finite'
  296. msg_template = 'value is not a valid decimal'
  297. class DecimalMaxDigitsError(PydanticValueError):
  298. code = 'decimal.max_digits'
  299. msg_template = 'ensure that there are no more than {max_digits} digits in total'
  300. def __init__(self, *, max_digits: int) -> None:
  301. super().__init__(max_digits=max_digits)
  302. class DecimalMaxPlacesError(PydanticValueError):
  303. code = 'decimal.max_places'
  304. msg_template = 'ensure that there are no more than {decimal_places} decimal places'
  305. def __init__(self, *, decimal_places: int) -> None:
  306. super().__init__(decimal_places=decimal_places)
  307. class DecimalWholeDigitsError(PydanticValueError):
  308. code = 'decimal.whole_digits'
  309. msg_template = 'ensure that there are no more than {whole_digits} digits before the decimal point'
  310. def __init__(self, *, whole_digits: int) -> None:
  311. super().__init__(whole_digits=whole_digits)
  312. class DateTimeError(PydanticValueError):
  313. msg_template = 'invalid datetime format'
  314. class DateError(PydanticValueError):
  315. msg_template = 'invalid date format'
  316. class DateNotInThePastError(PydanticValueError):
  317. code = 'date.not_in_the_past'
  318. msg_template = 'date is not in the past'
  319. class DateNotInTheFutureError(PydanticValueError):
  320. code = 'date.not_in_the_future'
  321. msg_template = 'date is not in the future'
  322. class TimeError(PydanticValueError):
  323. msg_template = 'invalid time format'
  324. class DurationError(PydanticValueError):
  325. msg_template = 'invalid duration format'
  326. class HashableError(PydanticTypeError):
  327. msg_template = 'value is not a valid hashable'
  328. class UUIDError(PydanticTypeError):
  329. msg_template = 'value is not a valid uuid'
  330. class UUIDVersionError(PydanticValueError):
  331. code = 'uuid.version'
  332. msg_template = 'uuid version {required_version} expected'
  333. def __init__(self, *, required_version: int) -> None:
  334. super().__init__(required_version=required_version)
  335. class ArbitraryTypeError(PydanticTypeError):
  336. code = 'arbitrary_type'
  337. msg_template = 'instance of {expected_arbitrary_type} expected'
  338. def __init__(self, *, expected_arbitrary_type: Type[Any]) -> None:
  339. super().__init__(expected_arbitrary_type=display_as_type(expected_arbitrary_type))
  340. class ClassError(PydanticTypeError):
  341. code = 'class'
  342. msg_template = 'a class is expected'
  343. class SubclassError(PydanticTypeError):
  344. code = 'subclass'
  345. msg_template = 'subclass of {expected_class} expected'
  346. def __init__(self, *, expected_class: Type[Any]) -> None:
  347. super().__init__(expected_class=display_as_type(expected_class))
  348. class JsonError(PydanticValueError):
  349. msg_template = 'Invalid JSON'
  350. class JsonTypeError(PydanticTypeError):
  351. code = 'json'
  352. msg_template = 'JSON object must be str, bytes or bytearray'
  353. class PatternError(PydanticValueError):
  354. code = 'regex_pattern'
  355. msg_template = 'Invalid regular expression'
  356. class DataclassTypeError(PydanticTypeError):
  357. code = 'dataclass'
  358. msg_template = 'instance of {class_name}, tuple or dict expected'
  359. class CallableError(PydanticTypeError):
  360. msg_template = '{value} is not callable'
  361. class EnumError(PydanticTypeError):
  362. code = 'enum_instance'
  363. msg_template = '{value} is not a valid Enum instance'
  364. class IntEnumError(PydanticTypeError):
  365. code = 'int_enum_instance'
  366. msg_template = '{value} is not a valid IntEnum instance'
  367. class IPvAnyAddressError(PydanticValueError):
  368. msg_template = 'value is not a valid IPv4 or IPv6 address'
  369. class IPvAnyInterfaceError(PydanticValueError):
  370. msg_template = 'value is not a valid IPv4 or IPv6 interface'
  371. class IPvAnyNetworkError(PydanticValueError):
  372. msg_template = 'value is not a valid IPv4 or IPv6 network'
  373. class IPv4AddressError(PydanticValueError):
  374. msg_template = 'value is not a valid IPv4 address'
  375. class IPv6AddressError(PydanticValueError):
  376. msg_template = 'value is not a valid IPv6 address'
  377. class IPv4NetworkError(PydanticValueError):
  378. msg_template = 'value is not a valid IPv4 network'
  379. class IPv6NetworkError(PydanticValueError):
  380. msg_template = 'value is not a valid IPv6 network'
  381. class IPv4InterfaceError(PydanticValueError):
  382. msg_template = 'value is not a valid IPv4 interface'
  383. class IPv6InterfaceError(PydanticValueError):
  384. msg_template = 'value is not a valid IPv6 interface'
  385. class ColorError(PydanticValueError):
  386. msg_template = 'value is not a valid color: {reason}'
  387. class StrictBoolError(PydanticValueError):
  388. msg_template = 'value is not a valid boolean'
  389. class NotDigitError(PydanticValueError):
  390. code = 'payment_card_number.digits'
  391. msg_template = 'card number is not all digits'
  392. class LuhnValidationError(PydanticValueError):
  393. code = 'payment_card_number.luhn_check'
  394. msg_template = 'card number is not luhn valid'
  395. class InvalidLengthForBrand(PydanticValueError):
  396. code = 'payment_card_number.invalid_length_for_brand'
  397. msg_template = 'Length for a {brand} card must be {required_length}'
  398. class InvalidByteSize(PydanticValueError):
  399. msg_template = 'could not parse value and unit from byte string'
  400. class InvalidByteSizeUnit(PydanticValueError):
  401. msg_template = 'could not interpret byte unit: {unit}'
  402. class MissingDiscriminator(PydanticValueError):
  403. code = 'discriminated_union.missing_discriminator'
  404. msg_template = 'Discriminator {discriminator_key!r} is missing in value'
  405. class InvalidDiscriminator(PydanticValueError):
  406. code = 'discriminated_union.invalid_discriminator'
  407. msg_template = (
  408. 'No match for discriminator {discriminator_key!r} and value {discriminator_value!r} '
  409. '(allowed values: {allowed_values})'
  410. )
  411. def __init__(self, *, discriminator_key: str, discriminator_value: Any, allowed_values: Sequence[Any]) -> None:
  412. super().__init__(
  413. discriminator_key=discriminator_key,
  414. discriminator_value=discriminator_value,
  415. allowed_values=', '.join(map(repr, allowed_values)),
  416. )