Keyword-only parameters#
Every parameter on a generated function is keyword-only — every call site after the connection argument requires a bare *,:
await get_user(conn, id=1) # correct
await get_user(conn, 1) # TypeErrorExample SQL#
-- @name GetUser
-- @returns :one
SELECT * FROM users WHERE id = $1;
-- @name ListUsers
-- @returns :many
SELECT * FROM users ORDER BY id;
-- @name CreateUser
-- @returns :exec
INSERT INTO users (name, email) VALUES ($1, $2);Generated code shape#
Row types default to a frozen, slotted dataclass:
@dataclass(frozen=True, slots=True)
class User:
"""A row from the users table."""
id: int
name: str
email: strEach generated row and function carries a one-line docstring. Set row_type = "pydantic" or row_type = "msgspec" in scythe.toml to generate a Pydantic BaseModel or msgspec Struct instead — see Configuration.
Driver differences#
psycopg3 uses %(name)s named placeholders internally and a two-step fetch:
cur = await conn.execute("SELECT * FROM users WHERE id = %(id)s", {"id": id})
row = await cur.fetchone()asyncpg uses $N positional placeholders in the underlying SQL while keeping the generated function's parameters keyword-only, and enables direct row access by column name from the driver's own result type.
Type mappings#
| SQL Type | Python Type |
|---|---|
SERIAL/INTEGER |
int |
TEXT/VARCHAR |
str |
BOOLEAN |
bool |
UUID |
uuid.UUID |
NUMERIC |
decimal.Decimal |
TIMESTAMPTZ |
datetime.datetime |
JSON/JSONB |
dict[str, Any] |
| Nullable column | T | None |
Enums#
A PostgreSQL CREATE TYPE ... AS ENUM generates a Python enum.Enum subclass that also inherits from str, so generated values compare equal to their SQL string representation directly.
Related#
- Backend Overview — how the Python backend fits into scythe's broader manifest-driven pipeline.
- Simple CRUD Example — Python output alongside Rust, Go, and TypeScript for the same schema.