
okapipy¶
Turn a flat OpenAPI 3.x document into a typed, hierarchical Python client — without losing your hand-written code on every spec change.
Why okapipy?¶
Every Python team that consumes a third-party REST API ends up writing the same wrapper twice: once to talk to the network, once to make the result feel like Python. The first half is mechanical and boring (URLs, JSON, headers, retries, pagination) and the second half is where the value is (domain methods, business logic, integration tests). Most generators only solve the first half — they hand you a flat list of operations, one method per OpenAPI path, and you spend the next quarter hand-wrapping them into something usable.
okapipy does both. It reads your OpenAPI 3.x document and recovers the
structure the spec is too flat to express: plurals become
collections, {id} segments become resources, verbs become
actions, and folder-style prefixes (/auth, /commerce,
/settings) become namespaces. The result is a client where
client.commerce.orders["ord_42"].submit() reads like the API it
represents — not client.commerce_orders_submit(id="ord_42") like it
came out of a Postman export.
The other half okapipy gets right is regeneration. Generated code is
emitted in two layers: a base/ tree the generator owns and rewrites on
every run, and a thin user layer the generator emits once and then
leaves alone forever. You add custom methods, swap transports, wire
authentication, override request bodies — all in plain Python
subclasses, no DSL, no protected-region markers. When the upstream spec
changes a year from now, you re-run one command, the base/ tree
catches up, and your code is exactly where you left it.
A 30-second tour¶
pip install okapipy
okapipy spec generate https://api.example.com/openapi.json \
--output ./my-client \
--package acme.commerce \
--client-class CommerceClient
from acme.commerce import CommerceClient
with CommerceClient(base_url="https://api.example.com") as client:
# Iterate a collection — pagination handled by the configured strategy.
for order in client.commerce.orders.filter(status="open").page_size(50):
print(order.id, order.total)
# Resources are looked up with [id], not (id).
order = client.commerce.orders["ord_42"].retrieve()
# Sub-collections, sub-resources — they walk the tree naturally.
new_line = client.commerce.orders["ord_42"].lines.create(
body={"sku": "SKU-1", "qty": 2},
)
# Actions live where they belong: under the resource that owns them.
client.commerce.orders["ord_42"].submit.run()
Async? Same shape, Async-prefixed:
from acme.commerce import AsyncCommerceClient
async with AsyncCommerceClient(base_url="...") as client:
async for order in client.commerce.orders:
...
What's in the box¶
-
Structural parser
Lifts a flat OpenAPI 3.x spec into a hierarchical tree using POS tagging plus a small heuristic registry. Override anything with
x-okapipy-*extensions or a project-local rules file. -
Two-layer code generation
A regenerated
base/layer holds wiring, transport, and Pydantic models. A one-shot user layer holds your overrides. Re-running the generator never touches your code. -
Pluggable strategies
Pagination, filter, and sort encodings are duck-typed Protocols with sensible built-ins (offset/limit, page/cursor, RFC 5988, JSON:API, …). Bring your own when the API does something custom.
-
Hookable templates
Every generated file is rendered from a Jinja template you can override per project — change the file header, the docstring style, the model layout, or the project skeleton. The packaged defaults work out of the box.
-
Lossless regeneration
Re-run
okapipy spec generatewhenever the spec moves. Your user code is left strictly alone; new namespaces and collections show up as drift warnings telling you exactly which line to add. -
Modern Python only
Python 3.12+, Pydantic v2,
httpx. Type-checked undermypy --stricton the parser and generator. Sync and async clients, side by side, sharing the same tree.
Get going in five minutes¶
- Install.
pip install okapipy(oruv add okapipy). - Pre-warm the spaCy model.
okapipy nlp fetch en— ~12 MB, one-time cost. - Sanity-check your spec.
okapipy spec parse openapi.yamlprints a structural tree and a counts panel; if any classifications look off, fix them in the rules file (see Rules and extensions) before you generate. - Generate.
okapipy spec generate openapi.yaml --output ./client --package acme.commerce --client-class CommerceClient. - Use it.
from acme.commerce import CommerceClient. The full surface — every collection, resource, action, sub-namespace — is already wired through the user-layer subclasses; you only edit them when you want to specialize.
That's the whole loop. There is no step 6.
Status¶
okapipy is in active development (beta). The parser is stable and ships today; the generator is implemented and emits a runnable client project with sync + async surfaces, vendored runtime, manifest-driven drift detection, and full template customization. The customer-facing API is still settling — pin to a specific version until 1.0.
License¶
okapipy is released under the Apache License 2.0. You're free to use it commercially, modify it, and redistribute it; please keep the copyright notice and the license text intact.