Skip to main content

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 StatusError decorator
  • Support for path parameters, query params, headers, JSON, form data, and file uploads
  • Built on top of msgspex for fast serialization and kungfu for 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


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)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

saronia-1.0.0-py3-none-any.whl (18.8 kB view details)

Uploaded Python 3

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

Hashes for saronia-1.0.0.tar.gz
Algorithm Hash digest
SHA256 232221dc99133259481eaa4d982cacd12093883159bc314865c9ae32152fd09e
MD5 78d5b2f52ffbb8b51475753fb7e02022
BLAKE2b-256 2215e39f087ce8a788644ba7d616ef86828bfb16cc6594946729a08a7e2806dd

See more details on using hashes here.

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

Hashes for saronia-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 939a0b6c144dc93a5584b3e306b035ee0309d2cf6ec82c6b4107cc87250a1759
MD5 b9b68d542457425363ba05feb78abaeb
BLAKE2b-256 011b50e1894d653678721232ba4be9e9d5b63ed6a25cba6ee7d086a9d3110fd8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page