Per-package codegen tooling decision for @blockchain0x/python (pypi blockchain0x).
| Layer | Source | Style |
|---|---|---|
| Pydantic models | apps/backend/openapi/openapi.yaml |
codegen |
| HTTP transport | src/blockchain0x/_client.py |
handwritten |
| Webhook verifier | src/blockchain0x/webhooks.py |
handwritten |
| x402 client | (Phase C-7, separate package blockchain0x-x402 on pypi) |
handwritten |
| Resource methods | src/blockchain0x/resources/*.py |
hybrid (handwritten ergonomics over codegen models) |
| Errors | src/blockchain0x/_errors.py |
handwritten |
| Tests | tests/ |
handwritten |
Pure codegen produces transport clients that look great until you need retry, idempotency, structured errors, or the discriminated-union webhook verifier - then the generator gets in the way. Pure handwriting makes every new OpenAPI field a manual port.
The split:
- Codegen the closed-set DTOs: every request body, response body,
and error envelope shape that mirrors
components.schemasin the spec. These rarely have ergonomics worth preserving. - Handwrite the verbs: every resource method (
client.api_keys.list, etc.) is a thin call into the transport with a typed return - small enough that the typing benefit beats the codegen drift cost. - Always handwrite the operational pieces: transport, retry, idempotency, error mapping, webhook verifier, x402 client. These are what the SDK ships for; codegen them once and you regret it for the next 18 months.
Pick: datamodel-code-generator
configured with --output-model-type pydantic_v2.BaseModel.
Rationale:
- Pydantic v2 is the de facto Python type framework for HTTP APIs.
datamodel-code-generatorunderstands the OpenAPI 3.1 dialect this spec uses (type: [string, 'null']tuples).- Output is editable, type-stable, and round-trippable to JSON.
- The generated models live under
src/blockchain0x/_models/(single flat namespace) so resources canfrom .._models import ApiKeywithout knowing about codegen plumbing.
# from packages/sdk-python/
./codegen/regenerate.shThe script (lands with C-1 implementation work) reads
../../apps/backend/openapi/openapi.yaml and writes to
src/blockchain0x/_models/api.py. The CI workflow in Phase G opens a
PR per language whenever openapi.yaml changes.
openapi-python-clientproduces a full Client + Resource scaffold, not just types. The scaffold style does not match this SDK's ergonomics (no structured retry, no Idempotency-Key minting, opaque error handling).openapi-generator-cliwith-g pythonis heavier (Jinja templates in Java; slow CI). Its generated client is the same mismatch.- We want types-only, fast, Python-native.
datamodel-code-generatoris that.