Python data validation library for validating nested data structures with comprehensive error reporting
—
Numeric range validation, sequence length validation, membership testing, and complex sequence validation patterns. These validators handle constraints on numeric values, collection sizes, and collection contents.
Validate that numeric values fall within specified ranges with configurable boundary inclusion.
class Range:
def __init__(self, min=None, max=None, min_included=True, max_included=True, msg=None):
"""
Validate numeric value is within range.
Parameters:
- min: Minimum allowed value (None for no minimum)
- max: Maximum allowed value (None for no maximum)
- min_included: Whether minimum value is included in valid range (default True)
- max_included: Whether maximum value is included in valid range (default True)
- msg: Custom error message
Returns:
Original numeric value if within range
Raises:
RangeInvalid: If value is outside specified range
"""Usage Examples:
from voluptuous import Schema, Range, All, Coerce
# Basic range validation
age_validator = Range(min=0, max=150)
percentage_validator = Range(min=0.0, max=100.0)
# Open ranges
positive_validator = Range(min=0) # No maximum
negative_validator = Range(max=0) # No minimum
# Exclusive ranges
exclusive_range = Range(min=0, max=10, min_included=False, max_included=False) # 0 < x < 10
# Combined with type coercion
flexible_range = Schema(All(
Coerce(float), # Convert to float first
Range(min=0.0, max=1.0), # Then validate range
))
# Schema usage
product_schema = Schema({
'price': All(Coerce(float), Range(min=0.01)), # Positive price
'discount': Range(min=0, max=100), # Percentage
'quantity': All(int, Range(min=1)), # At least 1
})
# Valid examples
product_schema({
'price': '19.99', # Coerced to 19.99
'discount': 25, # 25% discount
'quantity': 5, # 5 items
})Automatically adjust numeric values to stay within specified bounds instead of rejecting them.
class Clamp:
def __init__(self, min=None, max=None, msg=None):
"""
Clamp numeric value to specified range.
Parameters:
- min: Minimum allowed value (values below are set to min)
- max: Maximum allowed value (values above are set to max)
- msg: Custom error message (rarely used since values are adjusted)
Returns:
Numeric value clamped to specified range
Raises:
Invalid: If input is not numeric
"""Usage Examples:
from voluptuous import Schema, Clamp, All, Coerce
# Clamp values instead of rejecting them
volume_control = Clamp(min=0, max=100) # Volume: 0-100
opacity_clamp = Clamp(min=0.0, max=1.0) # Opacity: 0.0-1.0
# One-way clamping
non_negative = Clamp(min=0) # No upper limit
max_limit = Clamp(max=1000) # No lower limit
# Usage examples
settings_schema = Schema({
'volume': All(Coerce(int), volume_control),
'opacity': All(Coerce(float), opacity_clamp),
'timeout': All(Coerce(int), Clamp(min=1, max=3600)), # 1 second to 1 hour
})
# Clamping in action:
volume_control(150) # -> 100 (clamped to maximum)
volume_control(-10) # -> 0 (clamped to minimum)
opacity_clamp(1.5) # -> 1.0 (clamped to maximum)Validate the length of sequences, strings, and other objects with len().
class Length:
def __init__(self, min=None, max=None, msg=None):
"""
Validate length of sequences, strings, etc.
Parameters:
- min: Minimum allowed length (None for no minimum)
- max: Maximum allowed length (None for no maximum)
- msg: Custom error message
Returns:
Original object if length is within range
Raises:
LengthInvalid: If length is outside specified range
"""Usage Examples:
from voluptuous import Schema, Length, All
# String length validation
username_length = Length(min=3, max=20)
password_length = Length(min=8) # At least 8 characters
description_length = Length(max=500) # At most 500 characters
# List length validation
tags_length = Length(min=1, max=10) # 1-10 tags
items_length = Length(min=0, max=100) # 0-100 items
# Combined validation
user_schema = Schema({
'username': All(str, username_length),
'password': All(str, password_length),
'bio': All(str, description_length),
'tags': All([str], tags_length),
})
# Dictionary length (number of keys)
config_schema = Schema(All(
dict,
Length(min=1), # At least one configuration key
))
# Valid examples
user_schema({
'username': 'john_doe', # 8 characters (valid)
'password': 'secretpassword', # 14 characters (valid)
'bio': 'Software developer', # 18 characters (valid)
'tags': ['python', 'web'], # 2 tags (valid)
})Validate numeric precision and scale for high-precision financial or scientific calculations.
class Number:
def __init__(self, precision=None, scale=None, msg=None, yield_decimal=False):
"""
Validate numeric precision and scale.
Parameters:
- precision: Maximum total number of digits (None for no limit)
- scale: Maximum number of digits after decimal point (None for no limit)
- msg: Custom error message
- yield_decimal: Return Decimal object instead of original number
Returns:
Original number or Decimal object if yield_decimal=True
Raises:
Invalid: If number exceeds precision or scale limits
"""Usage Examples:
from voluptuous import Schema, Number, All, Coerce
from decimal import Decimal
# Financial precision validation
price_validator = Number(precision=10, scale=2) # Max 10 digits, 2 decimal places
currency_validator = Number(precision=15, scale=4) # High precision currency
# Scientific precision
measurement_validator = Number(precision=8, scale=6) # Scientific measurements
# With Decimal conversion
financial_schema = Schema({
'amount': All(
Coerce(Decimal), # Convert to Decimal first
Number(precision=12, scale=2, yield_decimal=True) # Validate and return Decimal
),
'rate': Number(precision=6, scale=4), # Interest rate with 4 decimal places
})
# Usage examples:
price_validator(123.45) # Valid: 5 digits total, 2 decimal places
price_validator(12345678.99) # Valid: 10 digits total, 2 decimal places
# price_validator(123.456) # Invalid: too many decimal places
currency_validator(1234567890.1234) # Valid: 14 digits total, 4 decimal places
# Financial calculations
financial_schema({
'amount': '1234.56', # Converted to Decimal(1234.56)
'rate': 0.0525, # 5.25% interest rate
})Validate that values are present or absent in specified containers.
class In:
def __init__(self, container, msg=None):
"""
Validate value is in specified container.
Parameters:
- container: Container to check membership in (list, set, dict keys, etc.)
- msg: Custom error message
Returns:
Original value if found in container
Raises:
InInvalid: If value not found in container
"""
class NotIn:
def __init__(self, container, msg=None):
"""
Validate value is not in specified container.
Parameters:
- container: Container to check membership against
- msg: Custom error message
Returns:
Original value if not found in container
Raises:
NotInInvalid: If value found in container
"""Usage Examples:
from voluptuous import Schema, In, NotIn, All
# Whitelist validation
status_validator = In(['active', 'inactive', 'pending', 'suspended'])
priority_validator = In([1, 2, 3, 4, 5])
country_validator = In(['US', 'CA', 'UK', 'DE', 'FR', 'JP'])
# Blacklist validation
forbidden_usernames = NotIn(['admin', 'root', 'system', 'administrator'])
forbidden_ports = NotIn([22, 23, 25, 53, 80, 110, 443])
# Combined validation
user_schema = Schema({
'username': All(str, forbidden_usernames, Length(min=3)),
'status': status_validator,
'priority': priority_validator,
'country': country_validator,
})
# Server configuration
server_schema = Schema({
'port': All(int, Range(min=1024, max=65535), forbidden_ports),
'protocol': In(['http', 'https', 'tcp', 'udp']),
})
# Dynamic containers
def get_valid_roles():
return ['user', 'moderator', 'admin'] # Could be from database
role_validator = lambda value: In(get_valid_roles())(value)Validate that collections contain specific items or match expected patterns.
class Contains:
def __init__(self, item, msg=None):
"""
Validate sequence contains specific item.
Parameters:
- item: Item that must be present in sequence
- msg: Custom error message
Returns:
Original sequence if item is found
Raises:
ContainsInvalid: If item not found in sequence
"""Usage Examples:
from voluptuous import Schema, Contains, All, Length
# Required items validation
required_permissions = Contains('read') # Must have 'read' permission
required_features = Contains('authentication') # Must include auth feature
# Multiple required items
permissions_schema = All(
[str], # List of strings
Contains('read'), # Must contain 'read'
Contains('write'), # Must contain 'write'
Length(min=2), # At least 2 permissions
)
# Configuration validation
app_config_schema = Schema({
'features': All([str], Contains('core')), # Core feature is required
'plugins': All([str], Length(min=0)), # Optional plugins
})
# Email list validation
email_list_schema = All(
[str],
Contains(lambda emails: any('@company.com' in email for email in emails)), # At least one company email
)Validate sequences against specific structural patterns.
class ExactSequence:
def __init__(self, validators, msg=None):
"""
Validate sequence elements against validators in exact order.
Parameters:
- validators: List of validators, one per sequence element
- msg: Custom error message
Returns:
Validated sequence with each element validated by corresponding validator
Raises:
ExactSequenceInvalid: If sequence length doesn't match validators or validation fails
"""
class Unordered:
def __init__(self, validators, msg=None):
"""
Validate sequence contains elements matching validators in any order.
Parameters:
- validators: List of validators that must all match some element
- msg: Custom error message
Returns:
Original sequence if all validators match some element
Raises:
Invalid: If any validator doesn't match any element
"""
class Unique:
def __init__(self, msg=None):
"""
Ensure sequence contains no duplicate elements.
Parameters:
- msg: Custom error message
Returns:
Original sequence if all elements are unique
Raises:
Invalid: If duplicate elements are found
"""Usage Examples:
from voluptuous import Schema, ExactSequence, Unordered, Unique, All, Range
# Exact sequence validation (fixed structure)
coordinates_validator = ExactSequence([
All(float, Range(min=-90, max=90)), # Latitude
All(float, Range(min=-180, max=180)), # Longitude
All(float, Range(min=0)), # Altitude (optional, positive)
])
rgb_color_validator = ExactSequence([
All(int, Range(min=0, max=255)), # Red
All(int, Range(min=0, max=255)), # Green
All(int, Range(min=0, max=255)), # Blue
])
# Unordered validation (flexible structure)
required_fields_validator = Unordered([
'name', # Must contain 'name'
'email', # Must contain 'email'
'age', # Must contain 'age'
])
# Unique elements validation
unique_tags = All([str], Unique())
unique_ids = All([int], Unique())
# Combined sequence validation
data_schema = Schema({
'coordinates': coordinates_validator, # [lat, lon, alt]
'color': rgb_color_validator, # [r, g, b]
'required_fields': required_fields_validator,
'tags': unique_tags, # Unique string tags
})
# Usage examples:
coordinates_validator([40.7128, -74.0060, 10.0]) # Valid coordinates
rgb_color_validator([255, 128, 0]) # Valid orange color
unique_tags(['python', 'web', 'api']) # Valid unique tagsEnsure value exactly matches a target value (no coercion or transformation).
class Equal:
def __init__(self, target, msg=None):
"""
Ensure value exactly matches target value.
Parameters:
- target: Exact value that input must match
- msg: Custom error message
Returns:
Original value if it exactly matches target
Raises:
Invalid: If value doesn't exactly match target
"""Usage Examples:
from voluptuous import Schema, Equal, Any, Required
# API status validation
status_schema = Schema({
'status': Equal('OK'), # Must be exactly 'OK'
'version': Equal(1), # Must be exactly 1 (integer)
'enabled': Equal(True), # Must be exactly True (boolean)
})
# Configuration validation
config_schema = Schema({
'debug': Equal(False), # Must be exactly False
'max_retries': Equal(3), # Must be exactly 3
'api_version': Any(Equal('v1'), Equal('v2')), # Must be 'v1' or 'v2'
})
# Validate exact matches
status_schema({'status': 'OK', 'version': 1, 'enabled': True}) # Valid
config_schema({'debug': False, 'max_retries': 3, 'api_version': 'v1'}) # Valid
# These would fail:
# status_schema({'status': 'ok'}) # Fails: 'ok' != 'OK' (case sensitive)
# config_schema({'debug': 0}) # Fails: 0 != False (no coercion)Complex validation patterns for sophisticated collection requirements.
Conditional Collection Validation:
from voluptuous import Schema, All, Any, Length, Contains
def conditional_length(data):
"""Different length requirements based on data type."""
if data.get('type') == 'premium':
return All([str], Length(min=5, max=20)) # Premium users get more tags
else:
return All([str], Length(min=1, max=10)) # Regular users get fewer tags
user_schema = Schema({
'type': In(['basic', 'premium']),
'tags': conditional_length,
})Nested Collection Validation:
from voluptuous import Schema, All, Length, Range
# Validate list of lists (matrix)
matrix_validator = All(
[[int]], # List of lists of integers
Length(min=1), # At least one row
lambda matrix: all(len(row) == len(matrix[0]) for row in matrix), # Rectangular
)
# Validate hierarchical data
tree_node = Schema({
'value': int,
'children': All([lambda x: tree_node(x)], Length(max=10)), # Recursive validation
})Collection Aggregation Validation:
from voluptuous import Schema, All
def sum_constraint(target_sum):
"""Validate that numeric list sums to target value."""
def validator(values):
if sum(values) != target_sum:
raise ValueError(f"Sum must equal {target_sum}, got {sum(values)}")
return values
return validator
def average_constraint(min_avg, max_avg):
"""Validate that numeric list average is within range."""
def validator(values):
if not values:
raise ValueError("Cannot calculate average of empty list")
avg = sum(values) / len(values)
if not (min_avg <= avg <= max_avg):
raise ValueError(f"Average {avg} not in range [{min_avg}, {max_avg}]")
return values
return validator
# Budget allocation (percentages must sum to 100)
budget_schema = Schema(All(
[All(float, Range(min=0, max=100))], # Each percentage 0-100
sum_constraint(100.0), # Must sum to 100%
Length(min=1), # At least one category
))
# Performance scores (average must be acceptable)
scores_schema = Schema(All(
[All(int, Range(min=0, max=100))], # Each score 0-100
average_constraint(70, 100), # Average must be 70-100
Length(min=3), # At least 3 scores
))Install with Tessl CLI
npx tessl i tessl/pypi-voluptuous