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.
Metadata
Release files for schemafied 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| schemafied-1.0.0.tar.gz | 28.3 kB | Without provenance | Details for schemafied-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| schemafied-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for schemafied-1.0.0-py3-none-any.whl |
Total release size: 46.2 kB
Release files / schemafied-1.0.0.tar.gz
| Download URL | schemafied-1.0.0.tar.gz |
|---|---|
| Size | 28.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ecefa824e734b024a71ebe084b4d36aec6b86f6db8bad1ff620e5056dec69c98
|
|
BLAKE2b-256 checksum How to use checksums |
3424b8c211a0346c971f4b1a873942c03e591926abd6968611ba97bdf8c2c629
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.3
|
Release files / schemafied-1.0.0-py3-none-any.whl
| Download URL | schemafied-1.0.0-py3-none-any.whl |
|---|---|
| Size | 17.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4a08f100d2ccec93f7255479fad2436c3d719f85812af902d9883af201009948
|
|
BLAKE2b-256 checksum How to use checksums |
34b64eaf89a6c78ce53709d712eb5eba8672a2b72262261eb8ed455f33f02dd6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.3
|
Security for schemafied 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.