Source code for imednet.errors.api
"""API-level errors."""
from typing import Any
from imednet.utils.secrets import redact_sensitive_payload as _redact_sensitive_value
from .base import ImednetError
[docs]class ApiError(ImednetError):
"""Raised for generic API errors (non-2xx HTTP status codes).
Attributes:
status_code: HTTP status code returned by the API.
response: Parsed JSON or raw text of the error response.
"""
[docs] def __init__(
self, response: dict[str, Any] | str | Any, status_code: int | None = None
) -> None:
"""Initialize an API error.
Args:
response: The error response from the API.
status_code: Optional HTTP status code.
"""
sanitized_response = _redact_sensitive_value(response)
message_arg = str(sanitized_response)
parsed_message = None
code = None
status = None
if isinstance(sanitized_response, dict):
if "message" in sanitized_response:
parsed_message = str(sanitized_response["message"])
elif "error" in sanitized_response:
parsed_message = str(sanitized_response["error"])
if "code" in sanitized_response:
try: # noqa: SIM105
code = int(sanitized_response["code"])
except (ValueError, TypeError):
pass
if "status" in sanitized_response:
status = str(sanitized_response["status"])
super().__init__(message_arg, status_code=status_code, code=code, status=status)
self.response = sanitized_response
self.message = parsed_message if parsed_message is not None else message_arg
def __str__(self) -> str:
"""Return a string representation of the error, including status and response.
Returns:
str: The formatted error message.
"""
base = super().__str__()
details = []
if self.status_code is not None:
details.append(f"Status Code: {self.status_code}")
if self.response:
details.append(f"Response: {self.response}")
if details:
return f"{base} ({', '.join(details)})"
return base
[docs]class AuthenticationError(ApiError):
"""Raised when authentication to the API fails (HTTP 401)."""
[docs]class AuthorizationError(ApiError):
"""Raised when access to the API is forbidden (HTTP 403)."""
[docs]class NotFoundError(ApiError):
"""Raised when a requested resource is not found (HTTP 404)."""
[docs]class RateLimitError(ApiError):
"""Raised when the API rate limit is exceeded (HTTP 429)."""
[docs]class ServerError(ApiError):
"""Raised when the API returns a server error (HTTP 5xx)."""
[docs]class UnauthorizedError(AuthenticationError):
"""Raised for HTTP 401 unauthorized errors."""
[docs]class ForbiddenError(AuthorizationError):
"""Raised for HTTP 403 forbidden errors."""
[docs]class ConflictError(ApiError):
"""Raised for HTTP 409 conflict errors."""