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.