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