Python Data Structures for Humans - a library for data validation and transformation using structured models
—
Comprehensive exception hierarchy for handling validation errors, conversion failures, and data processing issues in Schematics. The exception system provides detailed error information with field-level granularity and supports error aggregation for complex data structures.
Foundation exception classes providing common error handling functionality.
class BaseError(Exception):
"""
Base class for all Schematics errors with error aggregation.
Provides immutable error storage and primitive representation
for machine-readable error processing.
"""
def __init__(self, errors):
"""
Initialize base error.
Args:
errors: Error data (dict/list/string)
"""
@property
def errors(self):
"""
Get immutable error data.
Returns:
Frozen error structure
"""
def to_primitive(self):
"""
Convert errors to primitive representation.
Returns:
dict: JSON-serializable error structure
"""
class ErrorMessage:
"""
Error message container with internationalization support.
Wraps error messages with metadata for display and localization.
"""
def __init__(self, message, **kwargs):
"""
Initialize error message.
Args:
message (str): Error message text
**kwargs: Additional message metadata
"""Exceptions for individual field validation and conversion errors.
class FieldError(BaseError):
"""
Base class for field-specific errors.
Associates errors with specific model fields and provides
context for field-level error handling.
"""
class ConversionError(FieldError):
"""
Raised during data type conversion failures.
Occurs when input data cannot be converted to the field's
expected type (e.g., string to integer conversion failure).
"""
def __init__(self, message, **kwargs):
"""
Initialize conversion error.
Args:
message (str): Conversion error description
**kwargs: Additional conversion context
"""
class ValidationError(FieldError):
"""
Raised during field validation failures.
Occurs when converted data fails field validation rules
(e.g., string length, numeric range, regex pattern).
"""
def __init__(self, message, **kwargs):
"""
Initialize validation error.
Args:
message (str): Validation error description
**kwargs: Additional validation context
"""
class StopValidationError(FieldError):
"""
Stops validation chain without raising an error.
Used internally to halt validation processing without
treating the condition as an error state.
"""Exceptions for model-wide validation and data processing errors.
class CompoundError(BaseError):
"""
Container for multiple field errors.
Aggregates validation errors from multiple fields into a
single exception with structured error information.
"""
def __init__(self, errors, **kwargs):
"""
Initialize compound error.
Args:
errors (dict): Field name to error mapping
**kwargs: Additional error context
"""
class DataError(BaseError):
"""
Model data errors with partial data preservation.
Raised when model validation fails but allows access to
partial data that was successfully processed.
"""
def __init__(self, errors, partial_data=None, **kwargs):
"""
Initialize data error.
Args:
errors: Validation errors
partial_data (dict, optional): Successfully processed data
**kwargs: Additional error context
"""
@property
def partial_data(self):
"""
Get partial data that was successfully processed.
Returns:
dict: Partial model data
"""Specialized exceptions for specific error conditions.
class MockCreationError(BaseError):
"""
Errors during mock data generation.
Raised when mock object creation fails due to field
constraints or generation limitations.
"""
class UndefinedValueError(BaseError):
"""
Raised when accessing undefined field values.
Occurs when attempting to access a field value that
has not been set and has no default value.
"""
class UnknownFieldError(BaseError):
"""
Raised when accessing nonexistent fields.
Occurs when attempting to access a field that is not
defined in the model schema.
"""from schematics.models import Model
from schematics.types import StringType, IntType
from schematics.exceptions import ValidationError, ConversionError, DataError
class Product(Model):
name = StringType(required=True, max_length=50)
price = IntType(min_value=0, required=True)
category = StringType(choices=['electronics', 'books', 'clothing'])
# Validation error handling
try:
product = Product({
'name': 'A very long product name that exceeds the maximum length limit',
'price': -10, # Negative price
'category': 'invalid_category'
})
product.validate()
except ValidationError as e:
print(f"Validation failed: {e}")
print(f"Error details: {e.to_primitive()}")# DataError with multiple field validation failures
try:
invalid_product = Product({
'name': '', # Required field empty
'price': 'abc', # Cannot convert to int
'category': 'toys' # Not in choices
})
invalid_product.validate()
except DataError as e:
print("Multiple validation errors occurred:")
for field, error in e.errors.items():
print(f" {field}: {error}")
# Access any partial data that was successfully processed
if hasattr(e, 'partial_data') and e.partial_data:
print(f"Partial data: {e.partial_data}")from schematics.types import DateTimeType, EmailType
class User(Model):
email = EmailType(required=True)
registered_at = DateTimeType(required=True)
try:
user = User({
'email': 'invalid-email-format',
'registered_at': 'not-a-date'
})
user.validate()
except ConversionError as e:
print(f"Data conversion failed: {e}")
except ValidationError as e:
print(f"Data validation failed: {e}")def process_user_registration(user_data):
"""
Process user registration with comprehensive error handling.
Returns:
tuple: (success: bool, data: dict, errors: dict)
"""
try:
user = User(user_data)
user.validate()
return True, user.to_primitive(), None
except ValidationError as e:
# Single field validation error
return False, None, {
'type': 'validation_error',
'field': getattr(e, 'field_name', 'unknown'),
'message': str(e)
}
except DataError as e:
# Multiple field validation errors
return False, e.partial_data, {
'type': 'data_error',
'errors': e.to_primitive(),
'fields_with_errors': list(e.errors.keys())
}
except ConversionError as e:
# Data type conversion error
return False, None, {
'type': 'conversion_error',
'field': getattr(e, 'field_name', 'unknown'),
'message': f"Could not convert value: {str(e)}"
}
# Usage
success, data, errors = process_user_registration({
'email': 'john@example.com',
'registered_at': '2024-01-15T10:30:00Z'
})
if success:
print(f"User registered successfully: {data}")
else:
print(f"Registration failed: {errors}")from schematics.types import ModelType, ListType
class Address(Model):
street = StringType(required=True)
city = StringType(required=True)
zip_code = StringType(required=True, regex=r'^\d{5}(-\d{4})?$')
class Person(Model):
name = StringType(required=True)
addresses = ListType(ModelType(Address), min_size=1)
# Complex validation with nested errors
try:
person = Person({
'name': '', # Invalid: required field empty
'addresses': [
{
'street': '123 Main St',
'city': 'Anytown',
'zip_code': 'invalid' # Invalid: doesn't match regex
},
{
'street': '', # Invalid: required field empty
'city': 'Other City',
'zip_code': '12345'
}
]
})
person.validate()
except DataError as e:
print("Nested validation errors:")
errors = e.to_primitive()
# Navigate nested error structure
if 'name' in errors:
print(f"Name error: {errors['name']}")
if 'addresses' in errors:
for i, addr_errors in enumerate(errors['addresses']):
if addr_errors:
print(f"Address {i} errors: {addr_errors}")def safe_model_creation(model_class, data, strict=True):
"""
Safely create and validate model with configurable error handling.
Args:
model_class: Model class to instantiate
data: Raw data dictionary
strict: Whether to raise on validation errors
Returns:
tuple: (model_instance or None, error_info or None)
"""
try:
model = model_class(data)
model.validate()
return model, None
except ValidationError as e:
error_info = {
'error_type': 'validation',
'message': str(e),
'field': getattr(e, 'field_name', None)
}
if strict:
raise
return None, error_info
except ConversionError as e:
error_info = {
'error_type': 'conversion',
'message': str(e),
'field': getattr(e, 'field_name', None)
}
if strict:
raise
return None, error_info
except DataError as e:
error_info = {
'error_type': 'data',
'errors': e.to_primitive(),
'partial_data': getattr(e, 'partial_data', None)
}
if strict:
raise
return None, error_info
# Usage with different error handling strategies
model, error = safe_model_creation(User, user_data, strict=False)
if model:
print("Model created successfully")
else:
print(f"Model creation failed: {error}")Install with Tessl CLI
npx tessl i tessl/pypi-schematics