py.lib

Yohn Y. 2022-08-14 Parent:57f63bf31fd8 Child:ab4cf9f4f10a

34:84b54a8a6d4c Go to Latest

py.lib/dataclass_utils.py

+ Возможность обработки параметров конфигурации перед добавлением в класс конфигурации . Переформатирование части кода по PEP

History
awgur@31 1 # coding: utf-8
awgur@31 2
awgur@31 3 from dataclasses import fields, dataclass, is_dataclass
awgur@31 4 from typing import Union, Dict, Any
awgur@31 5
awgur@31 6
awgur@31 7 def _dict_has(obj: Dict[str, Any], key: str) -> bool:
awgur@31 8 return key in obj
awgur@31 9
awgur@31 10
awgur@31 11 def _dict_get(obj: Dict[str, Any], key: str) -> Any:
awgur@31 12 return obj[key]
awgur@31 13
awgur@31 14
awgur@31 15 def _obj_has(obj: object, key: str) -> bool:
awgur@31 16 return hasattr(obj, key)
awgur@31 17
awgur@31 18
awgur@31 19 def _obj_get(obj: object, key: str) -> Any:
awgur@31 20 return getattr(obj, key)
awgur@31 21
awgur@31 22
awgur@33 23 def difficult_type_recognizer(type_obj, value):
awgur@33 24 """\
awgur@33 25 Магия борющаяся с костылями type hinting
awgur@33 26 """
awgur@33 27 if type(type_obj).__name__ == '_GenericAlias':
awgur@33 28 if (
awgur@33 29 hasattr(type_obj, '__args__')
awgur@33 30 and type(type_obj.__args__) == tuple
awgur@33 31 and type_obj.__args__
awgur@33 32 ):
awgur@33 33 if type_obj.__name__ == 'List':
awgur@33 34 return list(map(type_obj.__args__[0], value))
awgur@33 35
awgur@33 36 elif type_obj.__name__ == 'Tuple':
awgur@33 37 return tuple(map(type_obj.__args__[0], value))
awgur@33 38
awgur@33 39 else:
awgur@33 40 raise ValueError('Неизвестный тип')
awgur@33 41
awgur@33 42 else:
awgur@33 43 ValueError('Неизвестный тип')
awgur@33 44
awgur@33 45 elif type(type_obj).__name__ == '_UnionGenericAlias':
awgur@33 46 if type_obj.__name__ not in ('Union', 'Optional'):
awgur@33 47 raise TypeError(f'Неизвестный подтип _UnionGenericAlias: {type_obj.__name__}')
awgur@33 48
awgur@33 49 if not (
awgur@33 50 hasattr(type_obj, '__args__')
awgur@33 51 and type(type_obj.__args__) == tuple
awgur@33 52 and type_obj.__args__
awgur@33 53 ):
awgur@33 54 raise TypeError(f'Не ясно как работать с типом не вижу аргументов: {type_obj.__name__}')
awgur@33 55
awgur@33 56 for _t in type_obj.__args__:
awgur@33 57 if _t.__name__ == 'NoneType':
awgur@33 58 continue
awgur@33 59
awgur@33 60 try:
awgur@33 61 return _t(value)
awgur@33 62
awgur@33 63 except (TypeError, ValueError):
awgur@33 64 continue
awgur@33 65
awgur@33 66 raise ValueError('Не удалось привести значение к типу')
awgur@33 67
awgur@33 68
awgur@34 69 def dataobj_extract(obj: Union[object, Dict[str, Any]], dataclass_type: type) -> object:
awgur@31 70 """\
awgur@31 71 Извлекает объект данных из предоставленного объекта, путём получения из него
awgur@31 72 указанных в классе данных аттрибутов и поиска их в данном объекте.
awgur@31 73 """
awgur@31 74
awgur@31 75 params = {}
awgur@31 76
awgur@31 77 if isinstance(obj, dict):
awgur@31 78 _has = _dict_has
awgur@31 79 _get = _dict_get
awgur@31 80
awgur@31 81 else:
awgur@31 82 _has = _obj_has
awgur@31 83 _get = _obj_get
awgur@31 84
awgur@31 85 if not is_dataclass(dataclass_type):
awgur@31 86 raise ValueError(f'Не относится к классам данных: {dataclass_type.__name__}')
awgur@31 87
awgur@31 88 for fld in fields(dataclass_type):
awgur@31 89 if _has(obj, fld.name):
awgur@31 90 val = _get(obj, fld.name)
awgur@31 91 if val is not None and not isinstance(val, fld.type):
awgur@31 92 try:
awgur@31 93 val = fld.type(val)
awgur@31 94
awgur@31 95 except (ValueError, TypeError) as e:
awgur@33 96 try:
awgur@33 97 val = difficult_type_recognizer(fld.type, val)
awgur@33 98
awgur@33 99 except (ValueError, TypeError):
awgur@33 100 raise ValueError(f'Аттрибут {fld.name} не может быть получен из значения "{val}"'
awgur@33 101 f' с типом {type(val).__name__} поскольку не может быть преобразован в'
awgur@33 102 f' тип {fld.type.__name__}, заданный в классе данных: {e}')
awgur@31 103
awgur@31 104 params[fld.name] = val
awgur@31 105
awgur@31 106 try:
awgur@31 107 res = dataclass_type(**params)
awgur@31 108
awgur@31 109 except (ValueError, TypeError) as e:
awgur@31 110 _params = ', '.join(map(lambda x: f'{x[0]}="{x[1]}"', params.items()))
awgur@31 111 raise ValueError(f'Не удалось получить объект'
awgur@31 112 f' класс {dataclass_type.__name__}'
awgur@31 113 f' из параметров: {_params}'
awgur@31 114 f' ошибка: {e}')
awgur@31 115
awgur@31 116 return res