A modern CLI tool for generating JSON schemas from Pydantic models.
- 🚀 Load one or more Python modules containing Pydantic models
- 🔍 Automatically discover all Pydantic models in each module
- 📋 Generate JSON schemas compliant with JSON Schema specification
- 💾 Write schemas to individual files or a single consolidated file
- 📦 Single-file mode with JSON Schema 2020-12
$defsand$refsupport - 🎨 Beautiful terminal output with colors and tables
- ⚙️ Flexible configuration via TOML files, environment variables, or CLI arguments
- 🧪 Comprehensive test coverage with pytest
- 🎯 Support for nested models and complex field types
- 🔧 Customizable output directory and JSON formatting
uv pip install schemalipip install schemaligit clone https://github.com/mbarlow12/schemali.git
cd schemali
uv pip install -e ".[dev]"- Python >= 3.8
- pydantic >= 2.0.0
- pydantic-settings >= 2.0.0
- typer >= 0.9.0
- rich >= 13.0.0
# Process a single module
schemali models.py
# Process multiple modules
schemali user.py product.py order.py
# Specify output directory
schemali models.py -o schemas/
# Generate single consolidated schema (JSON Schema 2020-12)
schemali models.py --single-file
# Use verbose output
schemali models.py -vGenerate schemas from a single Python module:
schemali models.pyThis will create {ModelName}.schema.json files in the current directory for each Pydantic model found.
Process multiple modules at once:
schemali user.py product.py order.pySpecify where to write the schema files:
schemali models.py -o schemas/
# or
schemali models.py --output-dir schemas/Control JSON formatting (default is 2 spaces):
schemali models.py --indent 4See detailed information about what's being processed:
schemali models.py -v
# or
schemali models.py --verboseGenerate a single JSON Schema 2020-12 compliant file with all models using $defs:
# Generate single consolidated schema
schemali models.py --single-file
# With custom filename
schemali models.py --single-file --single-file-name all-schemas.json
# Multiple modules into one consolidated file
schemali user.py product.py order.py --single-fileThis creates a schema file like:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "file:///path/to/schemas.json",
"title": "Consolidated Pydantic Models Schema",
"description": "JSON Schema definitions for all Pydantic models",
"$defs": {
"User": { /* User schema */ },
"Product": { /* Product schema */ },
"Order": { /* Order schema */ }
}
}You can reference models using $ref:
{
"type": "object",
"properties": {
"user": { "$ref": "#/$defs/User" },
"items": {
"type": "array",
"items": { "$ref": "#/$defs/Product" }
}
}
}Create a schemali.toml configuration file:
[tool.schemali]
output_dir = "schemas"
indent = 4
verbose = false
schema_suffix = ".schema.json"
overwrite = true
single_file = false
single_file_name = "schemas.json"Then run:
schemali models.py -c schemali.tomlYou can also configure schemali using environment variables with the SCHEMALI_ prefix:
export SCHEMALI_OUTPUT_DIR="schemas"
export SCHEMALI_INDENT=4
export SCHEMALI_VERBOSE=true
schemali models.pyCommand-line arguments override configuration file settings:
schemali user.py product.py -o schemas/ --indent 4 -v -c config.tomlSchemali uses a flexible configuration system powered by pydantic-settings. Configuration sources are prioritized as follows (highest to lowest):
- Command-line arguments
- Configuration file (TOML)
- Environment variables (with
SCHEMALI_prefix) - Default values
| Option | Type | Default | Description |
|---|---|---|---|
output_dir |
Path | current dir | Output directory for schema files |
indent |
int | 2 | JSON indentation spaces (0-8) |
verbose |
bool | false | Enable verbose output |
schema_suffix |
str | .schema.json |
Suffix for generated schema files |
overwrite |
bool | true | Whether to overwrite existing files |
single_file |
bool | false | Generate single consolidated schema file |
single_file_name |
str | schemas.json |
Name of single output file |
Schemali searches for configuration files in the following locations:
./schemali.toml(current directory)./.schemali.toml(current directory, hidden)~/.config/schemali/config.toml(user config directory)- Custom path via
-c/--configflag
Given a Python module models.py:
from pydantic import BaseModel, Field
from typing import Optional
class User(BaseModel):
"""User model."""
id: int
username: str = Field(..., min_length=3, max_length=50)
email: str
age: Optional[int] = Field(None, ge=0, le=150)
is_active: bool = True
class Product(BaseModel):
"""Product model."""
id: int
name: str
price: float = Field(..., gt=0)Running:
schemali models.py -vOutput:
Processing module: /path/to/models.py
┏━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Model ┃ Schema File ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ User │ /current/dir/User.schema.json │
│ Product │ /current/dir/Product.schema.json │
└─────────┴───────────────────────────────────────┘
✓ Complete! Generated 2 schemas
Generated User.schema.json:
{
"description": "User model.",
"properties": {
"id": {
"title": "Id",
"type": "integer"
},
"username": {
"maxLength": 50,
"minLength": 3,
"title": "Username",
"type": "string"
},
"email": {
"title": "Email",
"type": "string"
},
"age": {
"anyOf": [
{
"maximum": 150,
"minimum": 0,
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Age"
},
"is_active": {
"default": true,
"title": "Is Active",
"type": "boolean"
}
},
"required": ["id", "username", "email"],
"title": "User",
"type": "object"
}You can also run schemali as a Python module:
python -m schemali models.pyWe use uv for dependency management:
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Create virtual environment
uv venv
source .venv/bin/activate
# Install with dev dependencies
uv pip install -e ".[dev]"# Run all tests with coverage
pytest
# Run with verbose output
pytest -v
# Generate HTML coverage report
pytest --cov-report=html
open htmlcov/index.htmlWe use Ruff for linting and formatting:
# Check code
ruff check .
# Auto-fix issues
ruff check --fix .
# Format code
ruff format .schemali/
├── schemali/ # Main package
│ ├── __init__.py
│ ├── __main__.py # Entry point
│ ├── cli.py # CLI using Typer
│ ├── config.py # Configuration with pydantic-settings
│ └── schema_writer.py # Core logic
├── tests/ # Test suite
│ ├── conftest.py # Test fixtures
│ ├── test_cli.py
│ ├── test_config.py
│ └── test_schema_writer.py
├── examples/ # Example models
├── pyproject.toml # Project configuration
├── README.md
└── CLAUDE.md # Developer documentation
- Build System: hatchling (for uv compatibility)
- CLI Framework: Typer (type-safe, modern CLI)
- Configuration: pydantic-settings (TOML + env vars)
- Terminal Output: Rich (beautiful formatting)
- Testing: pytest with coverage
- Linting: Ruff (fast Python linter)
For more information:
schemali --helpContributions are welcome! Please see CONTRIBUTING.md for detailed guidelines on:
- Development setup and workflow
- Code style guidelines
- Testing requirements
- Pull request process
- CI/CD workflows
Quick start for contributors:
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Ensure all tests pass:
pytest - Run linter:
ruff check . - Submit a pull request
For developer documentation, see CLAUDE.md.
MIT License - see LICENSE file for details.