errors.py 4.9 KB

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