Skip to main content

Schemafied

A Python library for validating data structures against user-defined schemas with comprehensive error reporting and nested structure support.

Python Version License: MIT PyPI Version

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 missing
  • validators: List of custom validation functions
  • coerce: 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 length
  • max_length: Maximum string length
  • pattern: Regex pattern (string or compiled Pattern)
  • required: Whether field is required (default: True)
  • default: Default value if field is missing
  • validators: List of custom validation functions
  • coerce: 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 items
  • min_length: Minimum list length
  • max_length: Maximum list length
  • max_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 instances
  • strict: 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)

Source distribution for schemafied 1.0.0
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)

Table of built distributions (wheels) for schemafied 1.0.0
File Interpreter ABI Platform Actions
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.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page