Source code for trinity.common.dataclass_utils
from dataclasses import fields, is_dataclass
from typing import Any, Union, get_args, get_origin
from omegaconf import OmegaConf
[docs]
def build_dataclass_from_mapping(dataclass_type: type[Any], data: Any) -> Any:
"""
Convert a mapping (dict, OmegaConf, etc.) to a dataclass instance, recursively.
"""
if data is None:
return None
if is_dataclass(data):
return data
mapping = _normalize_mapping(data)
kwargs = {}
for dataclass_field in fields(dataclass_type):
if dataclass_field.name in mapping:
kwargs[dataclass_field.name] = _convert_dataclass_field(
dataclass_field.type, mapping[dataclass_field.name]
)
return dataclass_type(**kwargs)
def _normalize_mapping(raw_data: Any) -> dict[str, Any]:
if isinstance(raw_data, dict):
return raw_data
return OmegaConf.to_container(OmegaConf.create(raw_data), resolve=True) # type: ignore[return-value]
def _strip_optional(value_type: Any) -> Any:
origin = get_origin(value_type)
if origin is not Union:
return value_type
non_none_types = [arg for arg in get_args(value_type) if arg is not type(None)]
return non_none_types[0] if len(non_none_types) == 1 else value_type
def _convert_dataclass_field(expected_type: Any, value: Any) -> Any:
if value is None:
return None
expected_type = _strip_optional(expected_type)
origin = get_origin(expected_type)
if isinstance(expected_type, type) and is_dataclass(expected_type):
if is_dataclass(value) or not isinstance(value, dict):
return value
return build_dataclass_from_mapping(expected_type, value)
if origin in (list, tuple, set):
item_type = get_args(expected_type)[0] if get_args(expected_type) else Any
converted_items = [_convert_dataclass_field(item_type, item) for item in value]
if origin is tuple:
return tuple(converted_items)
if origin is set:
return set(converted_items)
return converted_items
if origin is dict:
args = get_args(expected_type)
value_type = args[1] if len(args) > 1 else Any
return {k: _convert_dataclass_field(value_type, v) for k, v in value.items()}
return value