Simple creation of data classes from dictionaries with advanced type support and validation
npx @tessl/cli install tessl/pypi-dacite@1.9.0Dacite simplifies the creation of Python data classes from dictionaries. It bridges the gap between raw dictionary data (from HTTP requests, databases, etc.) and strongly-typed data structures, enabling developers to create robust data transfer objects (DTOs) with minimal boilerplate code while maintaining type safety.
pip install dacitefrom dacite import from_dict, ConfigFor exceptions:
from dacite import (
DaciteError,
DaciteFieldError,
WrongTypeError,
MissingValueError,
UnionMatchError,
StrictUnionMatchError,
ForwardReferenceError,
UnexpectedDataError,
)For cache management:
from dacite import set_cache_size, get_cache_size, clear_cachefrom dataclasses import dataclass
from dacite import from_dict
@dataclass
class User:
name: str
age: int
is_active: bool
data = {
'name': 'John',
'age': 30,
'is_active': True,
}
user = from_dict(data_class=User, data=data)
assert user == User(name='John', age=30, is_active=True)The core functionality that converts dictionaries to dataclass instances with full type support and validation.
def from_dict(data_class: Type[T], data: Data, config: Optional[Config] = None) -> T:
"""
Create a data class instance from a dictionary.
Args:
data_class: A data class type to create an instance of
data: Dictionary-like data to convert (must support keys(), __getitem__, __contains__)
config: Optional configuration for controlling the conversion process
Returns:
An instance of the specified data class
Raises:
DaciteError: Base exception for all dacite-related errors
WrongTypeError: When a value has the wrong type for a field
MissingValueError: When a required field is missing from the data
UnionMatchError: When a value cannot match any union type
StrictUnionMatchError: When multiple union types match in strict mode
ForwardReferenceError: When forward references cannot be resolved
UnexpectedDataError: When strict mode encounters unexpected fields
"""Control the conversion process with various options for type handling, validation, and data transformation.
@dataclass
class Config:
"""
Configuration object for controlling from_dict behavior.
Attributes:
type_hooks: Dictionary mapping types to transformation functions
cast: List of types to attempt casting values to
forward_references: Dictionary for resolving forward references
check_types: Enable/disable runtime type checking (default: True)
strict: Enable strict mode to reject unexpected fields (default: False)
strict_unions_match: Enable strict matching for union types (default: False)
convert_key: Function to transform dictionary keys (default: identity function)
"""
type_hooks: Dict[Type, Callable[[Any], Any]]
cast: List[Type]
forward_references: Optional[Dict[str, Any]]
check_types: bool
strict: bool
strict_unions_match: bool
convert_key: Callable[[str], str]Usage example with configuration:
from dacite import from_dict, Config
from dataclasses import dataclass
from typing import Optional
@dataclass
class Person:
name: str
age: Optional[int]
email: str
config = Config(
type_hooks={str: str.strip}, # Strip whitespace from strings
check_types=True, # Enable type checking
strict=True, # Reject unexpected fields
convert_key=lambda key: key.lower() # Convert keys to lowercase
)
data = {"NAME": " John Doe ", "AGE": None, "EMAIL": "john@example.com"}
person = from_dict(Person, data, config)Control internal caching for performance optimization, particularly useful for applications with many dataclass conversions.
def set_cache_size(size: Optional[int]) -> None:
"""
Set the maximum cache size for internal caching.
Args:
size: Maximum cache size (None for unlimited, 0 to disable caching)
"""
def get_cache_size() -> Optional[int]:
"""
Get the current cache size setting.
Returns:
Current cache size (None if unlimited)
"""
def clear_cache() -> None:
"""
Clear all internal caches.
This can be useful for memory management or when type definitions change.
"""Comprehensive exception hierarchy for different error conditions during conversion.
class DaciteError(Exception):
"""Base exception class for all dacite-related errors."""
class DaciteFieldError(DaciteError):
"""
Base class for field-specific errors.
Attributes:
field_path: Path to the problematic field (e.g., 'user.address.street')
"""
field_path: Optional[str]
def update_path(self, parent_field_path: str) -> None:
"""Update the field path with parent context."""
class WrongTypeError(DaciteFieldError):
"""
Raised when a value has the wrong type for a field.
Attributes:
field_type: Expected field type
value: Actual value that caused the error
"""
field_type: Type
value: Any
class MissingValueError(DaciteFieldError):
"""Raised when a required field is missing from the input data."""
class UnionMatchError(WrongTypeError):
"""Raised when a value cannot match any type in a union."""
class StrictUnionMatchError(DaciteFieldError):
"""
Raised when multiple union types match in strict mode.
Attributes:
union_matches: Dictionary of matching types and their converted values
"""
union_matches: Dict[Type, Any]
class ForwardReferenceError(DaciteError):
"""
Raised when forward references cannot be resolved.
Attributes:
message: Error message describing the resolution failure
"""
message: str
class UnexpectedDataError(DaciteError):
"""
Raised when strict mode encounters unexpected fields in the input data.
Attributes:
keys: Set of unexpected field names
"""
keys: Set[str]Automatically converts nested dictionaries to nested dataclasses:
@dataclass
class Address:
street: str
city: str
@dataclass
class Person:
name: str
address: Address
data = {
"name": "John",
"address": {
"street": "123 Main St",
"city": "Anytown"
}
}
person = from_dict(Person, data)Supports complex type annotations including unions and optional fields:
from typing import Union, Optional
@dataclass
class FlexibleData:
value: Union[str, int, float]
optional_field: Optional[str] = None
data = {"value": 42} # optional_field will be None
result = from_dict(FlexibleData, data)Handles lists, tuples, sets, dictionaries, and generic types:
from typing import List, Dict
@dataclass
class Container:
items: List[str]
mapping: Dict[str, int]
data = {
"items": ["a", "b", "c"],
"mapping": {"x": 1, "y": 2}
}
container = from_dict(Container, data)Transform values during conversion using type hooks:
from datetime import datetime
def parse_datetime(value):
return datetime.fromisoformat(value)
@dataclass
class Event:
name: str
timestamp: datetime
config = Config(type_hooks={datetime: parse_datetime})
data = {"name": "Meeting", "timestamp": "2023-01-01T10:00:00"}
event = from_dict(Event, data, config)from typing import Protocol, Any, TypeVar, Type, Optional, Dict, Callable, List, Set
T = TypeVar("T")
class Data(Protocol):
"""
Protocol defining the interface for input data objects.
Any dictionary-like object that supports these methods can be used.
"""
def keys(self) -> Any: ...
def __getitem__(self, *args, **kwargs) -> Any: ...
def __contains__(self, *args, **kwargs) -> bool: ...