Skip to content

Instantly share code, notes, and snippets.

@spzm
Forked from quozd/serializer.py
Created December 16, 2019 22:43
Show Gist options
  • Save spzm/edc5b53aa79e2080cdfdd040043dad61 to your computer and use it in GitHub Desktop.
Save spzm/edc5b53aa79e2080cdfdd040043dad61 to your computer and use it in GitHub Desktop.
Typehints-based deserializer
def _create_config_hierarchy(config_cls: Type[TConfig], config_data: Mapping) -> TConfig:
sig = signature(config_cls)
cls_data_attributes = getmembers(config_cls, lambda a: isinstance(a, GenericDescriptor))
if len(cls_data_attributes) != len(sig.parameters):
raise TypeError(
'Type {type_name} has invalid __init__.\n'
'Init parameters count should equal to data descriptors count.\n'
'Init parameters: [{init_params}], data descriptors: [{data_descriptors}]'.format(
type_name=config_cls,
init_params=', '.join(sig.parameters.keys()),
data_descriptors=', '.join(attr_name for attr_name, _ in cls_data_attributes),
)
)
config_cls_kwargs: MutableMapping = OrderedDict()
for kwarg_name, kwarg in sig.parameters.items():
if kwarg_name == 'self':
continue
kwarg_type = kwarg.annotation
if kwarg_type in (bool, float, int, str, list, tuple) or issubclass(kwarg_type, Enum):
config_cls_kwargs[kwarg_name] = kwarg_type(config_data[kwarg_name])
elif kwarg_type in (datetime, date):
if isinstance(config_data[kwarg_name], kwarg_type):
# Some parsers will parse dates to objects, not strings
config_cls_kwargs[kwarg_name] = config_data[kwarg_name]
else:
if (
(isinstance(config_data[kwarg_name], date) and kwarg_type is datetime) or
(isinstance(config_data[kwarg_name], datetime) and kwarg_type is date)
):
raise TypeError(
'Invalid kwarg type while constructing {target_type}. '
'Expected {expected_type}, got {got_type}'.format(
target_type=config_cls,
expected_type=kwarg_type,
got_type=type(config_data[kwarg_name])
))
else:
raise NotImplementedError('Dates parsing is not supported')
elif isinstance(kwarg_type, GenericMeta) and isinstance(config_data[kwarg_name], (list, tuple)):
# Iterables and Mappings have annotation of type GenericMeta
# In this case we use actual data type to construct config kwarg
# Used for kwargs of type List[T], Mapping[KK, KV], etc
actual_data_type = type(config_data[kwarg_name])
config_cls_kwargs[kwarg_name] = actual_data_type(config_data[kwarg_name])
else:
config_cls_kwargs[kwarg_name] = _create_config_hierarchy(kwarg_type, config_data[kwarg_name])
return config_cls(**config_cls_kwargs) # type: ignore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment