action0-client

Backend-agnostic, fully typed HTTP API clients: describe your API once — as typed operations — and run it synchronously, on asyncio or on Twisted, just by plugging in a different backend. Built on action0-req (the request/response representation) and action0-url (the URL representation).

The same typed operation, driven by three different backends:

client = APIClient(RequestsBackend(), "https://api.example.com/v1")
item = client.send(GetItem(item_id=42))  # Item

client = APIClient(AsyncHttpxBackend(), "https://api.example.com/v1")
item = await client.send(GetItem(item_id=42))  # Awaitable[Item]

client = APIClient(TwistedBackend(), "https://api.example.com/v1")
deferred = client.send(GetItem(item_id=42))  # Deferred[Item]

(GetItem is an ordinary typed operation class, written once — the guide shows its definition.)

uv add "action0-client[httpx]"

Highlights:

  • One Client / APIClient, four execution models: the backend decides whether send() returns a value, an awaitable, a Twisted Deferred or a concurrent.futures.Future — and the type checker knows which, including the per-operation result type (Item, Awaitable[Item], Deferred[Item], Future[Item]).

  • One structural Backend protocol, generic over the execution model’s wrapper type — implement two methods and anything can drive the same clients, including execution models this library has never heard of (Client.send returns whatever your backend’s send returns). Built-in: requests, httpx (sync + async), aiohttp and Twisted — each behind an optional dependency — plus two stdlib-only ones: urllib and a thread-pool backend returning concurrent.futures.Future results.

  • Endpoints as typed dataclasses: Operation fixes method and path per class, the field specifiers of action0.client.fields place typed fields into query, headers, path templates or the JSON body.

  • Instrumentation Hooks (logging, metrics, tracing, request decoration) and uniform error translation into one exception family, in every execution model.

  • Batteries for testing API clients without a server: action0.client.testing ships recording stub backends for all three execution models.

  • Fully typed (checked with mypy strict, pyright and ty), Python 3.11+.

The action0 namespace is simply the one the author likes to use for personal projects.