A Python library for validating data structures against user-defined schemas with comprehensive error reporting and nested structure support.
Project description
Schemafied
A Python library for validating data structures against user-defined schemas with comprehensive error reporting and nested structure support.
Features
- 🔍 Declarative Schema Definition - Define validation rules using intuitive field classes
- 🎯 Comprehensive Error Aggregation - Collect all validation errors, not just the first one
- 🏗️ Nested Structure Support - Validate complex nested dictionaries and lists
- 🔧 Type Coercion - Automatically convert compatible types (strings to numbers, etc.)
- 📍 Precise Error Paths - Get exact field paths for errors (e.g.,
users[0].profile.age) - ⚡ Performance Optimized - Error limiting for large datasets
- 🎨 Extensible - Custom validators and field types
- 🛡️ Type Safe - Full type hint support
Quick Start
Installation
pip install schemafied
Basic Usage
from schemafied import Schema, NumberField, StringField
# Define your schema
schema = Schema({
"name": StringField(min_length=1, max_length=100),
"age": NumberField(min_value=0, max_value=120),
"email": StringField(pattern=r"^[^@]+@[^@]+\.[^@]+$")
})
# Validate data
data = {
"name": "Alice Johnson",
"age": 28,
"email": "alice@example.com"
}
try:
result = schema.validate(data)
print("Validation successful:", result)
except ValidationError as e:
print("Validation failed:", e)
Nested Structures
from schemafied import Schema, DictField, ListField, NumberField, StringField
# Complex nested schema
user_schema = Schema({
"user_id": NumberField(min_value=1),
"profile": DictField({
"personal": DictField({
"name": StringField(min_length=1, max_length=100),
"age": NumberField(min_value=13, max_value=120)
}),
"preferences": DictField({
"theme": StringField(required=False, default="light"),
"notifications": ListField(
StringField(max_length=20),
required=False
)
}, required=False)
}),
"tags": ListField(
StringField(min_length=1, max_length=20),
max_length=10,
required=False
)
})
# Validate nested data
nested_data = {
"user_id": 12345,
"profile": {
"personal": {
"name": "Bob Smith",
"age": 25
},
"preferences": {
"theme": "dark",
"notifications": ["email", "push"]
}
},
"tags": ["python", "developer", "tech"]
}
result = user_schema.validate(nested_data)
Field Types
NumberField
Validates numeric values (int or float) with optional constraints.
# Basic number validation
age = NumberField(min_value=0, max_value=120)
# Optional with default
score = NumberField(min_value=0, max_value=100, required=False, default=0)
# Custom validators
def must_be_even(value):
return value % 2 == 0
even_number = NumberField(validators=[must_be_even])
Parameters:
min_value: Minimum allowed value (inclusive)max_value: Maximum allowed value (inclusive)required: Whether field is required (default: True)default: Default value if field is missingvalidators: List of custom validation functionscoerce: Enable type coercion from strings (default: True)
StringField
Validates string values with length and pattern constraints.
# Basic string validation
name = StringField(min_length=1, max_length=50)
# Pattern validation (regex)
email = StringField(pattern=r"^[^@]+@[^@]+\.[^@]+$")
# Optional with default
status = StringField(required=False, default="active")
Parameters:
min_length: Minimum string lengthmax_length: Maximum string lengthpattern: Regex pattern (string or compiled Pattern)required: Whether field is required (default: True)default: Default value if field is missingvalidators: List of custom validation functionscoerce: Enable type coercion to string (default: True)
ListField
Validates lists with item type validation and length constraints.
# List of strings
tags = ListField(StringField(max_length=20), max_length=10)
# List of numbers with constraints
scores = ListField(
NumberField(min_value=0, max_value=100),
min_length=1,
max_length=50
)
# Nested list validation
contacts = ListField(
DictField({
"type": StringField(),
"value": StringField(min_length=1)
}),
max_length=5
)
Parameters:
item_field: Field instance for validating list itemsmin_length: Minimum list lengthmax_length: Maximum list lengthmax_item_errors: Maximum item errors to report (default: 10)required: Whether field is required (default: True)coerce: Enable coercion from iterables (default: True)
DictField
Validates nested dictionaries against a sub-schema.
# Nested dictionary validation
address = DictField({
"street": StringField(min_length=1),
"city": StringField(min_length=1),
"postal_code": StringField(pattern=r"^\d{5}$"),
"country": StringField(required=False, default="US")
})
# Strict mode (reject extra fields)
strict_config = DictField({
"api_key": StringField(min_length=1),
"timeout": NumberField(min_value=1, max_value=300)
}, strict=True)
Parameters:
schema: Dictionary mapping field names to Field instancesstrict: Reject dictionaries with extra fields (default: False)required: Whether field is required (default: True)coerce: Enable coercion from dict-like objects (default: True)
Error Handling
Schemafied provides detailed error information with exact field paths:
Single Error
from schemafied.exceptions import ValidationError
try:
schema.validate(invalid_data)
except ValidationError as e:
print(f"Error at {e.field_path}: {e.message}")
print(f"Error code: {e.error_code}")
print(f"Invalid value: {e.value}")
Multiple Errors
from schemafied.exceptions import ValidationErrorCollection
try:
schema.validate(invalid_data)
except ValidationErrorCollection as e:
print(f"Found {len(e.errors)} validation errors:")
# Group errors by field
for field_path, field_errors in e.get_errors_by_field().items():
print(f" {field_path}:")
for error in field_errors:
print(f" - {error.message}")
Partial Validation
Get validation results even when there are errors:
result = schema.validate_partial(data)
if result['is_valid']:
print("Data is valid:", result['data'])
else:
print("Validation errors:", result['errors'])
print("Partial data:", result['data'])
Custom Validators
Create custom validation logic:
def must_be_positive(value):
"""Custom validator that ensures value is positive."""
if value <= 0:
return "Value must be positive"
return True
def must_be_even(value):
"""Custom validator using exceptions."""
if value % 2 != 0:
raise ValueError("Value must be even")
return True
# Use in fields
positive_number = NumberField(
min_value=1,
validators=[must_be_positive, must_be_even]
)
Contributing
We welcome contributions! Please see our Contributing Guide for details.
License
This project is licensed under the MIT License - see the LICENSE file for details.
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 schemafied-1.0.0.tar.gz.
File metadata
- Download URL: schemafied-1.0.0.tar.gz
- Upload date:
- Size: 28.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ecefa824e734b024a71ebe084b4d36aec6b86f6db8bad1ff620e5056dec69c98
|
|
| MD5 |
5ca7965a72848a7c9df257ddce82e629
|
|
| BLAKE2b-256 |
3424b8c211a0346c971f4b1a873942c03e591926abd6968611ba97bdf8c2c629
|
File details
Details for the file schemafied-1.0.0-py3-none-any.whl.
File metadata
- Download URL: schemafied-1.0.0-py3-none-any.whl
- Upload date:
- Size: 17.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a08f100d2ccec93f7255479fad2436c3d719f85812af902d9883af201009948
|
|
| MD5 |
1b8158289935a6a688e2f17fd026b518
|
|
| BLAKE2b-256 |
34b64eaf89a6c78ce53709d712eb5eba8672a2b72262261eb8ed455f33f02dd6
|