Generating a client¶
One command reads the schema and writes the package:
action0-openapi petstore.json -o src/
src/petstore_client/__init__.py
src/petstore_client/client.py
src/petstore_client/errors.py
src/petstore_client/models.py
src/petstore_client/operations.py
src/petstore_client/py.typed
The package name (petstore_client) and the client class name
(PetstoreClient) are derived from the schema’s info.title; the
client’s default base URL comes from the schema’s first servers
entry (server variables at their defaults). A document without
top-level servers falls back to the servers declared on its paths
and operations — Open-Meteo’s specs are shaped like that — as long as
they all agree on their first URL; several distinct URLs leave the
client without a default (a warning names them), so callers pass
base_url themselves. All of it can be overridden:
action0-openapi petstore.yaml -o src/ \
--package-name zoo \
--client-name ZooClient \
--base-url https://zoo.example.com/v1
YAML schemas need the yaml extra (Installation). Schemas
split over several files ($ref: './components/geo.yaml#/...') are
loaded and bundled automatically — Supported schemas describes the
merge rules. Existing files are never overwritten unless you pass
--force.
The schema can also be an http(s) URL. The URL you name is fetched
directly, but files it references download only with your consent —
one [y/N] prompt per file, or all of them with --download:
action0-openapi https://example.com/api/openapi.yaml -o src/ --download
action0-openapi: downloading https://example.com/api/components/geo.yaml
src/example_client/__init__.py
...
For large APIs, --split-by-tag puts each OpenAPI tag’s operations
into a module of its own (operations_pets.py, operations_auth.py,
…; untagged operations stay in operations.py). The package root
re-exports everything either way, so user imports —
from petstore_client import ListPets — do not depend on the layout.
Constructs the generator flattens or skips (an unsupported security scheme, several request media types, …) are reported as warnings on stderr; constructs outside the supported subset stop the run with an error naming the schema location — Supported schemas lists both categories.
The generated code is meant to be checked in like hand-written
code: it is readable, fully typed (mypy strict, pyright and ty pass on
it), ruff-clean, and it depends only on
action0-client — not on
this package. Regenerate with --force when the schema changes, and
review the diff like any other change.
The same pipeline is available as a library — one function per stage:
from pathlib import Path
from action0.openapi import bundle_documents, generate_package, load_documents
from action0.openapi import parse_api, write_package
document, warnings = bundle_documents(load_documents("petstore.json"))
api = parse_api(document)
files = generate_package(api, client_name="PetstoreClient", schema_name="petstore.json")
write_package(files, Path("src/petstore_client"))
(For a schema that is known to be a single file, load_schema reads it
without following references, and its result can go straight into
parse_api.)