Configuration¶
Schema Gen is configured using a Python configuration file that provides type safety and flexibility.
Configuration File¶
The configuration file is a Python file (typically .schema-gen.config.py) that defines a config variable:
from schema_gen import Config
config = Config(
input_dir="schemas",
output_dir="generated",
targets=[
# Python ecosystem
"pydantic",
"sqlalchemy",
"dataclasses",
"typeddict",
"pathway",
# TypeScript/JavaScript
"zod",
# Schema formats
"jsonschema",
"graphql",
"protobuf",
"avro",
# JVM languages
"jackson",
"kotlin",
],
# Target-specific settings
pydantic={
"use_enum": True,
"extra": "forbid",
},
)
Core Settings¶
input_dir: str¶
Directory containing your schema definitions.
Default: "schemas"
output_dir: str¶
Directory where generated code will be written.
Default: "generated"
targets: List[str]¶
List of code generation targets to run.
Available targets:
- "pydantic" - Pydantic v2 models
- "sqlalchemy" - SQLAlchemy ORM models
- "zod" - Zod validation schemas (TypeScript)
- "pathway" - Pathway schemas (planned)
Default: ["pydantic"]
Target-Specific Settings¶
Pydantic Settings¶
Configure Pydantic model generation with the pydantic dictionary:
config = Config(
targets=["pydantic"],
pydantic={
# Model configuration
"use_enum": True, # Use Enum for Literal types
"extra": "forbid", # Forbid extra fields
"validate_assignment": True, # Validate on assignment
"arbitrary_types_allowed": False, # Allow arbitrary types
# Import settings
"imports": ["from myproject.types import CustomType"], # Additional imports
# Template customization
"template_dir": "templates/", # Custom template directory
# File naming
"model_suffix": "_models", # File suffix (user_models.py)
"class_naming": "PascalCase", # Class naming convention
},
)
Schema Variants¶
Schema variants allow you to generate multiple Pydantic models from a single schema definition, each containing only a specific subset of fields. This is useful for creating different models for different use cases (API requests, responses, public views, etc.).
How Variants Work:
Variants are defined directly in your schema files using a Variants class, not in the configuration:
@Schema
class User:
"""User schema for the application"""
id: int = Field(primary_key=True, description="Unique identifier")
name: str = Field(max_length=100, description="User's full name")
email: str = Field(format="email", description="User's email address")
age: Optional[int] = Field(default=None, description="User's age")
created_at: datetime = Field(auto_now_add=True, description="Creation timestamp")
class Variants:
create_request = ["name", "email", "age"] # For user creation
update_request = ["name", "email", "age"] # For user updates
public_response = ["id", "name", "age", "created_at"] # Public API
full_response = ["id", "name", "email", "age", "created_at"] # Admin API
Generated Models:
- Base model:
User(contains all fields) - Variant models:
UserCreateRequest,UserUpdateRequest,UserPublicResponse,UserFullResponse
Important Notes:
- Variants are controlled entirely by the
Variantsclass in your schema definition - Each variant becomes a separate Pydantic class with only the specified fields
- Only the base model includes custom validators and methods; variants are clean data structures
- All variants are generated automatically when you run
schema-gen generate
Available Pydantic options:
| Option | Type | Default | Description |
|---|---|---|---|
use_enum |
bool |
True |
Convert Literal types to Enums |
extra |
str |
"forbid" |
Extra field handling ("allow", "forbid", "ignore") |
validate_assignment |
bool |
True |
Validate field assignments |
arbitrary_types_allowed |
bool |
False |
Allow arbitrary Python types |
imports |
List[str] |
[] |
Additional import statements |
template_dir |
str |
None |
Custom Jinja2 template directory |
model_suffix |
str |
"_models" |
Generated file suffix |
class_naming |
str |
"PascalCase" |
Class naming convention |
SQLAlchemy Settings¶
Configure SQLAlchemy ORM model generation:
config = Config(
targets=["sqlalchemy"],
sqlalchemy={
"use_declarative": True, # Use declarative base
"base_class": "Base", # Base class name
"table_naming": "snake_case", # Table naming convention
"relationship_loading": "lazy", # Relationship loading strategy
"include_metadata": True, # Include Column metadata
"generate_relationships": True, # Generate relationship mappings
},
)
Available SQLAlchemy options:
| Option | Type | Default | Description |
|---|---|---|---|
use_declarative |
bool |
True |
Use declarative base class |
base_class |
str |
"Base" |
Base class name for models |
table_naming |
str |
"snake_case" |
Table naming convention |
relationship_loading |
str |
"lazy" |
Default relationship loading strategy |
include_metadata |
bool |
True |
Include Column metadata and constraints |
generate_relationships |
bool |
True |
Generate relationship mappings |
Zod Settings¶
Configure Zod schema generation for TypeScript:
config = Config(
targets=["zod"],
zod={
"strict": True, # Append .strict() to z.object() schemas
"coerce": False, # Use z.coerce.date() instead of z.string().date()
},
)
Available Zod options:
| Option | Type | Default | Description |
|---|---|---|---|
strict |
bool |
False |
Append .strict() to z.object() schemas (rejects unknown keys) |
coerce |
bool |
False |
Use z.coerce.date() / z.coerce.string() for date/datetime fields instead of z.string().date() |
Type mapping highlights:
| Python Type | Zod Output |
|---|---|
str |
z.string() |
int |
z.number().int() |
float |
z.number() |
bool |
z.boolean() |
datetime |
z.string().datetime() |
list[T] |
z.array(<zod_type>) |
dict[str, T] |
z.record(<zod_type>) |
dict[str, Any] |
z.record(z.any()) |
Optional[T] |
<zod_type>.optional() |
Literal["a"] |
z.literal("a") |
Nested @Schema |
SchemaNameSchema (cross-file import) |
Enum subclass |
z.enum([...]) (inline) |
Pathway Settings (Planned)¶
config = Config(
targets=["pathway"],
pathway={
"column_type": "pw.Column", # Column type to use
"table_type": "pw.Table", # Table type to use
"optional_handling": "Union", # How to handle Optional types
},
)
Environment-Specific Configuration¶
Multiple Config Files¶
Use different configuration files for different environments:
# .schema-gen.dev.config.py
from schema_gen import Config
config = Config(
input_dir="schemas",
output_dir="generated",
targets=["pydantic"],
pydantic={
"validate_assignment": True, # Strict validation for development
"extra": "forbid",
},
)
# .schema-gen.prod.config.py
from schema_gen import Config
config = Config(
input_dir="schemas",
output_dir="generated",
targets=["pydantic", "sqlalchemy"], # More targets for production
pydantic={
"validate_assignment": False, # Less strict for performance
"extra": "ignore",
},
)
Use with CLI:
schema-gen generate --config .schema-gen.dev.config.py
schema-gen generate --config .schema-gen.prod.config.py
Environment Variables¶
Reference environment variables in your config:
import os
from schema_gen import Config
config = Config(
input_dir=os.getenv("SCHEMA_INPUT_DIR", "schemas"),
output_dir=os.getenv("SCHEMA_OUTPUT_DIR", "generated"),
targets=os.getenv("SCHEMA_TARGETS", "pydantic").split(","),
)
Conditional Configuration¶
Use Python logic for conditional configuration:
import os
from schema_gen import Config
# Detect environment
is_development = os.getenv("ENV") == "development"
is_production = os.getenv("ENV") == "production"
config = Config(
input_dir="schemas",
output_dir="generated",
# More targets in production
targets=["pydantic", "sqlalchemy"] if is_production else ["pydantic"],
pydantic={
# Strict validation in development
"validate_assignment": is_development,
"extra": "forbid" if is_development else "ignore",
},
)
Validation and Type Safety¶
The configuration system provides full type safety and validation:
from schema_gen import Config
# This will raise a validation error
config = Config(targets=["invalid_target"]) # ❌ Error: unknown target
# This will provide IDE autocomplete and type checking
config = Config(
input_dir="schemas", # ✅ Type: str
targets=["pydantic"], # ✅ Type: List[str], valid values
pydantic={
"extra": "forbid", # ✅ Type: str, valid enum value
"use_enum": True, # ✅ Type: bool
},
)
Configuration Reference¶
Full Example¶
"""Complete configuration example"""
import os
from schema_gen import Config
config = Config(
# Core settings
input_dir="schemas",
output_dir="generated",
targets=["pydantic", "sqlalchemy", "zod"],
# Pydantic settings
pydantic={
# Model behavior
"use_enum": True,
"extra": "forbid",
"validate_assignment": True,
"arbitrary_types_allowed": False,
# Code generation
"imports": [
"from myproject.types import CustomEnum",
"from decimal import Decimal",
],
"model_suffix": "_models",
"class_naming": "PascalCase",
# Template customization
"template_dir": None, # Use built-in templates
},
# SQLAlchemy settings
sqlalchemy={
"use_declarative": True,
"base_class": "Base",
"table_naming": "snake_case",
"generate_relationships": True,
},
# Zod settings
zod={
"generate_types": True,
"strict_mode": True,
"export_schemas": True,
"file_extension": ".ts",
},
# Future: Pathway settings
pathway={
"column_type": "pw.Column",
"table_type": "pw.Table",
},
)