The pieces

  • Request / Response (from action0-req) describe what is on the wire.

  • A backend performs the HTTP I/O — through whichever library and execution model you chose. Backends implement one structural protocol, Backend, generic over what send() wraps the response in (Backend[Response] is a sync backend, Backend[Awaitable[Response]] an asyncio one, …); the built-in ones live in action0.client.backends.

  • Client sends raw requests through a backend (“ring 0”).

  • Operation + APIClient describe an API: typed endpoint classes in, typed results out (“ring 1”).

The key idea: the backend decides the execution model, and the types follow it. With a sync backend send() returns the result, with an async backend an Awaitable, with a Twisted backend a Deferred — the same client and operation classes throughout, verified by mypy, pyright and ty.