Skip to content

mbarlow12/schemali

Repository files navigation

Schemali

A modern CLI tool for generating JSON schemas from Pydantic models.

Features

  • 🚀 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 $defs and $ref support
  • 🎨 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

Installation

Using uv (recommended)

uv pip install schemali

Using pip

pip install schemali

From source

git clone https://github.com/mbarlow12/schemali.git
cd schemali
uv pip install -e ".[dev]"

Dependencies

  • Python >= 3.8
  • pydantic >= 2.0.0
  • pydantic-settings >= 2.0.0
  • typer >= 0.9.0
  • rich >= 13.0.0

Quick Start

# 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 -v

Usage

Basic Usage

Generate schemas from a single Python module:

schemali models.py

This will create {ModelName}.schema.json files in the current directory for each Pydantic model found.

Multiple Modules

Process multiple modules at once:

schemali user.py product.py order.py

Custom Output Directory

Specify where to write the schema files:

schemali models.py -o schemas/
# or
schemali models.py --output-dir schemas/

Custom Indentation

Control JSON formatting (default is 2 spaces):

schemali models.py --indent 4

Verbose Output

See detailed information about what's being processed:

schemali models.py -v
# or
schemali models.py --verbose

Single Consolidated Schema File

Generate 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-file

This 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" }
    }
  }
}

Using a Configuration File

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.toml

Environment Variables

You 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.py

Combined Options

Command-line arguments override configuration file settings:

schemali user.py product.py -o schemas/ --indent 4 -v -c config.toml

Configuration

Schemali uses a flexible configuration system powered by pydantic-settings. Configuration sources are prioritized as follows (highest to lowest):

  1. Command-line arguments
  2. Configuration file (TOML)
  3. Environment variables (with SCHEMALI_ prefix)
  4. Default values

Configuration Options

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

Configuration File Locations

Schemali searches for configuration files in the following locations:

  1. ./schemali.toml (current directory)
  2. ./.schemali.toml (current directory, hidden)
  3. ~/.config/schemali/config.toml (user config directory)
  4. Custom path via -c/--config flag

Example

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 -v

Output:

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"
}

Running as a Python Module

You can also run schemali as a Python module:

python -m schemali models.py

Development

Setup

We 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]"

Running Tests

# Run all tests with coverage
pytest

# Run with verbose output
pytest -v

# Generate HTML coverage report
pytest --cov-report=html
open htmlcov/index.html

Code Quality

We use Ruff for linting and formatting:

# Check code
ruff check .

# Auto-fix issues
ruff check --fix .

# Format code
ruff format .

Project Structure

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

Technology Stack

  • 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)

Help

For more information:

schemali --help

Contributing

Contributions 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:

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for new functionality
  4. Ensure all tests pass: pytest
  5. Run linter: ruff check .
  6. Submit a pull request

For developer documentation, see CLAUDE.md.

License

MIT License - see LICENSE file for details.

Links

About

A configurable CLI to product JSON schemas from Pydantic models

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages