exceptions.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. """
  2. Errors, oh no!
  3. """
  4. from __future__ import annotations
  5. from typing import TYPE_CHECKING, Any
  6. import attrs
  7. from referencing._attrs import frozen
  8. if TYPE_CHECKING:
  9. from referencing import Resource
  10. from referencing.typing import URI
  11. @frozen
  12. class NoSuchResource(KeyError):
  13. """
  14. The given URI is not present in a registry.
  15. Unlike most exceptions, this class *is* intended to be publicly
  16. instantiable and *is* part of the public API of the package.
  17. """
  18. ref: URI
  19. def __eq__(self, other: object) -> bool:
  20. if self.__class__ is not other.__class__:
  21. return NotImplemented
  22. return attrs.astuple(self) == attrs.astuple(other)
  23. def __hash__(self) -> int:
  24. return hash(attrs.astuple(self))
  25. @frozen
  26. class NoInternalID(Exception):
  27. """
  28. A resource has no internal ID, but one is needed.
  29. E.g. in modern JSON Schema drafts, this is the :kw:`$id` keyword.
  30. One might be needed if a resource was to-be added to a registry but no
  31. other URI is available, and the resource doesn't declare its canonical URI.
  32. """
  33. resource: Resource[Any]
  34. def __eq__(self, other: object) -> bool:
  35. if self.__class__ is not other.__class__:
  36. return NotImplemented
  37. return attrs.astuple(self) == attrs.astuple(other)
  38. def __hash__(self) -> int:
  39. return hash(attrs.astuple(self))
  40. @frozen
  41. class Unretrievable(KeyError):
  42. """
  43. The given URI is not present in a registry, and retrieving it failed.
  44. """
  45. ref: URI
  46. def __eq__(self, other: object) -> bool:
  47. if self.__class__ is not other.__class__:
  48. return NotImplemented
  49. return attrs.astuple(self) == attrs.astuple(other)
  50. def __hash__(self) -> int:
  51. return hash(attrs.astuple(self))
  52. @frozen
  53. class CannotDetermineSpecification(Exception):
  54. """
  55. Attempting to detect the appropriate `Specification` failed.
  56. This happens if no discernible information is found in the contents of the
  57. new resource which would help identify it.
  58. """
  59. contents: Any
  60. def __eq__(self, other: object) -> bool:
  61. if self.__class__ is not other.__class__:
  62. return NotImplemented
  63. return attrs.astuple(self) == attrs.astuple(other)
  64. def __hash__(self) -> int:
  65. return hash(attrs.astuple(self))
  66. @attrs.frozen # Because here we allow subclassing below.
  67. class Unresolvable(Exception):
  68. """
  69. A reference was unresolvable.
  70. """
  71. ref: URI
  72. def __eq__(self, other: object) -> bool:
  73. if self.__class__ is not other.__class__:
  74. return NotImplemented
  75. return attrs.astuple(self) == attrs.astuple(other)
  76. def __hash__(self) -> int:
  77. return hash(attrs.astuple(self))
  78. @frozen
  79. class PointerToNowhere(Unresolvable):
  80. """
  81. A JSON Pointer leads to a part of a document that does not exist.
  82. """
  83. resource: Resource[Any]
  84. def __str__(self) -> str:
  85. msg = f"{self.ref!r} does not exist within {self.resource.contents!r}"
  86. if self.ref == "/":
  87. msg += (
  88. ". The pointer '/' is a valid JSON Pointer but it points to "
  89. "an empty string property ''. If you intended to point "
  90. "to the entire resource, you should use '#'."
  91. )
  92. return msg
  93. @frozen
  94. class NoSuchAnchor(Unresolvable):
  95. """
  96. An anchor does not exist within a particular resource.
  97. """
  98. resource: Resource[Any]
  99. anchor: str
  100. def __str__(self) -> str:
  101. return (
  102. f"{self.anchor!r} does not exist within {self.resource.contents!r}"
  103. )
  104. @frozen
  105. class InvalidAnchor(Unresolvable):
  106. """
  107. An anchor which could never exist in a resource was dereferenced.
  108. It is somehow syntactically invalid.
  109. """
  110. resource: Resource[Any]
  111. anchor: str
  112. def __str__(self) -> str:
  113. return (
  114. f"'#{self.anchor}' is not a valid anchor, neither as a "
  115. "plain name anchor nor as a JSON Pointer. You may have intended "
  116. f"to use '#/{self.anchor}', as the slash is required *before each "
  117. "segment* of a JSON pointer."
  118. )