API reference

Everything public is importable from the package root:

from action0.openapi import load_documents, bundle_documents, RefResolver, SchemaError
from action0.openapi import Api, Model, EnumModel, Field, OperationIR, Param, Body
from action0.openapi import SecurityScheme

Schema loading

Loading OpenAPI schema documents from disk or via http(s).

load_schema() reads a JSON or YAML file into the plain dict the rest of the pipeline works on and verifies it actually is an OpenAPI 3.x document. load_documents() additionally follows file references (./components/geo.yaml#/...) and loads every referenced file too, for bundle_documents() to merge; its schema source may also be an http(s) URL, and referenced files download — each one gated by the caller’s allow_download consent callback. JSON needs nothing beyond the stdlib; YAML needs PyYAML, installable as the yaml extra of this package — the import happens lazily, so JSON-only users never touch it.

class action0.openapi.loader.Documents(root, files)[source]

An OpenAPI document plus every file it references, loaded.

Keys are canonical file paths (absolute, symlinks and .. resolved) or http(s) URLs, so the same file referenced from two places is loaded — and later bundled — only once.

Parameters:
  • root (str) – the canonical path or URL of the root document

  • files (Mapping[str, dict[str, Any]]) – the decoded documents, canonical path/URL → document

action0.openapi.loader.load_schema(path)[source]

Read an OpenAPI 3.x document from a JSON or YAML file.

.yaml/.yml files are parsed as YAML, everything else is parsed as JSON first and — since JSON is a subset of YAML — as YAML if that fails.

Parameters:

path (Path | str) – the schema file

Return type:

dict[str, Any]

Returns:

the decoded document

Raises:

SchemaError – if the file cannot be decoded, YAML support is not installed, or the document is not an OpenAPI 3.0/3.1 schema

action0.openapi.loader.load_documents(source, *, allow_download=None)[source]

Load a schema and, recursively, every file its $refs point at.

The source may be a file path or an http(s) URL; naming a URL as the source is the consent to fetch it. Relative reference paths resolve against the file containing them — against its URL, for a downloaded document, which can therefore only ever reference further URLs, never local files. Referenced URLs were not named by the caller, so each one downloads only after allow_download approves it; without the callback, any referenced URL is an error. Referenced files are decoded like schemas but not validated as OpenAPI documents — component-only fragment files have no openapi version field. The loaded set is merged into a single document by bundle_documents().

Parameters:
  • source (Path | str) – the root schema file or URL

  • allow_download (Callable[[str], bool] | None, default: None) – called with each referenced URL; returning True permits the download

Return type:

Documents

Returns:

the loaded document set

Raises:

SchemaError – if any file cannot be read, downloaded or decoded, a download is not approved, the root is not an OpenAPI 3.0/3.1 schema, or a reference has an unsupported scheme

Multi-file bundling

Bundling multi-file OpenAPI documents into a single document.

OpenAPI schemas may reference sibling files, as in $ref: './components/geo.yaml#/components/schemas/Point'. The rest of the pipeline works on one document with local #/... pointers only, so bundle_documents() merges a loaded file set (see load_documents()) up front: referenced components are imported into the root document’s components sections — renamed on a name collision, with a warning — and references to anything that is not a component are inlined. The result parses exactly like a hand-bundled single file.

action0.openapi.bundle.bundle_documents(documents)[source]

Merge a loaded multi-file schema into one single-file document.

Every reference into another file is either imported — the target sits under a #/components/<section>/<Name> pointer, so it moves into the root document’s matching section, keeping its name unless that name is already taken — or inlined in place, when the target is not a component (a deep pointer, or a whole-file reference). A single-file document without file references is returned unchanged.

>>> from action0.openapi.loader import Documents
>>> documents = Documents(
...     root="/specs/zoo.json",
...     files={
...         "/specs/zoo.json": {
...             "openapi": "3.0.3",
...             "components": {"schemas": {"Cage": {"$ref": "./geo.json#/components/schemas/Point"}}},
...         },
...         "/specs/geo.json": {"components": {"schemas": {"Point": {"type": "object"}}}},
...     },
... )
>>> document, warnings = bundle_documents(documents)
>>> document["components"]["schemas"]["Cage"]
{'$ref': '#/components/schemas/Point'}
>>> document["components"]["schemas"]["Point"]
{'type': 'object'}
>>> warnings
[]
Parameters:

documents (Documents) – the loaded document set

Return type:

tuple[dict[str, Any], list[str]]

Returns:

the merged document and the bundling warnings (component renames)

Raises:

SchemaError – on broken pointers, references to files missing from the set, or circular references that cannot be represented locally

action0.openapi.bundle.referenced_files(document, *, base)[source]

List the canonical paths of the files a document references.

>>> referenced_files(
...     {"$ref": "./components/geo.yaml#/components/schemas/Point"},
...     base="/specs/zoo.yaml",
... )
['/specs/components/geo.yaml']
Parameters:
  • document (Mapping[str, Any]) – the decoded document

  • base (str) – the canonical path or URL of the file holding the document

Return type:

list[str]

Returns:

the referenced files (paths or URLs), in document order, without duplicates and without base itself

Raises:

SchemaError – on references with an unsupported scheme

action0.openapi.bundle.is_url(source)[source]

Say whether a document source is an http(s) URL.

>>> is_url("https://example.com/api.yaml"), is_url("./api.yaml")
(True, False)
Parameters:

source (str) – a schema path or URL

Return type:

bool

Returns:

whether it is an http(s) URL

Reference resolution

Resolution of local $ref pointers inside an OpenAPI document.

OpenAPI schemas reference shared definitions as JSON pointers like #/components/schemas/Pet. RefResolver looks such pointers up in the loaded document and follows chains of them, so the translation stage can work with plain schema objects. Only local references (into the same document) are supported — remote and file references raise SchemaError.

class action0.openapi.resolve.RefResolver(document)[source]

Looks up local $ref JSON pointers in one OpenAPI document.

Parameters:

document (Mapping[str, Any]) – the loaded schema document

lookup(ref)[source]

Return the node a local JSON pointer refers to.

>>> resolver = RefResolver({"components": {"schemas": {"Pet": {"type": "object"}}}})
>>> resolver.lookup("#/components/schemas/Pet")
{'type': 'object'}
Parameters:

ref (str) – the pointer, e.g. #/components/schemas/Pet

Return type:

Any

Returns:

the referenced node

Raises:

SchemaError – if the pointer is not local or does not resolve

deref(node)[source]

Follow a (chain of) $ref to the actual schema object.

A node without $ref is returned as-is, so this is safe to call on every schema-shaped node.

>>> resolver = RefResolver({"components": {"schemas": {"Pet": {"type": "object"}}}})
>>> resolver.deref({"$ref": "#/components/schemas/Pet"})
{'type': 'object'}
>>> resolver.deref({"type": "string"})
{'type': 'string'}
Parameters:

node (Mapping[str, Any]) – a schema node that may be a reference

Return type:

Mapping[str, Any]

Returns:

the referenced (or given) schema object

Raises:

SchemaError – on non-local, broken or circular references, or if the target is not an object

static ref_name(ref)[source]

Return the last pointer segment — the component’s name.

>>> RefResolver.ref_name("#/components/schemas/Pet")
'Pet'
Parameters:

ref (str) – the pointer

Return type:

str

Returns:

the unescaped final segment

Translation

Translating an OpenAPI 3.x document into the intermediate representation.

parse_api() walks a loaded schema document and produces the Api the emitter renders: the components/schemas become models and enums (inline schemas are synthesized into named models on the way), the paths become operations with parameters, body and response type, and the referenced securitySchemes become client credentials. Everything outside the supported subset raises SchemaError naming the offending schema location; lesser omissions (an unsupported security scheme, several request media types) are collected as warnings.

action0.openapi.parse.parse_api(document)[source]

Translate a loaded OpenAPI 3.x document into an Api.

Parameters:

document (Mapping[str, Any]) – the document, as returned by load_schema()

Return type:

Api

Returns:

the intermediate representation

Raises:

SchemaError – for constructs outside the supported subset

Intermediate representation

The intermediate representation (IR) between OpenAPI and generated code.

The translation stage turns a loaded OpenAPI document into one Api value — plain, frozen dataclasses that carry everything the code emitter needs and nothing else: models with their fields, enums, operations with parameters and body, and the security schemes. All names in the IR are the final Python names (classes PascalCase, fields snake_case, path templates rewritten to match the field names); the original schema spellings survive as the wire_name.

Keeping this layer independent of both the OpenAPI document shape and the emitted source text is deliberate: a future dynamic mode (building operation classes at import time instead of writing files) would consume the very same Api.

class action0.openapi.ir.Scalar(*values)[source]

The scalar types generated code distinguishes.

ANY = 'any'

free-form values that stay whatever the JSON decoder produced

class action0.openapi.ir.ScalarType(kind)[source]

A scalar type.

Parameters:

kind (Scalar)

class action0.openapi.ir.ArrayType(item)[source]

A JSON array — list[item] in generated code.

Parameters:

item (ScalarType | ArrayType | MapType | ModelType | EnumType | UnionType)

class action0.openapi.ir.MapType(value)[source]

A JSON object with additionalProperties only — dict[str, value] in generated code.

Parameters:

value (ScalarType | ArrayType | MapType | ModelType | EnumType | UnionType)

class action0.openapi.ir.ModelType(name)[source]

A reference to a generated model dataclass, by Python class name.

Parameters:

name (str)

class action0.openapi.ir.EnumType(name)[source]

A reference to a generated enum class, by Python class name.

Parameters:

name (str)

class action0.openapi.ir.UnionType(name, members)[source]

A reference to a generated union alias, by Python name.

The members ride along so the type logic (annotations, whether a conversion is needed at all) works without looking the union up.

Parameters:
action0.openapi.ir.TypeExpr: TypeAlias = 'ScalarType | ArrayType | MapType | ModelType | EnumType | UnionType'

any type generated code can express

class action0.openapi.ir.Field(name, wire_name, type, required, nullable=False, default=None, description=None)[source]

One property of a model, or one field of a request body.

Parameters:
  • name (str) – the Python field name

  • wire_name (str) – the property name in the JSON/form payload

  • type (ScalarType | ArrayType | MapType | ModelType | EnumType | UnionType) – the field’s type

  • required (bool) – whether the payload must contain the property

  • nullable (bool, default: False) – whether null is a legal payload value

  • default (object | None, default: None) – the schema’s default value (scalars only), or None when the schema declares none

  • description (str | None, default: None) – the schema’s description, for docstrings

class action0.openapi.ir.Model(name, fields, additional_field=None, description=None)[source]

One generated dataclass model.

Parameters:
  • name (str) – the Python class name

  • fields (tuple[Field, ...]) – the model’s fields; the ones rendered without a dataclass default (required and not nullable) come first

  • additional_field (Field | None, default: None) – the catch-all field collecting the payload keys not declared under properties (schemas combining properties with additionalProperties), or None when the schema declares no additional properties; its type is always a MapType, and its wire_name is empty — the catch-all has no single wire spelling

  • description (str | None, default: None) – the schema’s description, for the docstring

class action0.openapi.ir.EnumModel(name, base, members, description=None)[source]

One generated enum.Enum class.

Parameters:
  • name (str) – the Python class name

  • base (Scalar) – the scalar kind of the values (Scalar.STR or Scalar.INT)

  • members (tuple[tuple[str, str | int], ...]) – (member_name, value) pairs

  • description (str | None, default: None) – the schema’s description, for the docstring

class action0.openapi.ir.UnionCheck(*values)[source]

How one union member is recognized in a decoded payload.

JSON_TYPE = 'json-type'

an isinstance check against a JSON-level Python type

TAG = 'tag'

the discriminator property equals a tag value

KEY = 'key'

a required key only this member has is present

class action0.openapi.ir.UnionCase(member, check, value)[source]

One branch of a union’s dispatching converter.

Parameters:
class action0.openapi.ir.UnionModel(name, members, cases, discriminator=None, description=None)[source]

One generated union: a type alias plus a dispatching converter.

Parameters:
class action0.openapi.ir.ParamLocation(*values)[source]

Where an operation parameter is placed.

class action0.openapi.ir.Param(name, wire_name, location, type, required, nullable=False, default=None, join_with=None, description=None)[source]

One path, query or header parameter of an operation.

Parameters:
  • name (str) – the Python field name

  • wire_name (str) – the parameter name on the wire

  • location (ParamLocation) – where the parameter goes

  • type (ScalarType | ArrayType | MapType | ModelType | EnumType | UnionType) – the parameter’s type

  • required (bool) – whether the parameter must be sent

  • nullable (bool, default: False) – whether the schema allows null

  • default (object | None, default: None) – the schema’s default value (scalars only), or None when the schema declares none

  • join_with (str | None, default: None) – the separator joining an array parameter’s items into one key=value pair (a non-exploded style), or None for the default one-pair-per-item serialization

  • description (str | None, default: None) – the parameter’s description, for docstrings

class action0.openapi.ir.BodyKind(*values)[source]

How an operation’s request body is expressed as fields.

JSON_FIELDS = 'json-fields'

an inline JSON object schema, one json_field() per property

JSON_BODY = 'json-body'

a referenced/array/scalar JSON schema, one json_body() field

FORM_FIELDS = 'form-fields'

application/x-www-form-urlencoded, one form_field() per property

RAW_BODY = 'raw-body'

any other media type, one raw-bytes body() field

class action0.openapi.ir.Body(kind, fields=(), type=None, required=True, media_type=None)[source]

An operation’s request body.

Parameters:
class action0.openapi.ir.ErrorCase(status, exception, model, description=None)[source]

One documented non-2xx response, raised as a typed exception.

Parameters:
  • status (str) – the response key — a concrete status ("400"), a range ("4XX"/"5XX") or "default"

  • exception (str) – the generated exception class name; operations documenting the same status with the same model share it

  • model (str) – the class name of the model the error payload parses into

  • description (str | None, default: None) – the response’s description, for the check docstring

class action0.openapi.ir.ResponseKind(*values)[source]

What an operation’s success response parses into.

MODEL = 'model'

a JSON payload loaded into a typed value

NONE = 'none'

no content (e.g. 204) — the operation returns None

BYTES = 'bytes'

non-JSON content returned as raw bytes

class action0.openapi.ir.OperationIR(class_name, method, path_template, wire_path, params=(), body=None, response_kind=ResponseKind.NONE, response_type=None, errors=(), summary=None, description=None, tag=None)[source]

One generated operation class.

Parameters:
  • class_name (str) – the Python class name

  • method (str) – the HTTP method, uppercase

  • path_template (str) – the path with {placeholder} names already rewritten to the Python parameter names

  • wire_path (str) – the original path as spelled in the schema

  • params (tuple[Param, …], default: ()) – the path/query/header parameters

  • body (Body | None, default: None) – the request body, if any

  • response_kind (ResponseKind, default: <ResponseKind.NONE: 'none'>) – what the success response parses into

  • response_type (TypeExpr | None, default: None) – the parsed type (for ResponseKind.MODEL; None otherwise)

  • errors (tuple[ErrorCase, …], default: ()) – the documented non-2xx responses raised as typed exceptions (concrete statuses first, then ranges, then default — the order the generated check tests them in)

  • summary (str | None, default: None) – the schema’s summary, for the docstring

  • description (str | None, default: None) – the schema’s description, for the docstring

  • tag (str | None, default: None) – the operation’s first tags entry, if any — the grouping key when the generated package splits operations into per-tag modules

class action0.openapi.ir.SecurityKind(*values)[source]

The supported OpenAPI security scheme kinds.

class action0.openapi.ir.SecurityScheme(kind, param_name, wire_name=None)[source]

One security scheme, turned into client credentials.

Parameters:
  • kind (SecurityKind) – the scheme kind

  • param_name (str) – the Python name of the credential parameter on the generated client’s __init__ (e.g. token, api_key)

  • wire_name (str | None, default: None) – the header or query parameter carrying the credential (None for HTTP bearer/basic, which fix the Authorization header)

class action0.openapi.ir.Api(title, version, base_url=None, models=(), operations=(), security=(), warnings=())[source]

Everything the emitter needs to generate one client package.

Parameters:
  • title (str) – the schema’s info.title

  • version (str) – the schema’s info.version

  • base_url (str | None, default: None) – the default base URL from servers, if any

  • models (tuple[Model | EnumModel | UnionModel, ...], default: ()) – the models and enums, in schema order

  • operations (tuple[OperationIR, ...], default: ()) – the operations, in path order

  • security (tuple[SecurityScheme, ...], default: ()) – the security schemes becoming client credentials

  • warnings (tuple[str, ...], default: ()) – notes about constructs the translation flattened or skipped (printed by the CLI, documented in the generated code where possible)

Name mangling

Turning OpenAPI spellings into the Python names of the generated code.

OpenAPI documents name things in whatever style the API grew up with — camelCase properties, kebab-case headers, PascalCase or dotted component names. Generated code follows PEP 8: classes are PascalCase, fields snake_case, enum members UPPER_SNAKE. The functions here perform that conversion and keep the results valid: Python keywords and context-reserved names get a trailing underscore (the original spelling survives as the field’s wire name), identifiers that would start with a digit get a V/v_ prefix, and NameRegistry de-duplicates within one scope.

action0.openapi.names.RESERVED_OPERATION_FIELDS = frozenset({'accept', 'body', 'default_location', 'form_field', 'header', 'json_body', 'json_field', 'method', 'path', 'path_param', 'query'})

names an operation dataclass field must not use: the Operation ClassVars (reserved by action0-client) and the field specifiers the generated operations module imports at module level (a field binding one of these in the class body would shadow the specifier for every later field of the same class)

action0.openapi.names.class_name(raw)[source]

Turn a schema name into a PascalCase class name.

>>> class_name("pet-store")
'PetStore'
>>> class_name("petStatus")
'PetStatus'
>>> class_name("HTTPValidationError")
'HttpValidationError'
>>> class_name("APIs.guru")  # pluralized acronyms stay one word
'ApisGuru'
>>> class_name("get /v1/forecast")  # digits stay with their word
'GetV1Forecast'
>>> class_name("PokéAPI")  # accents are dropped, not word breaks
'PokeApi'
>>> class_name("1password")  # leading digit: prefixed
'V1password'
Parameters:

raw (str) – the name as spelled in the schema

Return type:

str

Returns:

a valid Python class name

action0.openapi.names.field_name(raw, *, reserved=())[source]

Turn a schema name into a snake_case field name.

>>> field_name("petId")
'pet_id'
>>> field_name("X-Request-Id")
'x_request_id'
>>> field_name("numAPIs")  # pluralized acronyms stay one word
'num_apis'
>>> field_name("GetV1ForecastResponse")  # digits stay with their word
'get_v1_forecast_response'
>>> field_name("SHA256Sum")
'sha256_sum'
>>> field_name("class")  # Python keyword
'class_'
>>> field_name("path", reserved=RESERVED_OPERATION_FIELDS)
'path_'
>>> field_name("1st")  # leading digit: prefixed
'v_1st'
Parameters:
Return type:

str

Returns:

a valid, non-reserved Python field name

action0.openapi.names.constant_name(raw)[source]

Turn an enum value into an UPPER_SNAKE member name.

>>> constant_name("on-sale")
'ON_SALE'
>>> constant_name("notAvailable")
'NOT_AVAILABLE'
>>> constant_name("1st")  # digit-led: enum members must not start with "_"
'V_1ST'
Parameters:

raw (str) – the enum value as spelled in the schema

Return type:

str

Returns:

a valid Python enum member name

action0.openapi.names.operation_class_name(operation_id, method, path)[source]

Name the operation class after the operationId, if there is one, and after method and path otherwise.

>>> operation_class_name("listPets", "get", "/pets")
'ListPets'
>>> operation_class_name(None, "get", "/pets/{petId}")
'GetPetsPetId'
Parameters:
  • operation_id (str | None) – the schema’s operationId, if any

  • method (str) – the HTTP method

  • path (str) – the path as spelled in the schema

Return type:

str

Returns:

a valid Python class name

action0.openapi.names.converter_name(model_class)[source]

Name the JSON-to-model converter function for a model class.

>>> converter_name("Pet")
'pet_from_json'
>>> converter_name("HttpError")
'http_error_from_json'
Parameters:

model_class (str) – the model’s Python class name

Return type:

str

Returns:

the converter function’s name

action0.openapi.names.properties_constant_name(model_class)[source]

Name the declared-properties set constant of a model with a catch-all additionalProperties field.

The constant holds the wire names of the declared properties; the model’s converter fills the catch-all field with every payload key outside the set.

>>> properties_constant_name("Pet")
'_PET_PROPERTIES'
>>> properties_constant_name("HttpError")
'_HTTP_ERROR_PROPERTIES'
Parameters:

model_class (str) – the model’s Python class name

Return type:

str

Returns:

the constant’s name

action0.openapi.names.path_placeholders(path)[source]

Return the {placeholder} names of a path template, in order.

>>> path_placeholders("/stores/{storeId}/pets/{petId}")
('storeId', 'petId')
Parameters:

path (str) – the path template

Return type:

tuple[str, ...]

Returns:

the placeholder names

Raises:

ValueError – if the template’s braces are malformed

action0.openapi.names.rewrite_path(path, renames)[source]

Rename the {placeholder}s of a path template.

Placeholder names must equal the Python names of the operation’s path_param() fields (action0-client validates that, and the specifier deliberately has no wire-name parameter), so the template is rewritten to the renamed fields.

>>> rewrite_path("/pets/{petId}", {"petId": "pet_id"})
'/pets/{pet_id}'
Parameters:
  • path (str) – the path template as spelled in the schema

  • renames (Mapping[str, str]) – schema spelling to Python name, per placeholder

Return type:

str

Returns:

the rewritten template

class action0.openapi.names.NameRegistry[source]

De-duplicates names within one scope (module, enum, class).

The first claim of a name gets it as-is, later claims of the same name get a numeric suffix:

>>> registry = NameRegistry()
>>> registry.claim("Pet")
'Pet'
>>> registry.claim("Pet")
'Pet2'
>>> registry.claim("Pet")
'Pet3'
claim(preferred)[source]

Return the preferred name, made unique within this registry.

Parameters:

preferred (str) – the name to claim

Return type:

str

Returns:

the name, or the first free numbered variant of it

Type mapping

Mapping between schema scalar types and the Python of the generated code.

Three views of one TypeExpr: scalar_type() builds the IR leaf from a schema’s type/format pair, annotation() renders the type annotation the generated code spells, converter_expr() renders the expression that turns a decoded JSON value into the typed value (with imports_for() supplying the imports the rendered text needs). The request direction needs no counterpart: action0-client serializes enums, dates and nested dataclasses on its own — only UUID request fields need a serialize=str argument, which the emitter adds.

action0.openapi.types.scalar_type(type_name, format_name)[source]

Build the scalar for a schema’s type/format pair.

>>> scalar_type("string", None)
ScalarType(kind=<Scalar.STR: 'str'>)
>>> scalar_type("string", "date-time")
ScalarType(kind=<Scalar.DATETIME: 'datetime'>)
>>> scalar_type(None, None)  # no type: any JSON value is fine
ScalarType(kind=<Scalar.ANY: 'any'>)
Parameters:
  • type_name (str | None) – the schema’s type (a scalar one — object and array are structural and handled by the translation stage)

  • format_name (str | None) – the schema’s format, if any

Return type:

ScalarType

Returns:

the scalar

Raises:

ValueError – if the type is not a scalar type

action0.openapi.types.annotation(t, *, optional=False)[source]

Render the type annotation generated code uses for a type.

>>> annotation(ArrayType(ModelType("Pet")))
'list[Pet]'
>>> annotation(MapType(ScalarType(Scalar.DATE)), optional=True)
'dict[str, datetime.date] | None'
Parameters:
Return type:

str

Returns:

the annotation text

action0.openapi.types.imports_for(t)[source]

Return the import statements a type’s annotation and converter need.

Imports of generated models and enums are not included — where they live relative to the rendered module is the emitter’s business.

>>> sorted(imports_for(MapType(ScalarType(Scalar.UUID))))
['import uuid']
Parameters:

t (ScalarType | ArrayType | MapType | ModelType | EnumType | UnionType) – the type

Return type:

frozenset[str]

Returns:

the import statements

action0.openapi.types.needs_conversion(t)[source]

Whether a decoded JSON value of this type needs converting at all.

Plain scalars come out of the JSON decoder ready to use; dates, UUIDs, enums and models need an expression around them.

>>> needs_conversion(ArrayType(ScalarType(Scalar.STR)))
False
>>> needs_conversion(ArrayType(EnumType("Status")))
True
Parameters:

t (ScalarType | ArrayType | MapType | ModelType | EnumType | UnionType) – the type

Return type:

bool

Returns:

whether converter_expr() is more than a pass-through

action0.openapi.types.converter_expr(t, source, *, _depth=0)[source]

Render the expression converting a decoded JSON value to a type.

>>> converter_expr(ScalarType(Scalar.STR), 'data["name"]')
'data["name"]'
>>> converter_expr(EnumType("Status"), 'data["status"]')
'Status(data["status"])'
>>> converter_expr(ArrayType(ModelType("Pet")), 'data["items"]')
'[pet_from_json(item) for item in data["items"]]'
Parameters:
Return type:

str

Returns:

the converting expression (source itself when nothing needs converting)

Command line interface

The action0-openapi command line interface.

One command: read an OpenAPI 3.x schema — a file or an http(s) URL, following references to other files, where each referenced download needs an interactive yes or --download — generate the client package, write it into the output directory. Expected input problems (SchemaError, an existing output without --force) are printed as one-line errors without a traceback; translation warnings go to stderr, the written files to stdout.

action0.openapi.cli.main(argv=None)[source]

Run the generator CLI.

Parameters:

argv (Sequence[str] | None, default: None) – the command line arguments (sys.argv[1:] when None)

Return type:

int

Returns:

the exit code — 0 on success, 1 on input errors

Package generation

Assembling and writing one generated client package.

generate_package() renders the whole package — __init__.py, models.py, operations.py, client.py, errors.py (when the schema documents error responses) and the py.typed marker — as a mapping of file names to file contents; write_package() puts it on disk. The default package and client class names are derived from the schema’s info.title by default_package_name() and default_client_name().

action0.openapi.generate.default_package_name(title)[source]

The default generated-package name for a schema title.

>>> default_package_name("Petstore")
'petstore_client'
Parameters:

title (str) – the schema’s info.title

Return type:

str

Returns:

the package name

action0.openapi.generate.default_client_name(title)[source]

The default client class name for a schema title.

>>> default_client_name("Petstore")
'PetstoreClient'
Parameters:

title (str) – the schema’s info.title

Return type:

str

Returns:

the class name

action0.openapi.generate.generate_package(api, *, client_name, schema_name, split_by_tag=False)[source]

Render one generated client package.

Parameters:
  • api (Api) – the intermediate representation

  • client_name (str) – the client class name

  • schema_name (str) – the schema file’s name, quoted in the header of every generated file

  • split_by_tag (bool, default: False) – put each OpenAPI tag’s operations into a module of its own (operations_<tag>.py; untagged operations stay in operations.py) instead of one shared operations.py

Return type:

dict[str, str]

Returns:

file name to file content

action0.openapi.generate.write_package(files, package_dir, *, force=False)[source]

Write a generated package to disk.

Parameters:
  • files (dict[str, str]) – file name to file content, as returned by generate_package()

  • package_dir (Path) – the package directory (created if missing)

  • force (bool, default: False) – overwrite existing files instead of refusing

Return type:

list[Path]

Returns:

the written paths, in file-name order

Raises:

FileExistsError – if a file exists and force is not set (nothing is written then)

Code emission

Rendering the intermediate representation as Python source text.

The emitter builds the generated modules with plain string assembly — no template engine — so the exact output is controlled in one place. The produced text is already in the shape ruff format and ruff check (with this repository’s isort settings) accept: import blocks in isort order with one import per line, two blank lines between top-level definitions, double quotes, magic trailing commas on multi-line calls, and lines within the 99-column limit (over-long field conversions are wrapped the way ruff format wraps them).

class action0.openapi.render.Lines[source]

An indentation-aware builder for one module’s source text.

write(text='')[source]

Append one line at the current indentation.

Parameters:

text (str, default: '') – the line’s text (empty for a blank line)

Return type:

None

indent()[source]

Increase the indentation by one level.

Return type:

None

dedent()[source]

Decrease the indentation by one level.

Return type:

None

property level: int

The current indentation level.

extend(other)[source]

Append another builder’s lines verbatim.

Parameters:

other (Lines) – the builder whose lines to append

Return type:

None

separate(blank_lines=2)[source]

Ensure the given number of blank lines before what comes next.

Parameters:

blank_lines (int, default: 2) – how many blank lines separate the blocks

Return type:

None

comment(text)[source]

Append a #: documentation comment at the current indentation.

Sphinx autodoc reads consecutive #: lines above an attribute as its documentation. Each line of the text is wrapped to the line-length limit; blank lines are dropped (they would render as stray empty comments between fields).

Parameters:

text (str) – the comment text

Return type:

None

docstring(text)[source]

Append a docstring at the current indentation.

A single short line becomes a one-line docstring, anything else a block. Triple quotes inside the text are defused.

Parameters:

text (str) – the docstring text

Return type:

None

text()[source]

Render the collected module text.

Return type:

str

Returns:

the source text, with a trailing newline

class action0.openapi.render.Imports[source]

Collects import statements and renders them in isort order.

The order replicates this repository’s ruff isort settings (one import per line, action0 first-party): the __future__ block, the stdlib block, the action0 block, and relative imports last — within each block all plain import X lines first, then the from X import ... lines, each alphabetically.

add(*statements)[source]

Collect import statements.

Parameters:

statements (str) – lines like import datetime or from typing import Any

Return type:

None

render(lines)[source]

Write the collected imports as isort-ordered blocks.

Parameters:

lines (Lines) – the module builder to write into

Return type:

None

action0.openapi.render.render_models(api, header)[source]

Render the models.py module: enums, dataclass models, and the JSON-to-model converter functions.

Parameters:
  • api (Api) – the intermediate representation

  • header (str) – the generated-by header comment line (without #)

Return type:

str

Returns:

the module’s source text

action0.openapi.render.render_operations(api, header, operations=None)[source]

Render one operations module: one operation class per endpoint.

Parameters:
  • api (Api) – the intermediate representation

  • header (str) – the generated-by header comment line (without #)

  • operations (Sequence[OperationIR] | None, default: None) – the operations to render into this module (all of the API’s when None — per-tag splitting passes subsets)

Return type:

str

Returns:

the module’s source text

action0.openapi.render.render_errors(api, header)[source]

Render the errors.py module: the JSON decoding helper and one APIError subclass per documented (status, error model) pair.

Parameters:
  • api (Api) – the intermediate representation (with at least one operation carrying error cases)

  • header (str) – the generated-by header comment line (without #)

Return type:

str

Returns:

the module’s source text

action0.openapi.render.render_client(api, header, client_name)[source]

Render the client.py module: the API client subclass with the base URL and the security schemes baked in.

Parameters:
  • api (Api) – the intermediate representation

  • header (str) – the generated-by header comment line (without #)

  • client_name (str) – the client class name

Return type:

str

Returns:

the module’s source text

action0.openapi.render.render_init(api, header, client_name, operation_modules=None)[source]

Render the generated package’s __init__.py: docstring and re-exports of the client, the models and the operations.

Parameters:
  • api (Api) – the intermediate representation

  • header (str) – the generated-by header comment line (without #)

  • client_name (str) – the client class name

  • operation_modules (Mapping[str, str] | None, default: None) – operation class name to the module it lives in (every class in operations when None — per-tag splitting passes the actual layout)

Return type:

str

Returns:

the module’s source text

Errors

The exception raised for OpenAPI documents this library cannot process.

Everything that goes wrong with the input — an unreadable schema file, an unsupported OpenAPI version, a broken or unsupported $ref, a construct outside the supported subset — raises SchemaError with a message meant for the person running the generator. Bugs in this library keep raising their natural exceptions.

exception action0.openapi.errors.SchemaError[source]

An OpenAPI document cannot be loaded or translated.

The message names the offending file, reference or schema location and, for deliberate limitations, what to do instead — it is meant to be printed as-is by the CLI, without a traceback.