CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-apischema

JSON (de)serialization, GraphQL and JSON schema generation using Python typing.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

conversions.mddocs/

Type Conversions

Extensible conversion system for custom serialization and deserialization logic. Register converters for any types including third-party libraries, ORM models, and custom transformations.

Capabilities

Deserializer Registration

Register custom deserialization converters that transform data during the deserialization process.

def deserializer(
    deserializer: Optional[Deserializer] = None,
    *,
    lazy: Optional[Callable[[], Union[Converter, Conversion]]] = None,
    target: Optional[Type] = None,
) -> Callable:
    """
    Register a deserialization converter.
    
    Parameters:
    - deserializer: Function that converts data to target type
    - lazy: Lazy-loaded converter function (for avoiding import cycles)
    - target: Target type for the converter (inferred if not provided)
    
    Returns:
    Decorator function for registration
    """

Serializer Registration

Register custom serialization converters that transform objects during the serialization process.

def serializer(
    serializer: Optional[Serializer] = None,
    *,
    lazy: Optional[Callable[[], Union[Converter, Conversion]]] = None,
    source: Optional[Type] = None,
) -> Callable:
    """
    Register a serialization converter.
    
    Parameters:
    - serializer: Function that converts object to serializable data
    - lazy: Lazy-loaded converter function (for avoiding import cycles)
    - source: Source type for the converter (inferred if not provided)
    
    Returns:
    Decorator function for registration
    """

Conversion Configuration

Classes for configuring conversion behavior with detailed options and lazy loading.

@dataclass(frozen=True)
class Conversion:
    """
    Conversion configuration with converter function and type information.
    
    Attributes:
    - converter: Function that performs the conversion
    - source: Source type (for serialization converters)
    - target: Target type (for deserialization converters)  
    - inherited: Whether conversion applies to subclasses
    """
    converter: Callable
    source: Optional[AnyType] = None
    target: Optional[AnyType] = None
    inherited: Optional[bool] = None

@dataclass(frozen=True)
class LazyConversion:
    """
    Lazy-loaded conversion configuration.
    
    Attributes:
    - func: Function that returns Conversion or Converter when called
    """
    func: Callable[[], Union[Conversion, Callable]]

Utility Converters

Built-in utility functions for common conversion patterns.

def catch_value_error(func: Func) -> Func:
    """
    Decorator that catches ValueError and re-raises as ValidationError.
    
    Parameters:
    - func: Function to wrap
    
    Returns:
    Wrapped function that converts ValueError to ValidationError
    """

def as_str(cls: Cls) -> Cls:
    """
    Register string conversion for enum classes.
    
    Automatically registers serialization to string (enum.value or enum.name)
    and deserialization from string.
    
    Parameters:
    - cls: Enum class to register converters for
    
    Returns:
    Enum class (for use as decorator)
    """

def as_names(cls: EnumCls, aliaser: Callable[[str], str] = lambda s: s) -> EnumCls:
    """
    Register name-based conversion for enum classes with optional aliasing.
    
    Parameters:
    - cls: Enum class to register converters for
    - aliaser: Function to transform enum names (e.g., snake_case to camelCase)
    
    Returns:
    Enum class (for use as decorator)
    """

Usage Examples

Basic Type Conversion

from datetime import datetime
from typing import NewType
from apischema import deserializer, serializer

# Custom type for timestamps
Timestamp = NewType("Timestamp", int)

@deserializer
def deserialize_timestamp(timestamp: int) -> datetime:
    """Convert Unix timestamp to datetime."""
    return datetime.fromtimestamp(timestamp)

@serializer  
def serialize_timestamp(dt: datetime) -> int:
    """Convert datetime to Unix timestamp."""
    return int(dt.timestamp())

# Now datetime objects can be serialized/deserialized as timestamps
@dataclass
class Event:
    name: str
    created_at: datetime

data = {"name": "Meeting", "created_at": 1672531200}  # Unix timestamp
event = deserialize(Event, data)
print(event.created_at)  # datetime(2023, 1, 1, 0, 0)

result = serialize(Event, event)
print(result)  # {"name": "Meeting", "created_at": 1672531200}

Enum Conversions

from enum import Enum
from apischema.conversions import as_str, as_names

@as_str
class Status(Enum):
    PENDING = "pending"
    APPROVED = "approved" 
    REJECTED = "rejected"

@as_names  # Use enum names instead of values
class Priority(Enum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3

@dataclass
class Task:
    title: str
    status: Status
    priority: Priority

# Serialization uses enum values/names
data = {"title": "Review", "status": "approved", "priority": "HIGH"}
task = deserialize(Task, data)
print(task.status)  # Status.APPROVED
print(task.priority)  # Priority.HIGH

result = serialize(Task, task)
print(result)  # {"title": "Review", "status": "approved", "priority": "HIGH"}

Custom Object Conversion

from decimal import Decimal

@deserializer
def deserialize_decimal(value: str) -> Decimal:
    """Convert string to Decimal for precise arithmetic."""
    return Decimal(value)

@serializer
def serialize_decimal(value: Decimal) -> str:
    """Convert Decimal to string representation."""
    return str(value)

@dataclass
class Price:
    amount: Decimal
    currency: str

# Decimal values are handled as strings in JSON
data = {"amount": "99.99", "currency": "USD"}
price = deserialize(Price, data)
print(type(price.amount))  # <class 'decimal.Decimal'>

result = serialize(Price, price)
print(result)  # {"amount": "99.99", "currency": "USD"}

Third-Party Library Integration

import uuid
from dataclasses import dataclass

@deserializer
def deserialize_uuid(value: str) -> uuid.UUID:
    """Convert string to UUID object."""
    return uuid.UUID(value)

@serializer
def serialize_uuid(value: uuid.UUID) -> str:
    """Convert UUID to string representation."""
    return str(value)

@dataclass
class User:
    id: uuid.UUID
    name: str

# UUIDs are handled as strings in JSON
data = {"id": "123e4567-e89b-12d3-a456-426614174000", "name": "John"}
user = deserialize(User, data)
print(type(user.id))  # <class 'uuid.UUID'>

ORM Model Integration

# Example with SQLAlchemy models
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class UserModel(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    email = Column(String)

@deserializer
def deserialize_user_model(data: dict) -> UserModel:
    """Convert dict to SQLAlchemy model instance."""
    return UserModel(**data)

@serializer  
def serialize_user_model(user: UserModel) -> dict:
    """Convert SQLAlchemy model to dict."""
    return {
        "id": user.id,
        "name": user.name,
        "email": user.email
    }

# Now UserModel can be used in apischema serialization
@dataclass
class UserResponse:
    user: UserModel
    metadata: dict

data = {
    "user": {"id": 1, "name": "John", "email": "john@example.com"},
    "metadata": {"source": "database"}
}
response = deserialize(UserResponse, data)
print(type(response.user))  # <class 'UserModel'>

Lazy Conversion Loading

# Avoid import cycles with lazy loading
@deserializer(lazy=lambda: datetime_converter)
def lazy_datetime_deserializer():
    """Lazy-loaded datetime converter."""
    pass

def datetime_converter(timestamp: int) -> datetime:
    """Actual conversion function loaded lazily."""
    return datetime.fromtimestamp(timestamp)

Error Handling in Conversions

from apischema.conversions import catch_value_error

@catch_value_error
@deserializer
def deserialize_positive_int(value: str) -> int:
    """Convert string to positive integer."""
    result = int(value)
    if result <= 0:
        raise ValueError("Value must be positive")
    return result

# ValueError is automatically converted to ValidationError
try:
    deserialize(int, "-5")  # Uses the converter
except ValidationError as err:
    print(err.errors)  # Structured error with location info

Conditional Conversions

from typing import Union

@deserializer
def deserialize_flexible_number(value: Union[str, int, float]) -> float:
    """Convert various number formats to float."""
    if isinstance(value, str):
        if value.endswith('%'):
            return float(value[:-1]) / 100
        return float(value)
    return float(value)

@dataclass  
class Statistics:
    growth_rate: float  # Can accept "15.5%", "0.155", 15.5, etc.
    
data = {"growth_rate": "15.5%"}
stats = deserialize(Statistics, data)
print(stats.growth_rate)  # 0.155

Multiple Conversion Strategies

# Register different converters for different contexts
@serializer
def serialize_datetime_iso(dt: datetime) -> str:
    """Serialize datetime as ISO string."""
    return dt.isoformat()

@serializer  
def serialize_datetime_timestamp(dt: datetime) -> int:
    """Serialize datetime as Unix timestamp."""
    return int(dt.timestamp())

# Choose converter at call time
event_data = serialize(Event, event, conversion=serialize_datetime_iso)
timestamp_data = serialize(Event, event, conversion=serialize_datetime_timestamp)

Conversion Management

Functions for managing and clearing registered conversions.

def reset_deserializers(cls: Type) -> None:
    """
    Clear all registered deserializers for a given type.
    
    Parameters:
    - cls: The type to clear deserializers for
    """

def reset_serializer(cls: Type) -> None:
    """
    Clear all registered serializers for a given type.
    
    Parameters:
    - cls: The type to clear serializers for
    """

Type Aliases

Converter = Callable[[Any], Any]  # Basic conversion function

Install with Tessl CLI

npx tessl i tessl/pypi-apischema

docs

advanced.md

conversions.md

index.md

metadata.md

schema-generation.md

serialization.md

validation.md

tile.json