Skip to main content

A Python package for data contract management with five core services: contract parsing, metadata storage, Pydantic generation, JSON Schema conversion, and runtime validation

Project description

PyCharter

Dynamically generate Pydantic models from JSON schemas with coercion and validation support

Python 3.10+ License: MIT Code style: black

PyCharter is a powerful Python library that automatically converts JSON schemas into fully-functional Pydantic models. It fully supports the JSON Schema Draft 2020-12 standard, including all standard validation keywords (minLength, maxLength, pattern, enum, minimum, maximum, etc.), while also providing extensions for pre-validation coercion and post-validation checks. It handles nested objects, arrays, and custom validators, with all validation logic stored as data (not Python code).

✨ Features

  • 🚀 Dynamic Model Generation - Convert JSON schemas to Pydantic models at runtime
  • 📋 JSON Schema Compliant - Full support for JSON Schema Draft 2020-12 standard
  • 🔄 Type Coercion - Automatic type conversion before validation (e.g., string → integer)
  • Custom Validators - Built-in and extensible validation rules
  • 🏗️ Nested Structures - Full support for nested objects and arrays
  • 📦 Multiple Input Formats - Load schemas from dicts, JSON strings, files, or URLs
  • 🎯 Type Safe - Full type hints and Pydantic v2 compatibility
  • 🔧 Extensible - Register custom coercion and validation functions
  • 📊 Data-Driven - All validation logic stored as JSON data, not Python code

📦 Installation

pip install pycharter

🚀 Quick Start

from pycharter import from_dict

# Define your JSON schema
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer"},
        "email": {"type": "string"}
    },
    "required": ["name", "age"]
}

# Generate a Pydantic model
Person = from_dict(schema, "Person")

# Use it like any Pydantic model
person = Person(name="Alice", age=30, email="alice@example.com")
print(person.name)  # Output: Alice
print(person.age)   # Output: 30

📚 Usage Examples

Basic Usage

from pycharter import from_dict, from_json, from_file

# From dictionary
schema = {
    "type": "object",
    "properties": {
        "title": {"type": "string"},
        "published": {"type": "boolean", "default": False}
    }
}
Article = from_dict(schema, "Article")

# From JSON string
schema_json = '{"type": "object", "properties": {"name": {"type": "string"}}}'
User = from_json(schema_json, "User")

# From file
Product = from_file("product_schema.json", "Product")

Nested Objects

from pycharter import from_dict

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "address": {
            "type": "object",
            "properties": {
                "street": {"type": "string"},
                "city": {"type": "string"},
                "zipcode": {"type": "string"}
            }
        }
    }
}

Person = from_dict(schema, "Person")
person = Person(
    name="Alice",
    address={
        "street": "123 Main St",
        "city": "New York",
        "zipcode": "10001"
    }
)

print(person.address.city)  # Output: New York

Arrays and Collections

from pycharter import from_dict

schema = {
    "type": "object",
    "properties": {
        "tags": {
            "type": "array",
            "items": {"type": "string"}
        },
        "items": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "price": {"type": "number"}
                }
            }
        }
    }
}

Cart = from_dict(schema, "Cart")
cart = Cart(
    tags=["python", "pydantic"],
    items=[
        {"name": "Apple", "price": 1.50},
        {"name": "Banana", "price": 0.75}
    ]
)

print(cart.items[0].name)  # Output: Apple

Coercion and Validation

Charter supports coercion (pre-validation transformation) and validation (post-validation checks):

from pycharter import from_dict

schema = {
    "type": "object",
    "properties": {
        "flight_number": {
            "type": "integer",
            "coercion": "coerce_to_integer"  # Convert string/float to int
        },
        "destination": {
            "type": "string",
            "coercion": "coerce_to_string",
            "validations": {
                "min_length": {"threshold": 3},
                "max_length": {"threshold": 3},
                "no_capital_characters": None,
                "only_allow": {"allowed_values": ["abc", "def", "ghi"]}
            }
        },
        "distance": {
            "type": "number",
            "coercion": "coerce_to_float",
            "validations": {
                "greater_than_or_equal_to": {"threshold": 0}
            }
        }
    }
}

Flight = from_dict(schema, "Flight")

# Coercion happens automatically
flight = Flight(
    flight_number="123",    # Coerced to int: 123
    destination="abc",      # Passes all validations
    distance="100.5"        # Coerced to float: 100.5
)

📋 Standard JSON Schema Support

Charter supports all standard JSON Schema Draft 2020-12 validation keywords:

Keyword Type Description Example
minLength string Minimum string length {"minLength": 3}
maxLength string Maximum string length {"maxLength": 10}
pattern string Regular expression pattern {"pattern": "^[a-z]+$"}
enum any Allowed values {"enum": ["a", "b", "c"]}
const any Single allowed value {"const": "fixed"}
minimum number Minimum value (inclusive) {"minimum": 0}
maximum number Maximum value (inclusive) {"maximum": 100}
exclusiveMinimum number Minimum value (exclusive) {"exclusiveMinimum": 0}
exclusiveMaximum number Maximum value (exclusive) {"exclusiveMaximum": 100}
multipleOf number Must be multiple of {"multipleOf": 2}
minItems array Minimum array length {"minItems": 1}
maxItems array Maximum array length {"maxItems": 10}
uniqueItems array Array items must be unique {"uniqueItems": true}

All schemas are validated against JSON Schema standard before processing, ensuring compliance.

🔧 Built-in Coercions (Charter Extensions)

Coercion Description
coerce_to_string Convert int, float, bool, datetime, dict, list to string
coerce_to_integer Convert float, string (numeric), bool, datetime to int
coerce_to_float Convert int, string (numeric), bool to float
coerce_to_boolean Convert int, string to bool
coerce_to_datetime Convert string (ISO format), timestamp to datetime
coerce_to_uuid Convert string to UUID

✅ Built-in Validations (Charter Extensions)

Validation Description Configuration
min_length Minimum length for strings/arrays {"threshold": N}
max_length Maximum length for strings/arrays {"threshold": N}
only_allow Only allow specific values {"allowed_values": [...]}
greater_than_or_equal_to Numeric minimum {"threshold": N}
less_than_or_equal_to Numeric maximum {"threshold": N}
no_capital_characters No uppercase letters null
no_special_characters Only alphanumeric and spaces null

Note: Charter extensions (coercion and validations) are optional and can be used alongside standard JSON Schema keywords. All validation logic is stored as data in the JSON schema, making it fully data-driven.

🎨 Custom Coercions and Validations

Extend Charter with your own coercion and validation functions:

from pycharter.coercions import register_coercion
from pycharter.validations import register_validation

# Register custom coercion
def coerce_to_uppercase(data):
    if isinstance(data, str):
        return data.upper()
    return data

register_coercion("coerce_to_uppercase", coerce_to_uppercase)

# Register custom validation
def must_be_positive(threshold=0):
    def _validate(value, info):
        if value <= threshold:
            raise ValueError(f"Value must be > {threshold}")
        return value
    return _validate

register_validation("must_be_positive", must_be_positive)

📖 API Reference

Main Functions

  • from_dict(schema: dict, model_name: str = "DynamicModel") - Create model from dictionary
  • from_json(json_string: str, model_name: str = "DynamicModel") - Create model from JSON string
  • from_file(file_path: str, model_name: str = None) - Create model from JSON file
  • from_url(url: str, model_name: str = "DynamicModel") - Create model from URL
  • schema_to_model(schema: dict, model_name: str = "DynamicModel") - Low-level model generator

🎯 Design Principles & Requirements

Charter is designed to meet the following core requirements:

✅ JSON Schema Standard Compliance

All schemas must abide by conventional JSON Schema syntax and qualify as valid JSON Schema:

  • Validation: All schemas are validated against JSON Schema Draft 2020-12 standard before processing
  • Standard Keywords: Full support for all standard validation keywords (minLength, pattern, enum, minimum, maximum, etc.)
  • Compliance: Uses jsonschema library for validation with graceful fallback

✅ Data-Driven Validation Logic

All schema information and complex field validation logic is stored as data, not Python code:

  • Coercion: Referenced by name (string) in JSON: "coercion": "coerce_to_integer"
  • Validations: Referenced by name with configuration (dict) in JSON: "validations": {"min_length": {"threshold": 3}}
  • No Code Required: Validation rules are defined entirely in JSON schema files
  • Example: {"coercion": "coerce_to_string", "validations": {"min_length": {"threshold": 3}}}

✅ Dynamic Pydantic Model Generation

Models are created dynamically at runtime from JSON schemas:

  • Runtime Generation: Uses pydantic.create_model() to generate models on-the-fly
  • Dynamic Validators: Field validators are dynamically attached using field_validator decorators
  • Multiple Sources: Models can be created from dicts, JSON strings, files, or URLs
  • No Static Code: All models are generated from data, not pre-defined classes

✅ Nested Schema Support

Full support for nested object schemas and complex structures:

  • Recursive Processing: Nested objects are recursively processed into their own Pydantic models
  • Arrays of Objects: Arrays containing nested objects are fully supported
  • Deep Nesting: Deeply nested structures work correctly with full type safety
  • Type Safety: Each nested object becomes its own typed Pydantic model

✅ Extension Fields

Custom fields can be added to JSON Schema to extend functionality:

  • coercion: Pre-validation type conversion (e.g., string → integer)
  • validations: Post-validation custom rules
  • Optional: Extensions work alongside standard JSON Schema keywords
  • Separated: Extensions are clearly distinguished from standard JSON Schema

✅ Complex Field Validation

Support for both standard and custom field validators:

  • Standard Validators: minLength, pattern, enum, minimum, maximum, etc. (JSON Schema standard)
  • Custom Validators: Extensible validation rules via validations field
  • Validation Order: Coercion → Standard Validation → Pydantic Validation → Custom Validations
  • Factory Pattern: Validators are factory functions that return validation functions

🔗 Requirements

  • Python 3.10+
  • Pydantic >= 2.0.0
  • jsonschema >= 4.0.0 (optional, for enhanced validation)

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🔗 Links


Made with ❤️ for the Python community

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pycharter-0.0.1.tar.gz (56.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pycharter-0.0.1-py3-none-any.whl (55.5 kB view details)

Uploaded Python 3

File details

Details for the file pycharter-0.0.1.tar.gz.

File metadata

  • Download URL: pycharter-0.0.1.tar.gz
  • Upload date:
  • Size: 56.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10+

File hashes

Hashes for pycharter-0.0.1.tar.gz
Algorithm Hash digest
SHA256 133423d2f26eac9b9d76a4e3e6a3de9bb4ab04423cdd8c13a43c94765ab8fece
MD5 b3c90dd847dbfc3931e798c70fda06ef
BLAKE2b-256 fd3ebdd98ee326aac2a7df5492183fb7d7fb79f39079674d6d9fb0d83d2e0cf7

See more details on using hashes here.

File details

Details for the file pycharter-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: pycharter-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 55.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10+

File hashes

Hashes for pycharter-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b793a569bf11cdb98c6dcf5a0e8b3d977cfa490349c67e4044048615d7060c08
MD5 77d5f3900d649c431e7ac9154d2f0c92
BLAKE2b-256 44bd7259214a66758b7beafcc48b6966b5984bf5e76a5352f892678f78270384

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page