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
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 (
coercionandvalidations) 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 dictionaryfrom_json(json_string: str, model_name: str = "DynamicModel")- Create model from JSON stringfrom_file(file_path: str, model_name: str = None)- Create model from JSON filefrom_url(url: str, model_name: str = "DynamicModel")- Create model from URLschema_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
jsonschemalibrary 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_validatordecorators - 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
validationsfield - 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.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🔗 Links
- Repository: GitHub
- Issues: GitHub Issues
- Documentation: GitHub README
Made with ❤️ for the Python community
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
133423d2f26eac9b9d76a4e3e6a3de9bb4ab04423cdd8c13a43c94765ab8fece
|
|
| MD5 |
b3c90dd847dbfc3931e798c70fda06ef
|
|
| BLAKE2b-256 |
fd3ebdd98ee326aac2a7df5492183fb7d7fb79f39079674d6d9fb0d83d2e0cf7
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b793a569bf11cdb98c6dcf5a0e8b3d977cfa490349c67e4044048615d7060c08
|
|
| MD5 |
77d5f3900d649c431e7ac9154d2f0c92
|
|
| BLAKE2b-256 |
44bd7259214a66758b7beafcc48b6966b5984bf5e76a5352f892678f78270384
|