CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-dataclasses-json

Easily serialize dataclasses to and from JSON.

Pending
Overview
Eval results
Files

global-configuration.mddocs/

Global Configuration

Package-wide settings for custom encoders, decoders, and marshmallow fields that apply across all dataclasses unless overridden at the field level. Global configuration provides a centralized way to define type-specific serialization behavior.

Capabilities

Global Configuration Object

The global_config object provides package-wide settings that affect all dataclass serialization unless overridden.

class _GlobalConfig:
    """
    Global configuration singleton for package-wide serialization settings.
    """
    encoders: Dict[Union[type, Optional[type]], Callable]
    decoders: Dict[Union[type, Optional[type]], Callable]  
    mm_fields: Dict[Union[type, Optional[type]], marshmallow.fields.Field]

global_config: _GlobalConfig

The global configuration object contains three main dictionaries:

  • encoders: Maps types to functions that convert Python values to JSON-serializable values
  • decoders: Maps types to functions that convert JSON values back to Python values
  • mm_fields: Maps types to marshmallow Field instances for validation and transformation

Global Encoder Configuration

Register custom encoders for specific types that will be used across all dataclasses.

# Encoder function signature
EncoderFunc = Callable[[Any], Any]  # Python value -> JSON-serializable value

Usage example:

from dataclasses import dataclass
from dataclasses_json import dataclass_json, global_config
from datetime import date, datetime
from decimal import Decimal
import uuid

# Register global encoders
global_config.encoders[date] = lambda d: d.isoformat()
global_config.encoders[datetime] = lambda dt: dt.isoformat()
global_config.encoders[Decimal] = str
global_config.encoders[uuid.UUID] = str

@dataclass_json
@dataclass
class Document:
    id: uuid.UUID
    title: str
    created_date: date
    modified_time: datetime
    price: Decimal

# All instances automatically use global encoders
doc = Document(
    id=uuid.uuid4(),
    title="Sample Document", 
    created_date=date.today(),
    modified_time=datetime.now(),
    price=Decimal("19.99")
)

json_str = doc.to_json()
# Uses global encoders for UUID, date, datetime, and Decimal

Global Decoder Configuration

Register custom decoders for converting JSON values back to Python types.

# Decoder function signature  
DecoderFunc = Callable[[Any], Any]  # JSON value -> Python value

Usage example:

from datetime import date, datetime
from decimal import Decimal
import uuid

# Register global decoders
global_config.decoders[date] = lambda s: datetime.fromisoformat(s).date()
global_config.decoders[datetime] = datetime.fromisoformat
global_config.decoders[Decimal] = Decimal
global_config.decoders[uuid.UUID] = uuid.UUID

# Now all dataclasses can deserialize these types automatically
json_data = '''{
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "title": "Sample Document",
    "created_date": "2023-12-01",
    "modified_time": "2023-12-01T10:30:00",
    "price": "19.99"
}'''

doc = Document.from_json(json_data)
# Automatically converts strings back to proper types

Global Marshmallow Field Configuration

Register marshmallow fields for types that require validation or complex transformation.

from marshmallow import fields

# Common marshmallow field types
Field = fields.Field

Usage example:

from marshmallow import fields
from datetime import datetime
from decimal import Decimal

# Register global marshmallow fields
global_config.mm_fields[datetime] = fields.DateTime(format='iso')
global_config.mm_fields[Decimal] = fields.Decimal(places=2, rounding='ROUND_HALF_UP')
global_config.mm_fields[str] = fields.String(validate=lambda x: len(x.strip()) > 0)

@dataclass_json
@dataclass
class Order:
    timestamp: datetime
    amount: Decimal
    description: str

# Schema validation uses global mm_fields
schema = Order.schema()

# This will validate using global field definitions
try:
    order = schema.loads('{"timestamp": "2023-12-01T10:30:00", "amount": "19.999", "description": ""}')
except ValidationError as e:
    print("Validation failed:", e)  # Empty description fails validation

Configuration Priority

Configuration follows a clear precedence hierarchy:

  1. Field-level configuration (highest priority)
  2. Global configuration
  3. Library defaults (lowest priority)

Example demonstrating precedence:

from dataclasses import dataclass, field
from dataclasses_json import dataclass_json, config, global_config
from datetime import datetime

# Global configuration
global_config.encoders[datetime] = lambda dt: dt.strftime('%Y-%m-%d')

@dataclass_json
@dataclass
class Event:
    # Uses global encoder (YYYY-MM-DD format)
    start_time: datetime
    
    # Field-level config overrides global (ISO format)
    end_time: datetime = field(metadata=config(
        encoder=lambda dt: dt.isoformat()
    ))

event = Event(datetime.now(), datetime.now())
json_str = event.to_json()
# start_time uses global encoder, end_time uses field-level encoder

Advanced Global Configuration Patterns

Type Hierarchy Configuration

Configure encoders for base classes that apply to all subclasses:

from abc import ABC, abstractmethod

class SerializableEntity(ABC):
    @abstractmethod
    def to_dict(self):
        pass

# Global encoder for all SerializableEntity subclasses
global_config.encoders[SerializableEntity] = lambda obj: obj.to_dict()

class User(SerializableEntity):
    def __init__(self, name: str):
        self.name = name
    
    def to_dict(self):
        return {"name": self.name}

@dataclass_json
@dataclass
class Document:
    author: User  # Uses global SerializableEntity encoder
    title: str

Optional Type Configuration

Handle Optional types explicitly:

from typing import Optional
from datetime import datetime

# Configure for Optional[datetime]
global_config.encoders[Optional[datetime]] = lambda dt: dt.isoformat() if dt else None
global_config.decoders[Optional[datetime]] = lambda s: datetime.fromisoformat(s) if s else None

@dataclass_json
@dataclass
class Task:
    name: str
    due_date: Optional[datetime] = None  # Uses Optional[datetime] configuration

Environment-Based Configuration

Configure based on runtime environment:

import os
from datetime import datetime

# Development vs Production encoders
if os.getenv('ENV') == 'development':
    # Verbose format for debugging
    global_config.encoders[datetime] = lambda dt: {
        'timestamp': dt.isoformat(),
        'readable': dt.strftime('%Y-%m-%d %H:%M:%S'),
        'timezone': str(dt.tzinfo)
    }
else:
    # Compact format for production
    global_config.encoders[datetime] = lambda dt: dt.timestamp()

Configuration Validation

Validate global configuration at startup:

def validate_global_config():
    """Validate that all global encoders have corresponding decoders."""
    encoder_types = set(global_config.encoders.keys())
    decoder_types = set(global_config.decoders.keys())
    
    missing_decoders = encoder_types - decoder_types
    if missing_decoders:
        raise ValueError(f"Missing decoders for types: {missing_decoders}")

# Call during application initialization
validate_global_config()

Configuration Backup and Restore

Save and restore configuration state:

def backup_global_config():
    """Create a backup of current global configuration."""
    return {
        'encoders': global_config.encoders.copy(),
        'decoders': global_config.decoders.copy(),
        'mm_fields': global_config.mm_fields.copy()
    }

def restore_global_config(backup):
    """Restore global configuration from backup."""
    global_config.encoders.clear()
    global_config.encoders.update(backup['encoders'])
    global_config.decoders.clear()
    global_config.decoders.update(backup['decoders'])
    global_config.mm_fields.clear()
    global_config.mm_fields.update(backup['mm_fields'])

# Usage in tests
def test_with_custom_config():
    backup = backup_global_config()
    try:
        # Modify global config for test
        global_config.encoders[str] = lambda s: s.upper()
        # ... run test ...
    finally:
        restore_global_config(backup)

Performance Considerations

Global configuration lookups are performed for every field serialization. For optimal performance:

  • Keep global configuration minimal and focused on frequently used types
  • Use field-level configuration for one-off customizations
  • Consider caching for expensive encoder/decoder operations
from functools import lru_cache

# Cache expensive transformations
@lru_cache(maxsize=1000)
def expensive_encoder(value):
    # Complex transformation logic
    return transformed_value

global_config.encoders[ComplexType] = expensive_encoder

Install with Tessl CLI

npx tessl i tessl/pypi-dataclasses-json

docs

core-serialization.md

field-configuration.md

global-configuration.md

index.md

undefined-parameters.md

tile.json