Claude Code for schema: Lightweight Data Validation in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for schema: Lightweight Data Validation in Python
AI

Claude Code for schema: Lightweight Data Validation in Python

Published: April 12, 2028
Read time: 5 min read
By: Claude Skills 360

schema is a minimal Python validation library with a clean API. pip install schema. Basic: from schema import Schema; Schema({"name": str, "age": int}).validate({"name": "Alice", "age": 30}). Optional: from schema import Optional; Schema({Optional("bio"): str}). Or: from schema import Or; Schema({"status": Or("active", "inactive")}). And: from schema import And; Schema({"age": And(int, lambda n: n > 0)}). Use: from schema import Use; Schema({"count": Use(int)}) — coerce. Regex: from schema import Regex; Schema({"email": Regex(r".+@.+")}). Literal: from schema import Literal; Schema({Literal("type", description="must be 'widget'"): "widget"}). Forbidden: from schema import Forbidden; Schema({Forbidden("password"): object}). List: Schema([int]) — validates list of ints. Validate: s.validate(data) → data or raises SchemaError. is_valid: s.is_valid(data) → bool. Error: from schema import SchemaError; try: s.validate(d) except SchemaError as e: e.errors. schema.error — single error message. Nested: Schema({"address": {"city": str, "zip": str}}). JSON: Schema({"items": [{"id": int, "name": str}]}). Custom: Schema(lambda x: x > 0, error="must be positive"). Default: Schema({Optional("theme", default="light"): str}). Claude Code generates schema validation pipelines, config validators, and API request validators.

CLAUDE.md for schema

## schema Stack
- Version: schema >= 0.7 | pip install schema
- Validate: Schema(spec).validate(data) → validated_data | raises SchemaError
- Check: Schema(spec).is_valid(data) → bool (no exception)
- Types: str, int, float, bool — direct type validation
- Coerce: Use(int) | Use(float) — convert types
- Constraints: And(int, lambda n: n > 0) | Or("a","b","c") | Regex(r"pattern")
- Optional: Optional("key", default=val) — missing key uses default

schema Validation Pipeline

# app/schema_utils.py — schema validation, coerce, custom validators, nested schemas
from __future__ import annotations

import re
from typing import Any, Callable

from schema import (
    And,
    Forbidden,
    Literal,
    Optional,
    Or,
    Regex,
    Schema,
    SchemaError,
    Use,
)


# ─────────────────────────────────────────────────────────────────────────────
# 1. Reusable validators
# ─────────────────────────────────────────────────────────────────────────────

# Email — simple RFC-ish pattern
EMAIL_RE   = Regex(r"^[^@\s]+@[^@\s]+\.[^@\s]+$", error="Invalid email address")
# URL
URL_RE     = Regex(r"^https?://\S+$",               error="Invalid URL")
# Slug: lowercase alphanumeric and hyphens
SLUG_RE    = Regex(r"^[a-z0-9]+(?:-[a-z0-9]+)*$",  error="Invalid slug (use lowercase, numbers, hyphens)")
# ISO 3166-1 alpha-2 country code
COUNTRY_RE = Regex(r"^[A-Z]{2}$", error="Country must be a 2-letter ISO code (e.g. US)")

# Positive number
POS_INT   = And(Use(int),   lambda n: n > 0,  error="Must be a positive integer")
NON_NEG_INT = And(Use(int), lambda n: n >= 0, error="Must be a non-negative integer")
POS_FLOAT = And(Use(float), lambda f: f > 0,  error="Must be a positive number")


def in_range(min_val: float, max_val: float) -> And:
    """Validator: number within [min_val, max_val]."""
    return And(
        Use(float),
        lambda n: min_val <= n <= max_val,
        error=f"Must be between {min_val} and {max_val}",
    )


def min_length(n: int) -> And:
    """Validator: string with at least n chars."""
    return And(str, lambda s: len(s.strip()) >= n, error=f"Must be at least {n} chars")


def max_length(n: int) -> And:
    """Validator: string no longer than n chars."""
    return And(str, lambda s: len(s) <= n, error=f"Must be at most {n} chars")


def between_length(min_n: int, max_n: int) -> And:
    """Validator: string length within [min_n, max_n]."""
    return And(
        str,
        lambda s: min_n <= len(s.strip()) <= max_n,
        error=f"Must be between {min_n} and {max_n} chars",
    )


def one_of(*choices) -> Or:
    """Validator: value must be one of the given choices."""
    return Or(*choices, error=f"Must be one of {choices}")


def non_empty(v: Any) -> Any:
    """Validator: no None, no empty string, no empty list/dict."""
    if v is None or v == "" or v == [] or v == {}:
        raise SchemaError("Must not be empty")
    return v


# ─────────────────────────────────────────────────────────────────────────────
# 2. Common schemas
# ─────────────────────────────────────────────────────────────────────────────

USER_SCHEMA = Schema({
    "name":                between_length(1, 100),
    "email":               And(str, EMAIL_RE),
    Optional("age"):       And(Use(int), lambda n: 0 <= n <= 150),
    Optional("role",
             default="user"): one_of("admin", "moderator", "user"),
    Optional("website"):   And(str, URL_RE),
})

PRODUCT_SCHEMA = Schema({
    "id":                  POS_INT,
    "name":                between_length(1, 200),
    "price":               POS_FLOAT,
    Optional("sku"):       And(str, SLUG_RE),
    Optional("stock",
             default=0):   NON_NEG_INT,
    Optional("tags",
             default=[]):  [str],
    Optional("active",
             default=True): bool,
})

PAGINATION_SCHEMA = Schema({
    Optional("page",     default=1):  And(Use(int), lambda n: n >= 1),
    Optional("per_page", default=20): And(Use(int), lambda n: 1 <= n <= 500),
    Optional("sort_by",  default="id"): str,
    Optional("order",    default="asc"): one_of("asc", "desc"),
})

ADDRESS_SCHEMA = Schema({
    "street":           between_length(1, 200),
    "city":             min_length(1),
    Optional("state"):  str,
    "country":          And(str, COUNTRY_RE),
    Optional("postal_code"): And(str, Regex(r"^[\w\s-]{3,10}$")),
})


# ─────────────────────────────────────────────────────────────────────────────
# 3. Validation helpers
# ─────────────────────────────────────────────────────────────────────────────

def validate(schema: Schema, data: Any) -> tuple[Any, str | None]:
    """
    Validate data against schema.
    Returns (validated, None) on success or (None, error_message) on failure.
    """
    try:
        return schema.validate(data), None
    except SchemaError as e:
        return None, str(e)


def is_valid(schema: Schema, data: Any) -> bool:
    """Return True if data passes schema validation."""
    return schema.is_valid(data)


def validate_many(
    schema: Schema,
    items: list[Any],
) -> tuple[list[Any], dict[int, str]]:
    """
    Validate a list of items.
    Returns (valid_items, {index: error_message}).
    """
    valid: list[Any] = []
    errors: dict[int, str] = {}
    for i, item in enumerate(items):
        result, err = validate(schema, item)
        if err:
            errors[i] = err
        else:
            valid.append(result)
    return valid, errors


def validate_or_raise(schema: Schema, data: Any, error_cls=ValueError) -> Any:
    """Validate; raise error_cls with message on failure."""
    result, err = validate(schema, data)
    if err:
        raise error_cls(err)
    return result


def collect_errors(schemas: dict[str, Schema], data: dict[str, Any]) -> dict[str, str]:
    """
    Validate each key in data against its named schema.
    Returns {field_name: error_message} for invalid fields.

    Example:
        errors = collect_errors({"email": EMAIL_SCHEMA, "age": AGE_SCHEMA}, form_data)
    """
    errors: dict[str, str] = {}
    for field, schema in schemas.items():
        if field in data:
            _, err = validate(schema, data[field])
            if err:
                errors[field] = err
    return errors


# ─────────────────────────────────────────────────────────────────────────────
# 4. Nested schema helpers
# ─────────────────────────────────────────────────────────────────────────────

ORDER_ITEM_SCHEMA = Schema({
    "product_id": POS_INT,
    "quantity":   POS_INT,
    "price":      POS_FLOAT,
})

ORDER_SCHEMA = Schema({
    "order_id":  And(str, min_length(1)),
    "customer":  USER_SCHEMA,
    "items":     And([ORDER_ITEM_SCHEMA], lambda items: len(items) > 0,
                     error="Order must have at least one item"),
    "address":   ADDRESS_SCHEMA,
    Optional("notes", default=""): str,
})


# ─────────────────────────────────────────────────────────────────────────────
# 5. Config validation
# ─────────────────────────────────────────────────────────────────────────────

def make_config_schema(fields: dict[str, Any]) -> Schema:
    """
    Build a Schema from a {key: validator} dict.

    Example:
        schema = make_config_schema({
            "host": str,
            "port": And(Use(int), lambda p: 1 <= p <= 65535),
            Optional("debug", default=False): Use(bool),
        })
    """
    return Schema(fields)


DATABASE_CONFIG_SCHEMA = Schema({
    "host":                  str,
    "port":                  And(Use(int), lambda p: 1 <= p <= 65535, error="Invalid port"),
    "name":                  min_length(1),
    "user":                  str,
    Optional("password", default=""): str,
    Optional("pool_size", default=5): And(Use(int), lambda n: 1 <= n <= 100),
    Optional("ssl",      default=False): Use(bool),
})


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== USER_SCHEMA — valid ===")
    user, err = validate(USER_SCHEMA, {
        "name": "Alice",
        "email": "[email protected]",
        "age": "30",
        "role": "admin",
        "website": "https://alice.dev",
    })
    print("user:", user, "error:", err)

    print("\n=== USER_SCHEMA — invalid ===")
    _, err = validate(USER_SCHEMA, {
        "name": "",
        "email": "bad",
        "age": -5,
        "role": "superuser",
    })
    print("error:", err[:120] if err else None)

    print("\n=== PRODUCT_SCHEMA with coercion ===")
    prod, err = validate(PRODUCT_SCHEMA, {
        "id": "42",
        "name": "Widget",
        "price": "9.99",
        "tags": ["python", "tool"],
    })
    print("product:", prod)

    print("\n=== validate_many ===")
    items = [
        {"id": "1", "name": "A", "price": "1.99"},
        {"id": "-1", "name": "", "price": "-5"},
        {"id": "2", "name": "B", "price": "4.99"},
    ]
    valid_items, errs = validate_many(PRODUCT_SCHEMA, items)
    print(f"Valid: {len(valid_items)}, Errors: {dict(errs.items())}")

    print("\n=== Nested ORDER_SCHEMA ===")
    order = {
        "order_id": "ORD-001",
        "customer": {"name": "Bob", "email": "[email protected]"},
        "items": [
            {"product_id": "1", "quantity": "2", "price": "9.99"},
        ],
        "address": {"street": "123 Main St", "city": "Portland", "country": "US"},
    }
    validated_order, err = validate(ORDER_SCHEMA, order)
    print("order valid:", err is None)
    if err:
        print("error:", err)

    print("\n=== DATABASE_CONFIG_SCHEMA ===")
    cfg, err = validate(DATABASE_CONFIG_SCHEMA, {
        "host": "db.internal",
        "port": "5432",
        "name": "myapp",
        "user": "postgres",
    })
    print("config:", cfg)

    print("\n=== Custom range validator ===")
    score_schema = Schema({"score": in_range(0, 100)})
    ok, _ = validate(score_schema, {"score": 85})
    print("score 85 ok:", ok)
    _, err2 = validate(score_schema, {"score": 150})
    print("score 150 error:", err2[:80] if err2 else None)

For the voluptuous alternative — voluptuous uses Required/Optional key wrappers, Schema({...}, extra=...) for unknown-key handling, and All()/Any() for compound rules; schema uses a simpler And()/Or() style with direct Optional("key", default=val) and Use(type) coercion — both are functional but schema’s API feels closer to writing natural Python conditions. For the cerberus alternative — cerberus uses string-typed rule dicts ({"type": "integer", "min": 0}) which are easy to serialize but verbose to write; schema defines rules as Python expressions (And(Use(int), lambda n: n >= 0)) that can reference closures, functions, and imported validators directly. The Claude Skills 360 bundle includes schema skill sets covering Schema validate/is_valid, And/Or/Use/Regex/Optional/Forbidden validators, reusable validator constants (EMAIL_RE, SLUG_RE, POS_INT), between_length/in_range/one_of helpers, USER_SCHEMA/PRODUCT_SCHEMA/PAGINATION_SCHEMA/ADDRESS_SCHEMA examples, validate()/validate_many()/collect_errors() helpers, nested ORDER_SCHEMA, DATABASE_CONFIG_SCHEMA, and make_config_schema() factory. Start with the free tier to try schema validation code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free