Schema
Library
Schema
Overview
Schema is a simple and lightweight Python data structure validation library developed with the catchphrase "Schema validation just got Pythonic". It specializes in validating Python data types converted from JSON/YAML, such as those from configuration files, forms, external services, or command-line arguments. Developed by Vladimir Keleshev, it features an intuitive Pythonic API design with minimal dependencies. This validation library pursues simplicity and ease of use by eliminating complex features.
Details
Schema provides basic validation functionality including type checking, callable validators, logical operators (And/Or), Optional, Use, and Regex. With its priority-based dispatch mechanism, it can flexibly handle various data types and validation rules. Implemented as a self-contained single schema.py file, it can be easily integrated into projects. Supporting a wide range of Python versions from 2.6 to 3.9, it also includes draft-07 JSON Schema generation capabilities.
Key Features
- Extremely Simple API: Intuitive usage with Pythonic notation
- Lightweight Implementation: Works with a single file (schema.py)
- Flexible Type Checking: Validation using Python types, callables, and regular expressions
- Logical Operator Support: Complex condition combinations with And/Or
- JSON Schema Generation: Standard draft-07 format schema output
- Detailed Error Reporting: Clear error messages via SchemaError
Pros and Cons
Pros
- Extremely low learning curve (simple API)
- No dependencies (self-contained single file)
- Lightweight and fast operation
- Intuitive Pythonic notation
- Wide Python version support (2.6-3.9)
- Free usage under MIT license
Cons
- Lack of advanced validation features
- Limited type conversion functionality
- Small ecosystem
- Insufficient features for large projects
- Not optimized for performance
- No async processing support
References
Examples
Installation and Basic Setup
# Install with pip
pip install schema
# Or copy the single file to your project
wget https://raw.githubusercontent.com/keleshev/schema/master/schema.py
Basic Type Checking
from schema import Schema
# Simple type checking
Schema(int).validate(123) # Success: returns 123
Schema(str).validate("hello") # Success: returns "hello"
# Type mismatch
try:
Schema(int).validate("123") # Raises SchemaError
except SchemaError as e:
print(f"Error: {e}")
# Validation only with is_valid
if Schema(int).is_valid(123):
print("Valid data")
Dictionary Validation
from schema import Schema, And, Use, Optional
# User information validation schema
user_schema = Schema({
'name': And(str, len), # Non-empty string
'age': And(Use(int), lambda n: 18 <= n <= 99), # Convert string to int and check range
Optional('email'): And(str, lambda s: '@' in s), # Optional email address
Optional('gender'): And(str, Use(str.lower), lambda s: s in ('male', 'female'))
})
# Data validation
data = {
'name': 'John Doe',
'age': '25', # Passed as string but converted to integer
'email': '[email protected]'
}
validated = user_schema.validate(data)
print(validated) # {'name': 'John Doe', 'age': 25, 'email': '[email protected]'}
List and Nested Structure Validation
from schema import Schema, And, Use
# Product list validation
products_schema = Schema([{
'id': And(int, lambda n: n > 0),
'name': And(str, len),
'price': And(Use(float), lambda p: p > 0),
'tags': [str], # List of strings
'stock': {
'quantity': int,
'warehouse': str
}
}])
# Data validation
products = [{
'id': 1,
'name': 'Laptop',
'price': '899.99', # Converted from string to float
'tags': ['Electronics', 'Computers'],
'stock': {
'quantity': 15,
'warehouse': 'New York'
}
}]
validated = products_schema.validate(products)
Callable Validators and Custom Errors
from schema import Schema, Use, SchemaError
# Custom validator function
def validate_email(email):
import re
pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
if not re.match(pattern, email):
raise SchemaError(f'Invalid email address: {email}')
return email
# Password strength check
def strong_password(password):
if len(password) < 8:
return False
if not any(c.isupper() for c in password):
return False
if not any(c.isdigit() for c in password):
return False
return True
# Schema definition
account_schema = Schema({
'username': And(str, lambda s: 3 <= len(s) <= 20),
'email': Use(validate_email),
'password': And(str, strong_password, error='Password must be at least 8 characters with uppercase and digits')
})
# Validation execution
try:
account_schema.validate({
'username': 'user123',
'email': '[email protected]',
'password': 'weak' # Will cause error
})
except SchemaError as e:
print(f"Validation error: {e}")
Using Or and Forbidden
from schema import Schema, Or, Forbidden
# Authentication method selection (either email or username, but not both)
auth_schema = Schema({
Or('email', 'username', only_one=True): str,
'password': str,
Forbidden('admin'): object # admin field is forbidden
})
# Valid data
auth_schema.validate({'email': '[email protected]', 'password': 'Pass123!'})
auth_schema.validate({'username': 'user123', 'password': 'Pass123!'})
# Error case
try:
# Error when both are specified
auth_schema.validate({
'email': '[email protected]',
'username': 'user123',
'password': 'Pass123!'
})
except SchemaError as e:
print(f"Error: {e}")
JSON Schema Generation
from schema import Schema, Optional, Literal
import json
# Schema definition with descriptions
api_schema = Schema(
{
Literal("name", description="User display name"): str,
Literal("age", description="Age (18 or older)"): And(int, lambda n: n >= 18),
Optional(Literal("bio", description="User biography")): str
},
description="User Profile API"
)
# Generate JSON Schema
json_schema = api_schema.json_schema("https://example.com/user-profile.json")
print(json.dumps(json_schema, indent=2))