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.
 
 
 
 

190 lines
5.9 KiB

  1. """Pydantic-specific errors."""
  2. from __future__ import annotations as _annotations
  3. import re
  4. from typing import Any, ClassVar, Literal
  5. from typing_extensions import Self
  6. from typing_inspection.introspection import Qualifier
  7. from pydantic._internal import _repr
  8. from ._migration import getattr_migration
  9. from .version import version_short
  10. __all__ = (
  11. 'PydanticUserError',
  12. 'PydanticUndefinedAnnotation',
  13. 'PydanticImportError',
  14. 'PydanticSchemaGenerationError',
  15. 'PydanticInvalidForJsonSchema',
  16. 'PydanticForbiddenQualifier',
  17. 'PydanticErrorCodes',
  18. )
  19. # We use this URL to allow for future flexibility about how we host the docs, while allowing for Pydantic
  20. # code in the while with "old" URLs to still work.
  21. # 'u' refers to "user errors" - e.g. errors caused by developers using pydantic, as opposed to validation errors.
  22. DEV_ERROR_DOCS_URL = f'https://errors.pydantic.dev/{version_short()}/u/'
  23. PydanticErrorCodes = Literal[
  24. 'class-not-fully-defined',
  25. 'custom-json-schema',
  26. 'decorator-missing-field',
  27. 'discriminator-no-field',
  28. 'discriminator-alias-type',
  29. 'discriminator-needs-literal',
  30. 'discriminator-alias',
  31. 'discriminator-validator',
  32. 'callable-discriminator-no-tag',
  33. 'typed-dict-version',
  34. 'model-field-overridden',
  35. 'model-field-missing-annotation',
  36. 'config-both',
  37. 'removed-kwargs',
  38. 'circular-reference-schema',
  39. 'invalid-for-json-schema',
  40. 'json-schema-already-used',
  41. 'base-model-instantiated',
  42. 'undefined-annotation',
  43. 'schema-for-unknown-type',
  44. 'import-error',
  45. 'create-model-field-definitions',
  46. 'validator-no-fields',
  47. 'validator-invalid-fields',
  48. 'validator-instance-method',
  49. 'validator-input-type',
  50. 'root-validator-pre-skip',
  51. 'model-serializer-instance-method',
  52. 'validator-field-config-info',
  53. 'validator-v1-signature',
  54. 'validator-signature',
  55. 'field-serializer-signature',
  56. 'model-serializer-signature',
  57. 'multiple-field-serializers',
  58. 'invalid-annotated-type',
  59. 'type-adapter-config-unused',
  60. 'root-model-extra',
  61. 'unevaluable-type-annotation',
  62. 'dataclass-init-false-extra-allow',
  63. 'clashing-init-and-init-var',
  64. 'model-config-invalid-field-name',
  65. 'with-config-on-model',
  66. 'dataclass-on-model',
  67. 'validate-call-type',
  68. 'unpack-typed-dict',
  69. 'overlapping-unpack-typed-dict',
  70. 'invalid-self-type',
  71. 'validate-by-alias-and-name-false',
  72. ]
  73. class PydanticErrorMixin:
  74. """A mixin class for common functionality shared by all Pydantic-specific errors.
  75. Attributes:
  76. message: A message describing the error.
  77. code: An optional error code from PydanticErrorCodes enum.
  78. """
  79. def __init__(self, message: str, *, code: PydanticErrorCodes | None) -> None:
  80. self.message = message
  81. self.code = code
  82. def __str__(self) -> str:
  83. if self.code is None:
  84. return self.message
  85. else:
  86. return f'{self.message}\n\nFor further information visit {DEV_ERROR_DOCS_URL}{self.code}'
  87. class PydanticUserError(PydanticErrorMixin, TypeError):
  88. """An error raised due to incorrect use of Pydantic."""
  89. class PydanticUndefinedAnnotation(PydanticErrorMixin, NameError):
  90. """A subclass of `NameError` raised when handling undefined annotations during `CoreSchema` generation.
  91. Attributes:
  92. name: Name of the error.
  93. message: Description of the error.
  94. """
  95. def __init__(self, name: str, message: str) -> None:
  96. self.name = name
  97. super().__init__(message=message, code='undefined-annotation')
  98. @classmethod
  99. def from_name_error(cls, name_error: NameError) -> Self:
  100. """Convert a `NameError` to a `PydanticUndefinedAnnotation` error.
  101. Args:
  102. name_error: `NameError` to be converted.
  103. Returns:
  104. Converted `PydanticUndefinedAnnotation` error.
  105. """
  106. try:
  107. name = name_error.name # type: ignore # python > 3.10
  108. except AttributeError:
  109. name = re.search(r".*'(.+?)'", str(name_error)).group(1) # type: ignore[union-attr]
  110. return cls(name=name, message=str(name_error))
  111. class PydanticImportError(PydanticErrorMixin, ImportError):
  112. """An error raised when an import fails due to module changes between V1 and V2.
  113. Attributes:
  114. message: Description of the error.
  115. """
  116. def __init__(self, message: str) -> None:
  117. super().__init__(message, code='import-error')
  118. class PydanticSchemaGenerationError(PydanticUserError):
  119. """An error raised during failures to generate a `CoreSchema` for some type.
  120. Attributes:
  121. message: Description of the error.
  122. """
  123. def __init__(self, message: str) -> None:
  124. super().__init__(message, code='schema-for-unknown-type')
  125. class PydanticInvalidForJsonSchema(PydanticUserError):
  126. """An error raised during failures to generate a JSON schema for some `CoreSchema`.
  127. Attributes:
  128. message: Description of the error.
  129. """
  130. def __init__(self, message: str) -> None:
  131. super().__init__(message, code='invalid-for-json-schema')
  132. class PydanticForbiddenQualifier(PydanticUserError):
  133. """An error raised if a forbidden type qualifier is found in a type annotation."""
  134. _qualifier_repr_map: ClassVar[dict[Qualifier, str]] = {
  135. 'required': 'typing.Required',
  136. 'not_required': 'typing.NotRequired',
  137. 'read_only': 'typing.ReadOnly',
  138. 'class_var': 'typing.ClassVar',
  139. 'init_var': 'dataclasses.InitVar',
  140. 'final': 'typing.Final',
  141. }
  142. def __init__(self, qualifier: Qualifier, annotation: Any) -> None:
  143. super().__init__(
  144. message=(
  145. f'The annotation {_repr.display_as_type(annotation)!r} contains the {self._qualifier_repr_map[qualifier]!r} '
  146. f'type qualifier, which is invalid in the context it is defined.'
  147. ),
  148. code=None,
  149. )
  150. __getattr__ = getattr_migration(__name__)