py.lib

Yohn Y. 2022-08-20 Parent:dataclass_utils.py@ae0107755941 Child:366c9fe26d76

38:4f4cc2fc9805 Go to Latest

py.lib/type_utils/dataclass_utils.py

. Полный рефакторинг кода модулей dataclass_utils.py и config_parse_helper.py. Теперь логика предсказуема. + функция dataobj_extract не просто бездумно загоняет данные в класс данных, но имеет функционал проверки данных с возбуждением исключения при разнице (по умолчанию) и принудительного приведения типов.

History
awgur@31 1 # coding: utf-8
awgur@38 2 """\
awgur@38 3 Наполнение заданного класса данных из предоставленного словаря или объекта.
awgur@38 4
awgur@38 5 НЕ РАБОТАЕТ БЕЗ ОСТАЛЬНОГО МОДУЛЯ type_utils
awgur@38 6 """
awgur@31 7
awgur@35 8 from dataclasses import fields, is_dataclass, asdict
awgur@35 9 from typing import Union, Dict, Any, Iterable
awgur@38 10 from .type_descriptor import get_type_describer
awgur@31 11
awgur@31 12
awgur@31 13 def _dict_has(obj: Dict[str, Any], key: str) -> bool:
awgur@31 14 return key in obj
awgur@31 15
awgur@31 16
awgur@31 17 def _dict_get(obj: Dict[str, Any], key: str) -> Any:
awgur@31 18 return obj[key]
awgur@31 19
awgur@31 20
awgur@31 21 def _obj_has(obj: object, key: str) -> bool:
awgur@31 22 return hasattr(obj, key)
awgur@31 23
awgur@31 24
awgur@31 25 def _obj_get(obj: object, key: str) -> Any:
awgur@31 26 return getattr(obj, key)
awgur@31 27
awgur@31 28
awgur@38 29 def dataobj_extract(obj: Union[object, Dict[str, Any]], dataclass_type: type, type_convert: bool = False) -> object:
awgur@31 30 """\
awgur@31 31 Извлекает объект данных из предоставленного объекта, путём получения из него
awgur@31 32 указанных в классе данных аттрибутов и поиска их в данном объекте.
awgur@38 33 :param obj: Объект, из которого берутся данные для класса данных
awgur@38 34 :param dataclass_type: Класс данных, наполняемый из ``obj``
awgur@38 35 :param type_convert: Признак конвертирования данных. Если задан, выполняет попытку сконвертировать имеющееся
awgur@38 36 в параметре значение в тип, указанных в классе данных. Чудес не бывает, и процесс может
awgur@38 37 ошибиться особенно с Union
awgur@31 38 """
awgur@31 39
awgur@31 40 params = {}
awgur@31 41
awgur@31 42 if isinstance(obj, dict):
awgur@31 43 _has = _dict_has
awgur@31 44 _get = _dict_get
awgur@31 45
awgur@31 46 else:
awgur@31 47 _has = _obj_has
awgur@31 48 _get = _obj_get
awgur@31 49
awgur@31 50 if not is_dataclass(dataclass_type):
awgur@31 51 raise ValueError(f'Не относится к классам данных: {dataclass_type.__name__}')
awgur@31 52
awgur@31 53 for fld in fields(dataclass_type):
awgur@31 54 if _has(obj, fld.name):
awgur@31 55 val = _get(obj, fld.name)
awgur@36 56 type_desc = get_type_describer(fld.type)
awgur@37 57 if val is not None:
awgur@38 58 if not type_desc.check(val):
awgur@38 59 if not type_convert:
awgur@38 60 raise ValueError(f'Аттрибут {fld.name} не может быть получен из значения "{val}"'
awgur@38 61 f' с типом {type(val).__name__} поскольку не может быть преобразован в'
awgur@38 62 f' тип {type_desc}, заданный в классе данных: {type_desc.event_description}')
awgur@31 63
awgur@38 64 try:
awgur@38 65 val = type_desc(val)
awgur@38 66
awgur@38 67 except (ValueError, TypeError) as e:
awgur@38 68 raise ValueError(f'Аттрибут {fld.name} не может быть получен из значения "{val}"'
awgur@38 69 f' с типом {type(val).__name__} поскольку не может быть преобразован в'
awgur@38 70 f' тип {type_desc}, заданный в классе данных: {e}')
awgur@38 71
awgur@38 72 elif not (type_desc.is_nullable or type_convert):
awgur@38 73 raise ValueError(f'Передан "None" в поле "{fld.name}", хотя он не ожидался')
awgur@31 74
awgur@31 75 params[fld.name] = val
awgur@31 76
awgur@31 77 try:
awgur@31 78 res = dataclass_type(**params)
awgur@31 79
awgur@31 80 except (ValueError, TypeError) as e:
awgur@31 81 _params = ', '.join(map(lambda x: f'{x[0]}="{x[1]}"', params.items()))
awgur@31 82 raise ValueError(f'Не удалось получить объект'
awgur@31 83 f' класс {dataclass_type.__name__}'
awgur@31 84 f' из параметров: {_params}'
awgur@31 85 f' ошибка: {e}')
awgur@31 86
awgur@31 87 return res
awgur@35 88
awgur@35 89