A lightweight, spec-driven builder for API clients and controllers.
Project description
saronia
A lightweight, spec-driven builder for API clients and controllers.
Installation
# Base installation
pip install saronia
# With rnet client
pip install saronia[rnet]
# With aiohttp client
pip install saronia[aiohttp]
Features
- Declarative API controller syntax
- Type-safe request/response handling with
APIResult - Support for multiple HTTP clients (rnet, aiohttp, or custom)
- Comprehensive error handling with
StatusErrordecorator - Support for path parameters, query params, headers, JSON, form data, and file uploads
- Built on top of
msgspexfor fast serialization andkungfufor Result types
Quick Start
import asyncio
from uuid import UUID
from kungfu import Error
from msgspex import Model
from saronia import API, APIResult, get, post
# With rnet
from rnet import Client
from saronia import RnetClient
# Or with aiohttp
# from aiohttp import ClientSession
# from saronia import AiohttpClient
cool_api = API.endpoint("/coolapi/v1")
class Book(Model):
id: UUID
name: str
class ValidationError(Model):
message: str
class NotFoundError(Model):
message: str
class CreateBookDTO(Model):
id: UUID
name: str
@cool_api("/books")
class BooksController:
@get("/{book_id}")
async def get_book_by_id(self, book_id: UUID) -> APIResult[Book, ValidationError | NotFoundError]:
...
@post("/create", CreateBookDTO)
async def create_book(self) -> APIResult[Book, ValidationError | NotFoundError]:
...
books = BooksController()
async def main() -> None:
# Using rnet
client = Client()
cool_api.build(RnetClient(client, base_url="https://api.example.com"))
# Or using aiohttp
# async with ClientSession() as session:
# cool_api.build(AiohttpClient(session, base_url="https://api.example.com"))
book = (await books.get_book_by_id(UUID("12345678-1234-5678-1234-567812345678"))).unwrap()
print(f"Book: {book.name}")
match await books.create_book(id=UUID("87654321-4321-8765-4321-876543218765"), name="New Book"):
case Error(error):
print(f"Error: {error}")
asyncio.run(main())
Error Handling
saronia provides comprehensive error handling following OpenAPI best practices:
from http import HTTPStatus
from saronia import StatusError
# Associate error types with HTTP status codes
class NotFoundError(Model, StatusError[HTTPStatus.NOT_FOUND]):
message: str
resource: str
class ValidationError(Model, StatusError[HTTPStatus.BAD_REQUEST, HTTPStatus.UNPROCESSABLE_ENTITY]):
message: str
details: dict[str, list[str]] | None = None
# Use in controller
@get("/{book_id}")
async def get_book(self, book_id: UUID) -> APIResult[Book, NotFoundError | ValidationError]:
...
# Handle errors
result = await books.get_book(book_id)
match result:
case Error(api_error):
print(f"Status: {api_error.status}") # HTTPStatus enum
print(f"Path: {api_error.path}") # Request path
print(f"Request ID: {api_error.request_id}") # For tracing
print(f"Is client error: {api_error.is_client_error}") # 4xx
print(f"Is server error: {api_error.is_server_error}") # 5xx
# Pattern match on specific error types
match api_error.error:
case NotFoundError(message=msg):
print(f"Not found: {msg}")
case ValidationError(message=msg, details=details):
print(f"Validation failed: {msg}")
case _:
book = result.unwrap()
print(f"Success: {book.name}")
Custom HTTP Client
You can implement your own HTTP client by inheriting from ABCClient:
from saronia.client.abc import ABCClient
class MyCustomClient(ABCClient):
async def request(self, path, method, *, errors, response_type, ...):
# Your implementation
...
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
saronia-1.0.0.tar.gz
(22.2 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
saronia-1.0.0-py3-none-any.whl
(18.8 kB
view details)
File details
Details for the file saronia-1.0.0.tar.gz.
File metadata
- Download URL: saronia-1.0.0.tar.gz
- Upload date:
- Size: 22.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
232221dc99133259481eaa4d982cacd12093883159bc314865c9ae32152fd09e
|
|
| MD5 |
78d5b2f52ffbb8b51475753fb7e02022
|
|
| BLAKE2b-256 |
2215e39f087ce8a788644ba7d616ef86828bfb16cc6594946729a08a7e2806dd
|
File details
Details for the file saronia-1.0.0-py3-none-any.whl.
File metadata
- Download URL: saronia-1.0.0-py3-none-any.whl
- Upload date:
- Size: 18.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
939a0b6c144dc93a5584b3e306b035ee0309d2cf6ec82c6b4107cc87250a1759
|
|
| MD5 |
b9b68d542457425363ba05feb78abaeb
|
|
| BLAKE2b-256 |
011b50e1894d653678721232ba4be9e9d5b63ed6a25cba6ee7d086a9d3110fd8
|