Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Python SDK

This is the README that tungsten generates for the Python target, shown for a small example API (Swagger's Petstore). Names change with your API; the structure does not.

Python SDK for the openapi API (1.0.0), generated by tungsten.

Every call returns a Result and never raises for API or transport errors. Arguments are validated before any request is sent. Python 3.12 or newer; models are Pydantic v2.

Install

pip install openapi-sdk

Create a client

from openapi_sdk import OpenapiClient

client = OpenapiClient(
    base_url="http://petstore.swagger.io/v1",
)

Read

from openapi_sdk import Ok

res = client.pets.list()
if isinstance(res, Ok):
    print(res.value)
else:
    print(res.error["category"], res.error["remediation"])

The async client has the same methods, awaited:

import asyncio

from openapi_sdk import AsyncOpenapiClient

async def main() -> None:
    async with AsyncOpenapiClient(base_url="http://petstore.swagger.io/v1") as client:
        res = await client.pets.list()
        print(res.ok)

asyncio.run(main())

unwrap(res) (from openapi_sdk import unwrap) returns the value or raises TungstenError with the envelope, for code that prefers exceptions.

Change something, safely

Mutations are never retried blindly. When the operation takes an idempotency key, generate it once, persist it with your intent, and reuse it on every retry.

import uuid

key = str(uuid.uuid4())
res = client.pets.create(opts={"idempotency_key": key})

Models and optional fields

Request and response bodies are Pydantic models (from openapi_sdk.models import ...). A field that may be left out defaults to UNSET (exported by this package), which is not sent; None sends JSON null. model_dump(mode="json", by_alias=True) gives the wire form. Fields whose wire name is not a Python name are renamed: a keyword or a name the model needs gets _ appended (type_, json_) and a leading digit gets field_ in front (field_2fa); the wire name is used on the wire.

Errors

A failed call returns Err(error), where error is the diagnostic envelope:

{
  "status": "error",
  "category": "VALIDATION_FAILED",
  "operation": "<operation id>",
  "http_status": null,
  "code": null,
  "failed_parameter": "<args path>",
  "received_value": null,
  "expected": "<what was expected>",
  "remediation": "<what to do next>",
  "retryable": "never",
  "retry_after_ms": null,
  "next_action": null,
  "request_id": null,
  "trace": { "attempts": 0 }
}

Branch on category and retryable: same_key_only means retry only with the same idempotency key, after_remediation means act first (fix the input, or check whether an unknown outcome took effect: without a key a repeat can apply it twice), and OUTCOME_UNKNOWN means the request may have been applied. A success response without a body has value=None.

Extending

openapi_sdk/custom/__init__.py is yours: it is created once, never overwritten, and imported as openapi_sdk.custom.