Coverage for src/lanraragi_api/error.py: 56%
27 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-22 23:20 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-22 23:20 +0000
1from typing import Any
4class APIError(Exception):
5 """Base exception for all API client errors."""
8class APIRequestError(APIError):
9 """Raised when a request cannot be sent to the server.
11 It covers transport level failures such as a refused connection or a
12 timeout. HTTP error statuses are reported through ``APIHttpError`` instead.
13 The message names the class of the underlying ``requests`` exception when
14 one is available.
16 Attributes:
17 url: Absolute URL the request was sent to.
18 """
20 def __init__(self, url: str, error_type: str | None = None):
21 suffix = f" ({error_type})" if error_type else ""
22 super().__init__(f"Request to {url} failed{suffix}")
23 self.url: str = url
26class APIHttpError(APIError):
27 """Raised when the server answers with an unexpected status code.
29 Attributes:
30 status_code: HTTP status code returned by the server.
31 url: Absolute URL the request was sent to.
32 """
34 def __init__(self, status_code: int, url: str):
35 super().__init__(f"HTTP {status_code} for {url}")
36 self.status_code: int = status_code
37 self.url: str = url
40class APIResponseDecodeError(APIError):
41 """Raised when a response body cannot be turned into the expected model.
43 It is raised for a body that is not valid JSON for a JSON endpoint, and for
44 a JSON payload that does not match the pydantic model of the endpoint.
46 Attributes:
47 url: Absolute URL the request was sent to.
48 """
50 def __init__(self, url: str, message: str):
51 super().__init__(f"Failed to parse response from {url}: {message}")
52 self.url: str = url
55class APIOperationError(APIError):
56 """Raised when an operation endpoint reports a failure.
58 Only raised when raising is enabled, either through the client-level
59 ``raise_on_operation_error`` setting or through the ``raise_on_failure``
60 argument of a single call. Otherwise the failed operation is returned to the
61 caller like any other result.
63 Attributes:
64 operation: Name of the operation reported by the server.
65 message: Error message reported by the server, if it was kept.
66 status_code: HTTP status code of the response, if one was returned.
67 payload: Raw response payload, if the client was configured to keep it.
68 """
70 def __init__(
71 self,
72 operation: str,
73 message: str | None,
74 status_code: int | None = None,
75 payload: dict[str, Any] | None = None,
76 ):
77 error_message = message or "operation failed without an error message"
78 prefix = f"Operation '{operation}' failed"
79 if status_code is not None:
80 prefix = f"{prefix} with HTTP {status_code}"
81 super().__init__(f"{prefix}: {error_message}")
82 self.operation: str = operation
83 self.message: str | None = message
84 self.status_code: int | None = status_code
85 self.payload: dict[str, Any] | None = payload