Exceptions#
Lima-API provides a comprehensive exception system that allows you to handle HTTP errors and validation issues in a structured way. All exceptions in Lima-API are built on top of the base LimaException
class.
Base Exception Classes#
LimaException#
- class lima_api.exceptions.LimaException(detail: str | None = None, status_code: codes | int | None = None, content: bytes | None = None, request: Request | None = None, response: Response | None = None)[source]#
Bases:
Exception
- model: T | None = None#
- __init__(detail: str | None = None, status_code: codes | int | None = None, content: bytes | None = None, request: Request | None = None, response: Response | None = None)[source]#
- detail: str = ''#
- property http_request: Request | None#
- property http_response: Response | None#
- property status_code: codes | int | None#
- property content: bytes | None#
- json(default: ~typing.Any | None = <class 'dict'>)[source]#
Return the JSON-decoded content of the response.
If the content is None, return the provided default value. The default value can be a callable, in which case it is called to obtain the return value.
- Parameters:
default – The default value to return if content is None.
- Returns:
The JSON-decoded content or the default value.
- Raises:
json.JSONDecodeError – If the content cannot be decoded.
LimaException
is the base exception class for all Lima-API exceptions. It provides rich information about HTTP requests and responses, making it easier to debug and handle errors.
Key Features#
HTTP Context: Access to the original HTTP request and response objects
Status Code: Automatic extraction of HTTP status codes
Content Handling: Built-in methods to parse response content as JSON or custom objects
Flexible Initialization: Can be initialized with various combinations of parameters
Usage Examples#
import lima_api
try:
# Your API call here
result = await client.some_api_call()
except lima_api.LimaException as e:
print(f"Status Code: {e.status_code}")
print(f"Error Detail: {e.detail}")
print(f"Response Content: {e.json()}")
# Access the original HTTP objects
if e.http_request:
print(f"Request URL: {e.http_request.url}")
if e.http_response:
print(f"Response Headers: {e.http_response.headers}")
ValidationError#
- class lima_api.exceptions.ValidationError(detail: str | None = None, status_code: codes | int | None = None, content: bytes | None = None, request: Request | None = None, response: Response | None = None)[source]#
Bases:
LimaException
- detail: str = 'Validation error'#
ValidationError
is a specialized exception for handling validation errors that occur during request processing or response parsing.
Usage Examples#
import lima_api
from pydantic import ValidationError as PydanticValidationError
try:
# API call that might have validation issues
result = await client.create_user(invalid_data)
except lima_api.ValidationError as e:
print(f"Validation failed: {e}")
# The underlying pydantic error is available via __cause__
if isinstance(e.__cause__, PydanticValidationError):
for error in e.__cause__.errors():
print(f"Field: {error['loc']}, Error: {error['msg']}")
Exception Handling Patterns#
Response Mapping#
Lima-API allows you to map specific HTTP status codes to custom exception classes using the response_mapping
feature:
import lima_api
class CustomNotFoundError(lima_api.LimaException):
detail = "Resource not found"
class CustomAuthError(lima_api.LimaException):
detail = "Authentication failed"
class MyApiClient(lima_api.LimaApi):
base_url = "https://api.example.com"
response_mapping = {
404: CustomNotFoundError,
401: CustomAuthError,
}
@lima_api.get("/users/{user_id}")
async def get_user(self, user_id: int) -> dict:
pass
# Usage
with MyApiClient() as client:
try:
user = await client.get_user(999)
except CustomNotFoundError:
print("User not found!")
except CustomAuthError:
print("Authentication required!")
Method-Level Exception Mapping#
You can also define response mapping at the method level:
@lima_api.get(
"/sensitive-data",
response_mapping={
403: CustomPermissionError,
429: CustomRateLimitError,
}
)
async def get_sensitive_data(self) -> dict:
pass
Global Exception Handling#
For global exception handling, you can catch the base LimaException
:
async def safe_api_call():
try:
return await client.some_method()
except lima_api.LimaException as e:
# Log the error with full context
logger.error(
"API call failed",
extra={
"status_code": e.status_code,
"detail": e.detail,
"url": e.http_request.url if e.http_request else None,
"response_content": e.json() if e.content else None,
}
)
# Re-raise or handle as needed
raise
Best Practices#
Create Meaningful Exception Classes#
class UserNotFoundError(lima_api.LimaException):
detail = "User not found"
class UserAlreadyExistsError(lima_api.LimaException):
detail = "User already exists"
class InsufficientPermissionsError(lima_api.LimaException):
detail = "Insufficient permissions to perform this action"
Use Response Models for Structured Error Handling#
from pydantic import BaseModel
class ErrorResponse(BaseModel):
error_code: str
message: str
details: dict = {}
class ApiError(lima_api.LimaException):
model = ErrorResponse
Read errors ussing response
function#
async def safe_api_call():
try:
return await client.some_method()
except ApiError as e:
error = e.response(default=None)
if isinstanceof(error, ErrorResponse):
...
elif error is None:
print("Empty body")
else:
print(f"Unexpected error {error}")
raise
Implement Retry Logic with Exception Handling#
@lima_api.get(
"/retry-data",
response_mapping={
403: CustomPermissionError,
429: CustomRateLimitError,
}
retry_mapping={429: lima_api.retry_processors.RetryAfterProcessor},
)
async def get_retry_data(self) -> dict:
pass
5. Testing Exception Scenarios#
For testing we recoment mock the client funtion directly result, not the http response body.