Skip to content

castiron

A schema→typed-code compiler for Python.

Point castiron at a schema source — a Supabase URL, your SQL migrations, or a live database — and get typed models (and, soon, a typed client for tables, views, and RPCs). A check mode fails CI when your committed generated code drifts from the schema.

castiron gen --from https://abcdefgh.supabase.co --emit pydantic
castiron: read 6 tables, 1 enum and 4 functions from https://abcdefgh.supabase.co/rest/v1/
castiron: wrote schema.py (14.2 kB)

No database connection. No driver. No connection string.

Get started

Pre-alpha

castiron is on PyPI and installable, but it is young and moving fast — APIs may change between releases. What is documented here is what ships today: the gen and check commands, the OpenAPI/PostgREST source, and the Pydantic emitter. It is the successor to supabase-pydantic.

Architecture

Pluggable sources parse a schema into one formalized Schema IR; pluggable emitters turn the IR into typed code. check re-emits in memory and fails if the committed output has drifted from the schema.

flowchart LR
    S1["OpenAPI / PostgREST"] --> IR
    S2["SQL migrations<br/>(planned)"] --> IR
    S3["Live database<br/>(planned)"] --> IR
    IR["Schema IR<br/>one typed, formal model"] --> E1["Pydantic v2"]
    IR --> E2["SQLAlchemy<br/>(planned)"]
    IR --> E3["Typed Supabase client<br/>(planned)"]
    IR --> CHK["check<br/>drift guard in CI"]

What works today

Piece Status
castiron gen — the CLI, with a [tool.castiron] project config shipped
OpenAPI/PostgREST source (a Supabase URL, a PostgREST root, or a saved JSON document) shipped
Pydantic v2 emitter — Row / Insert / Update / operational models, enums, FK relationships shipped
Byte-stable, deterministic output, lint-clean as emitted shipped
SQL-migration and live-database sources planned
SQLAlchemy emitter, typed Supabase client planned
castiron check — the drift guard, exit 3 on drift shipped

Honest by design

castiron's OpenAPI source needs no credentials and pays a real price for it: unique and check constraints, identity columns, exact integer widths below bigint, and function return types are simply not in the document it reads. castiron does not guess at them — it documents them, warns when one of them is about to change your output, and points at the live-database path for when you need the rest.

Read What the OpenAPI source can and cannot see before you trust a generated constraint.

Install

uv add cast-iron       # or: pip install cast-iron
castiron --version     # the command has no hyphen

You install cast-iron; you run castiron

The hyphen belongs to the distribution name and nothing else. The command, the import package (import castiron), and this repository all stay unhyphenated — the same ordinary split as pip install python-dateutilimport dateutil, or pip install scikit-learnimport sklearn. PyPI does not allow castiron as a distribution name, so this is permanent rather than a stopgap.

Working on castiron itself? Run it from a checkout instead.

Why "castiron"

Cast iron is durable, low-maintenance, and does one job for decades. It also puns on type casting — casting an untyped schema into hard, checked types.