or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced.mdconversions.mdindex.mdmetadata.mdschema-generation.mdserialization.mdvalidation.md
tile.json

validation.mddocs/

Validation

Comprehensive validation framework with structured error reporting, field-level validators, custom validation functions, and detailed error location tracking.

Capabilities

ValidationError

Structured exception class that provides detailed information about validation failures with precise location tracking.

class ValidationError(Exception):
    """
    Exception raised when validation fails.
    
    Provides structured error information with location tracking
    for precise error reporting and debugging.
    """
    
    def __init__(
        self,
        messages: Optional[Union[ErrorMsg, Sequence[ErrorMsg]]] = None,
        children: Optional[Mapping[ErrorKey, "ValidationError"]] = None,
    ):
        """
        Initialize validation error.
        
        Parameters:
        - messages: Error messages for this location
        - children: Nested validation errors for child fields/elements
        """
    
    @property
    def errors(self) -> List[LocalizedError]:
        """
        Get all errors as a flat list with location information.
        
        Returns:
        List of errors with 'loc' (location path) and 'err' (error message)
        """
    
    @staticmethod
    def from_errors(errors: Sequence[LocalizedError]) -> "ValidationError":
        """
        Create ValidationError from a list of localized errors.
        
        Parameters:
        - errors: List of error dictionaries with 'loc' and 'err' keys
        
        Returns:
        ValidationError instance
        """

Validator Decorator

Decorator for registering validation functions that can be applied to types, fields, or during operations.

def validator(
    func: Optional[Callable] = None,
    *,
    field: Any = None,
    discard: Any = None,
    owner: Optional[Type] = None,
) -> Callable:
    """
    Register a validation function.
    
    Parameters:
    - func: Validation function to register
    - field: Specific field to validate (default: whole object)
    - discard: Fields to discard when validation passes
    - owner: Owner class for field validation
    
    Returns:
    Decorated validation function
    """

Validator Class

Configuration class for detailed validator setup with field targeting and error handling.

class Validator:
    """
    Validator configuration with field targeting and options.
    """
    
    def __init__(
        self,
        func: Callable,
        field: Optional[FieldOrName] = None,
        discard: Optional[Collection[FieldOrName]] = None,
    ):
        """
        Initialize validator.
        
        Parameters:
        - func: Validation function 
        - field: Target field for validation
        - discard: Fields to discard on successful validation
        """

Validation Functions

Functions for applying validators to objects and handling validation results.

def validate(
    obj: T,
    validators: Optional[Iterable[Validator]] = None,
    kwargs: Optional[Mapping[str, Any]] = None,
    *,
    aliaser: Aliaser = lambda s: s,
) -> T:
    """
    Apply validators to an object.
    
    Parameters:
    - obj: Object to validate
    - validators: List of validators to apply
    - kwargs: Additional arguments for validation functions
    - aliaser: Function to transform field names
    
    Returns:
    Validated object (may be modified if fields discarded)
    
    Raises:
    ValidationError: If validation fails
    """

Discard Exception

Exception for conditionally removing fields during validation.

class Discard(Exception):
    """
    Exception for discarding fields during validation.
    
    Allows validators to conditionally remove fields from objects
    while providing error information.
    """
    
    def __init__(self, fields: Optional[AbstractSet[str]], error: ValidationError):
        """
        Initialize discard exception.
        
        Parameters:  
        - fields: Field names to discard
        - error: Associated validation error
        """

Usage Examples

Basic Field Validation

from dataclasses import dataclass
from apischema import validator, deserialize, ValidationError

@dataclass
class User:
    name: str
    age: int
    email: str

@validator
def validate_age(age: int) -> int:
    """Validate age is reasonable."""
    if age < 0:
        raise ValueError("Age cannot be negative")
    if age > 150:
        raise ValueError("Age cannot exceed 150")
    return age

@validator  
def validate_email(email: str) -> str:
    """Validate email format."""
    if "@" not in email:
        raise ValueError("Invalid email format")
    return email

# Register validators for specific fields
validator(validate_age, field="age", owner=User)
validator(validate_email, field="email", owner=User)

# Validation during deserialization
try:
    user = deserialize(User, {"name": "John", "age": -5, "email": "invalid"})
except ValidationError as err:
    for error in err.errors:
        print(f"{error['loc']}: {error['err']}")
    # ['age']: Age cannot be negative
    # ['email']: Invalid email format

Custom Validation Functions

from typing import List

@dataclass
class Product:
    name: str
    price: float
    tags: List[str]

@validator
def validate_product(product: Product) -> Product:
    """Validate entire product object."""
    if product.price <= 0:
        raise ValueError("Price must be positive")
    
    if len(product.name.strip()) == 0:
        raise ValueError("Name cannot be empty")
    
    if len(product.tags) == 0:
        raise ValueError("Product must have at least one tag")
    
    return product

# Apply validator to the Product class
validator(validate_product, owner=Product)

try:
    product = deserialize(Product, {
        "name": "",
        "price": -10,
        "tags": []
    })
except ValidationError as err:
    print(err.errors)
    # Multiple validation errors for the product object

Conditional Field Validation

@dataclass
class Account:
    type: str  # "personal" or "business"
    name: str
    tax_id: Optional[str] = None

@validator
def validate_business_tax_id(account: Account) -> Account:
    """Business accounts must have tax ID."""
    if account.type == "business" and not account.tax_id:
        raise ValueError("Business accounts require tax ID")
    return account

validator(validate_business_tax_id, owner=Account)

# Valid personal account
personal = deserialize(Account, {"type": "personal", "name": "John Doe"})

# Invalid business account  
try:
    business = deserialize(Account, {"type": "business", "name": "ACME Corp"})
except ValidationError as err:
    print(err.errors)  # Missing tax ID error

Field Discarding

from apischema.validation import Discard

@dataclass
class UserInput:
    username: str
    password: str
    password_confirm: str

@validator
def validate_passwords(user: UserInput) -> UserInput:
    """Validate passwords match and discard confirmation field."""
    if user.password != user.password_confirm:
        raise ValidationError([{
            "loc": ["password_confirm"], 
            "err": "Passwords do not match"
        }])
    
    # Discard password_confirm field after validation
    raise Discard({"password_confirm"}, ValidationError([]))

validator(validate_passwords, owner=UserInput)

# After successful validation, password_confirm is removed
user_data = {"username": "john", "password": "secret", "password_confirm": "secret"}
user = deserialize(UserInput, user_data)
# user object won't have password_confirm field

Validation with Custom Error Messages

@validator
def validate_username(username: str) -> str:
    """Validate username with detailed error messages."""
    if len(username) < 3:
        raise ValueError("Username must be at least 3 characters long")
    
    if not username.isalnum():
        raise ValueError("Username must contain only letters and numbers")
    
    if username.lower() in ["admin", "root", "user"]:
        raise ValueError("Username is reserved and cannot be used")
    
    return username

validator(validate_username, field="username", owner=User)

Multiple Validators

@dataclass
class Order:
    items: List[Dict[str, Any]]
    total: float
    discount: float = 0.0

@validator
def validate_items_not_empty(items: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
    """Order must have at least one item."""
    if not items:
        raise ValueError("Order must contain at least one item")
    return items

@validator  
def validate_total_positive(total: float) -> float:
    """Total must be positive.""" 
    if total <= 0:
        raise ValueError("Order total must be positive")
    return total

@validator
def validate_discount_range(discount: float) -> float:
    """Discount must be between 0 and 1."""
    if not 0 <= discount <= 1:
        raise ValueError("Discount must be between 0 and 1")
    return discount

# Register all validators
validator(validate_items_not_empty, field="items", owner=Order)
validator(validate_total_positive, field="total", owner=Order)
validator(validate_discount_range, field="discount", owner=Order)

Additional Validation APIs

Functions for working with validators programmatically and retrieving validation information.

def get_validators(tp: AnyType) -> Sequence[Validator]:
    """
    Get all validators registered for a given type.
    
    Parameters:
    - tp: The type to get validators for
    
    Returns:
    Sequence of Validator objects registered for the type
    """

Type Aliases

ErrorMsg = str  # Error message string
ErrorKey = Union[str, int]  # Key for error location (field name or array index)
LocalizedError = Dict[str, Any]  # Error dict with 'loc' and 'err' keys
Error = Union[ErrorMsg, Tuple[Any, ErrorMsg]]  # Basic error type
ValidatorResult = Generator[Error, None, T]  # Generator-based validator result type
FieldOrName = Union[str, Any]  # Field reference or field name
Aliaser = Callable[[str], str]  # Function for transforming field names