Skip to main content

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.

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.

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

schemafied-1.0.0.tar.gz (28.3 kB view details)

Uploaded Source

Built Distribution

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

schemafied-1.0.0-py3-none-any.whl (17.9 kB view details)

Uploaded Python 3

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

Hashes for schemafied-1.0.0.tar.gz
Algorithm Hash digest
SHA256 ecefa824e734b024a71ebe084b4d36aec6b86f6db8bad1ff620e5056dec69c98
MD5 5ca7965a72848a7c9df257ddce82e629
BLAKE2b-256 3424b8c211a0346c971f4b1a873942c03e591926abd6968611ba97bdf8c2c629

See more details on using hashes here.

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

Hashes for schemafied-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4a08f100d2ccec93f7255479fad2436c3d719f85812af902d9883af201009948
MD5 1b8158289935a6a688e2f17fd026b518
BLAKE2b-256 34b64eaf89a6c78ce53709d712eb5eba8672a2b72262261eb8ed455f33f02dd6

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