Senzo
Senzo is a Python library for generating typed API clients from OpenAPI 3.x specifications. Unlike other client generators, Senzo is designed to support a number of HTTP clients (aiohttp, httpx, requests) for synchronous/async clients as well as several dataclass-like libraries such as Pydantic and msgspec.
Note: Senzo is still in development and is not yet ready for production use.
Using Senzo
Senzo is designed to be used programmatically through the Python package as opposed to a CLI tool. One can write a simple Python script in order to generate your client package. For example, if you wanted to build an API client that utilized Pydantic models for serialization and httpx for HTTP requests, you could do the following:
import json
import senzo
from senzo.backends.dataclass.pydantic import PydanticBackend
from senzo.backends.http.httpx import HttpxBackend
with open("openapi.json") as f:
spec = json.load(f)
tree = senzo.generate_tree(
spec,
package_name="my_api",
dataclass_backend=PydanticBackend(),
http_backend=HttpxBackend(async_mode=True),
)
senzo.write_package(tree, output_dir="./my_api")
Senzo is designed to make it easy to generate synchronous and asynchronous clients for your API. For example, if you wanted to generate a synchronous client in addition to the asynchronous client, you could do the following:
import json
import senzo
from senzo.backends.dataclass.pydantic import PydanticBackend
from senzo.backends.http.httpx import HttpxBackend
with open("openapi.json") as f:
spec = json.load(f)
tree = senzo.generate_tree(
spec,
package_name="my_api",
dataclass_backend=PydanticBackend(),
http_backend=HttpxBackend(async_mode=True),
)
senzo.write_package(tree, output_dir="./my_api")
tree = senzo.generate_tree(
spec,
package_name="my_sync_api",
dataclass_backend=PydanticBackend(),
http_backend=HttpxBackend(async_mode=False),
)
senzo.write_package(tree, output_dir="./my_sync_api")
The generated output is a small importable package (e.g. my_api/) containing:
client.py(HTTP client methods)types/models.py(schema models)_base.py(runtime helpers: errors, response wrapper, optional pagination/websocket support)py.typed(PEP 561 marker)
Use the generated client
Exact method names depend on operationId and tag_style.
from my_api import Client
async with Client(base_url="https://api.example.com") as client:
result = await client.some_operation(...)
Configuration
TagStyle
Controls how operations are organized:
TagStyle.FLAT(default): oneClientclass with all methodsTagStyle.FLAT_PREFIXED: oneClient, methods prefixed with tagTagStyle.GROUPED:Clientexposes sub-clients per tag (uses the first tag only)
import senzo
tree = senzo.generate_tree(spec, tag_style=senzo.TagStyle.GROUPED, ...)
EnumStyle (inline enums)
Given an inline enum in your OpenAPI spec:
status:
type: string
enum: ["pending", "approved", "rejected"]
| Style | Generated Type |
|---|---|
EnumStyle.LITERAL (default) |
Literal["pending", "approved", "rejected"] |
EnumStyle.ENUM |
enum.Enum class |
EnumStyle.STR_ENUM |
StrEnum class (Python 3.11+, recommended) |
EnumStyle.STR |
str (no type safety) |
Controls how inline OpenAPI enums (schemas with "enum": [...] but no named component) are represented in type annotations.
from senzo import generate_tree, EnumStyle
tree = generate_tree(spec, enum_style=EnumStyle.LITERAL, ...)
Named enum schemas under components/schemas are generated by the selected dataclass backend.
Backends
Dataclass (model) backends
senzo.backends.dataclass.pydantic.PydanticBackendsenzo.backends.dataclass.msgspec.MsgspecBackend
These backends control:
- how schema objects are rendered (Pydantic models vs msgspec structs)
- how JSON encoding/decoding code is emitted into the client
HTTP backends
senzo.backends.http.httpx.HttpxBackend(async_mode=...)(sync or async)senzo.backends.http.aiohttp.AiohttpBackend()(async only)senzo.backends.http.requests.RequestsBackend()(sync only)
Pagination (optional)
Senzo can generate an iterator helper <operation>_iter for operations that match a token-based pagination pattern.
Enable it via:
- generator config:
pagination={operation_id: {...}}, or - per-operation OpenAPI extension:
x-senzo-pagination: {...}
Fields:
items(default"items"): response property containing the item listnext_token(default"next_page"): response property containing the next tokentoken_param(default"page_token"): query parameter name for the page tokenlimit_param(default"limit"): query parameter name for the page size
Limitations:
- requires
token_paramandlimit_paramquery parameters to exist - requires a success response type with
itemsandnext_tokenproperties - does not paginate operations with a request body
WebSocket (optional; aiohttp backend only)
WebSocket endpoints are generated only when:
- the OpenAPI operation has
x-websocket: true, and - the selected HTTP backend supports websockets (currently
AiohttpBackend)
Message types can be declared via x-websocket-messages:
paths:
/ws/chat:
get:
operationId: connect_chat
x-websocket: true
x-websocket-messages:
send:
$ref: "#/components/schemas/ChatMessage"
receive:
$ref: "#/components/schemas/ServerEvent"
Hooks (generation-time; optional)
Senzo can bake calls to user-provided hook functions into the generated client. Hooks are configured at generation time using HooksConfig and are referenced by import path.
Supported hook slots:
pre_hook(inner, request, info) -> requestpost_hook(inner, response, info) -> responseon_error(inner, error, info) -> Noneon_result(inner, result, info) -> result
Acknowledgements
Senzo is heavily influenced by Progenitor, an OpenAPI client generator for Rust developed by Oxide.
Metadata
Release files for senzo 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| senzo-0.1.0.tar.gz | 410.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| senzo-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 472.1 kB
Release files / senzo-0.1.0.tar.gz
| Download URL | senzo-0.1.0.tar.gz |
|---|---|
| Size | 410.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3fd8cf48ee7673a507f70b5e3b50159493360ec1877a00ae78af25c2c55e4770
|
|
BLAKE2b-256 checksum How to use checksums |
647108e69328e5b0a49ca99d254d2720e6dee5fecd66e7437952f72613877a42
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / senzo-0.1.0-py3-none-any.whl
| Download URL | senzo-0.1.0-py3-none-any.whl |
|---|---|
| Size | 61.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a9d8c74f7f03e8f4db8878427594fc5eef6761b3dc7815f31936bd090cb3f87b
|
|
BLAKE2b-256 checksum How to use checksums |
5e868f031c497aea358c0f4ba8e68f9f23b13b8e0ffb7096585474aef56874a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for senzo 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.