Generate pydantic v2 BaseModel classes from schema-salad definitions
Project description
schema-salad-plus-pydantic
Generate pydantic v2 BaseModel classes,
TypeScript interfaces, and Effect Schema
modules from schema-salad
definitions.
What it does
Schema-salad defines record types, enums, inheritance, and unions in YAML.
This tool reads those definitions and emits a Python module of pydantic
BaseModel classes, a TypeScript module of interfaces, or an Effect Schema
TypeScript module with runtime validation -- all conforming to the schema.
Key features:
- Proper pydantic inheritance -- abstract bases declare fields, children inherit them, multiple inheritance works naturally.
- Schema annotations for types schema-salad can't express natively:
pydantic:type-- override the generated type annotation (e.g.dict[str, NativeStep])pydantic:alias-- set a Field alias for JSON keys that differ from the Python namepydantic:discriminator_field/pydantic:discriminator_map-- discriminated unions
- Enums -- multi-symbol enums become
str, Enumclasses; single-symbol enums becomeLiteral["value"]with auto-defaults. - Forward references --
model_rebuild()for all classes,from __future__ import annotations. - Permissive by default --
extra="allow",populate_by_name=True. - TypeScript output --
--format=typescriptemits interfaces, string union enums, and type guard functions for discriminated unions. - Effect Schema output --
--format=effect-schemaemitsSchema.Structdefinitions with runtime validation viaSchema.decodeUnknownSync(), discriminated unions, and type guards.
Installation
pip install schema-salad-plus-pydantic
Or with uv:
uv pip install schema-salad-plus-pydantic
Usage
CLI
Generate pydantic models from a schema-salad YAML file:
schema-salad-plus-pydantic generate schema.yml -o models.py
Pass --strict to emit models with extra="forbid" (reject unknown JSON keys); the default is permissive extra="allow".
Generate TypeScript interfaces:
schema-salad-plus-pydantic generate schema.yml --format typescript -o models.ts
Generate Effect Schema TypeScript (with runtime validation):
schema-salad-plus-pydantic generate schema.yml --format effect-schema -o models.ts
Or write to stdout:
schema-salad-plus-pydantic generate schema.yml > models.py
Python API
from io import StringIO
from schema_salad_plus_pydantic.orchestrate import generate_from_schema
buf = StringIO()
generate_from_schema("path/to/schema.yml", buf)
code = buf.getvalue()
# Or write directly to a file
with open("models.py", "w") as f:
generate_from_schema("path/to/schema.yml", f)
# Optional: strict=True emits models with extra="forbid" (unknown keys rejected)
with open("models_strict.py", "w") as f:
generate_from_schema("path/to/schema.yml", f, strict=True)
# Generate TypeScript interfaces
with open("models.ts", "w") as f:
generate_from_schema("path/to/schema.yml", f, output_format="typescript")
# Generate Effect Schema TypeScript (runtime validation)
with open("models.ts", "w") as f:
generate_from_schema("path/to/schema.yml", f, output_format="effect-schema")
Using the generated models
import json
from generated_models import MyRecord # the module you generated
# Validate a dict
obj = MyRecord.model_validate({"field": "value", "count": 42})
# Validate from JSON
with open("data.json") as f:
obj = MyRecord.model_validate(json.load(f))
# Access fields
print(obj.field)
print(obj.count)
# Serialize back to dict/JSON
print(obj.model_dump())
print(obj.model_dump_json(indent=2))
Schema annotations
Add pydantic:* keys to schema-salad field definitions to control the
generated type annotations. Requires a pydantic namespace declaration:
$namespaces:
pydantic: "https://example.org/pydantic#"
$graph:
- name: MyRecord
type: record
fields:
- name: steps
type: Any?
pydantic:type: "dict[str, Step]"
- name: format_version
type: string
pydantic:alias: "format-version"
- name: creator
type: Any?
pydantic:type: "list[Person | Organization] | None"
pydantic:discriminator_field: "class"
pydantic:discriminator_map: '{"Person": "Person", "Organization": "Organization"}'
TypeScript output
With --format=typescript, the same schema annotations produce TypeScript
interfaces with mapped types:
| Python / pydantic | TypeScript |
|---|---|
dict[str, Step] |
Record<string, Step> |
list[Person | Organization] |
Array<Person | Organization> |
Literal["value"] |
"value" (string literal) |
int, float |
number |
str |
string |
Discriminated unions emit type guard functions:
export function isPerson(v: Person | Organization): v is Person {
return v?.class === "Person";
}
Effect Schema output
With --format=effect-schema, the tool generates TypeScript using
Effect Schema which provides both
compile-time types and runtime validation:
| Python / pydantic | Effect Schema |
|---|---|
dict[str, Step] |
Schema.Record({ key: Schema.String, value: StepSchema }) |
list[Person | Organization] |
Schema.Array(Schema.Union(PersonSchema, OrganizationSchema)) |
Literal["value"] |
Schema.Literal("value") |
int, float |
Schema.Number |
str |
Schema.String |
| enum (multi-symbol) | Schema.Literal("a", "b", "c") |
| optional field | Schema.optional(T) |
| inheritance | ...ParentSchema.fields spread |
Records become Schema.Struct definitions with derived type aliases:
import { Schema } from "effect"
export const MyRecordSchema = Schema.Struct({
name: Schema.optional(Schema.Union(Schema.Null, Schema.String)),
status: Schema.optional(Schema.Union(Schema.Null, StatusEnumSchema)),
})
export type MyRecord = typeof MyRecordSchema.Type
// Runtime validation
const record = Schema.decodeUnknownSync(MyRecordSchema)({
name: "example",
status: "active",
})
Circular references (e.g. recursive schemas) are handled automatically
via Schema.suspend.
Development
Setup with uv:
uv sync --group test --group lint --group mypy
Run checks:
make test # pytest
make lint # ruff + black
make mypy # type checking
Releasing
See RELEASE_CHECKLIST.md. Quick version:
make add-history # generate PR acknowledgements in HISTORY.rst
make release # tag, build, push (triggers PyPI publish via GitHub Actions)
License
MIT -- see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file schema_salad_plus_pydantic-0.1.8.tar.gz.
File metadata
- Download URL: schema_salad_plus_pydantic-0.1.8.tar.gz
- Upload date:
- Size: 28.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8da404402a53faed2cd7c789fc3effd3b5ef05d64416d8da7793520e15810d5e
|
|
| MD5 |
2f331e7e446c90f03127669ddb714ce7
|
|
| BLAKE2b-256 |
31a654978cd1afb1f69da70532af489df049fef87b1c225b8f44b7edc3d481f9
|
Provenance
The following attestation bundles were made for schema_salad_plus_pydantic-0.1.8.tar.gz:
Publisher:
deploy.yaml on jmchilton/schema-salad-plus-pydantic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
schema_salad_plus_pydantic-0.1.8.tar.gz -
Subject digest:
8da404402a53faed2cd7c789fc3effd3b5ef05d64416d8da7793520e15810d5e - Sigstore transparency entry: 1236007586
- Sigstore integration time:
-
Permalink:
jmchilton/schema-salad-plus-pydantic@3a15c8c55f1c50519c49c274fb0c117150179197 -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/jmchilton
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yaml@3a15c8c55f1c50519c49c274fb0c117150179197 -
Trigger Event:
push
-
Statement type:
File details
Details for the file schema_salad_plus_pydantic-0.1.8-py3-none-any.whl.
File metadata
- Download URL: schema_salad_plus_pydantic-0.1.8-py3-none-any.whl
- Upload date:
- Size: 25.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
135bdbc7c16d6078596a0f6fa5da96c530b3e159715eee03824e57208a47b641
|
|
| MD5 |
354f9b7d78ebf15b67173cdcdc3f3586
|
|
| BLAKE2b-256 |
49fcf89aefb5a6c5e88ab875b53e62e5cae4972f67ba00482ff061835229ed98
|
Provenance
The following attestation bundles were made for schema_salad_plus_pydantic-0.1.8-py3-none-any.whl:
Publisher:
deploy.yaml on jmchilton/schema-salad-plus-pydantic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
schema_salad_plus_pydantic-0.1.8-py3-none-any.whl -
Subject digest:
135bdbc7c16d6078596a0f6fa5da96c530b3e159715eee03824e57208a47b641 - Sigstore transparency entry: 1236007598
- Sigstore integration time:
-
Permalink:
jmchilton/schema-salad-plus-pydantic@3a15c8c55f1c50519c49c274fb0c117150179197 -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/jmchilton
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yaml@3a15c8c55f1c50519c49c274fb0c117150179197 -
Trigger Event:
push
-
Statement type: