py.lib
38:4f4cc2fc9805
Go to Latest
py.lib/type_utils/type_descriptor.py
. Полный рефакторинг кода модулей dataclass_utils.py и config_parse_helper.py. Теперь логика предсказуема.
+ функция dataobj_extract не просто бездумно загоняет данные в класс данных, но имеет функционал проверки данных с возбуждением исключения при разнице (по умолчанию) и принудительного приведения типов.
3 class TypeDescriptorInterface:
5 Определяем общий интерфейс для описателей типов
7 def __init__(self, name, type_class, is_nullable=False):
9 self.t_class = type_class
10 self.is_nullable = is_nullable
11 self.event_description = ''
12 self.union_sort_order = 3
14 def raise_nullable(self, msg=''):
15 msg = msg if not msg else f': {msg}'
17 if not self.is_nullable:
18 raise ValueError(f'Передача значения "None" переменной, которая этого не ожидает{msg}')
20 def __call__(self, val):
22 Реализация приведения типа
24 raise NotImplemented()
27 if self.t_class is None:
31 return f'{self.__name__}({self.t_class.__name__})'
35 Реализация проверки соответствия типу
37 raise NotImplemented()
40 class BaseTypeDescriptor(TypeDescriptorInterface):
42 Базовый класс, объявляющий общий интерфейс классов адаптеров проверки и преобразования типов
45 event_description = ''
48 event_description = 'Передано значение "None"'
49 res = self.is_nullable
51 elif isinstance(self.t_class, TypeDescriptorInterface):
52 res = self.t_class.check(val)
53 event_description = self.t_class.event_description
56 event_description = f'Требуется тип "{self.t_class.__name__}" а получаем "{type(val).__name__}"'
57 res = isinstance(val, self.t_class)
60 self.event_description = event_description
64 def check_fail(self, msg):
65 self.event_description = msg
69 class ScalarTypeDescriptor(BaseTypeDescriptor):
71 Реализация адаптера над простыми типами
73 def __init__(self, type_class, is_nullable=False):
74 super().__init__(type_class.__name__, type_class, is_nullable)
75 if type_class in (int, float, bool):
76 self.union_sort_order = 0
78 elif type_class == str:
79 self.union_sort_order = 2
82 self.union_sort_order = 1
84 def __call__(self, val):
86 return self.raise_nullable()
91 return self.t_class(val)
94 class IterableTypeDescriptor(BaseTypeDescriptor):
96 Реализация адаптера над последовательностями
98 def __init__(self, iterator_class, value_class, is_nullable=False):
100 name=f'{iterator_class.__name__}[{value_class.__name__}]',
101 type_class=value_class,
102 is_nullable=is_nullable
105 self.iterator_class = iterator_class
107 def check(self, vals):
110 if not super().check(val):
111 return self.check_fail(f'Элемент {idx}: {self.event_description}')
118 def __call__(self, vals):
120 return self.raise_nullable()
125 self.raise_nullable(f'Элемент "{len(res)}"')
128 res.append(self.t_class(val))
130 except (TypeError, ValueError) as e:
131 raise ValueError(f'Не удалось преобразовать элемент "{len(res)}" со значением "{val}" '
132 f'в результирующий тип: {e}')
134 return self.iterator_class(res)
137 class TupleTypeDescriptor(BaseTypeDescriptor):
139 Адаптер над кортежами
141 def __init__(self, type_classes, is_nullable=False):
142 if not isinstance(type_classes, (list, tuple)) or not type_classes:
143 raise TypeError(f'В конструктор прокси для обработки кортежей не передано типов кортежа')
145 names = ', '.join([i.__name__ for i in type_classes])
147 super().__init__(f'Tuple[{names}]', None, is_nullable)
149 self.type_classes = tuple(map(
150 lambda x: x if isinstance(x, TypeDescriptorInterface) else ScalarTypeDescriptor(x),
154 def check(self, vals):
155 if not isinstance(vals, tuple):
156 return self.check_fail(f'Переданная переменная не является кортежем, а относится к типу: '
157 f'{type(vals).__name__}')
159 if len(vals) != len(self.type_classes):
160 return self.check_fail(f'Не достаточно элементов в кортеже: '
161 f'имеется={len(vals)} нужно={len(self.type_classes)}')
163 for i in range(len(self.type_classes)):
164 if not self.type_classes[i].check(vals[i]):
165 return self.check_fail(f'Элемент {i}, значение "{vals[i]}": '
166 f'{self.type_classes[i].event_description}')
168 def __call__(self, vals):
170 return self.raise_nullable()
172 if not isinstance(vals, tuple):
173 raise ValueError(f'Переданная переменная не является кортежем, а относится к типу: '
174 f'{type(vals).__name__}')
176 if len(vals) != len(self.type_classes):
177 raise ValueError(f'Не достаточно элементов в кортеже: '
178 f'имеется={len(vals)} нужно={len(self.type_classes)}')
181 for i in range(len(self.type_classes)):
183 res.append(self.type_classes[i](vals[i]))
185 except (ValueError, TypeError) as e:
186 raise ValueError(f'Не удалось привести к результирующему виду '
187 f'элемент {i} со значением "{vals[i]}": {e}')
192 class UnionTypeDescriptor(BaseTypeDescriptor):
194 Адаптер для объединения типов
196 def __init__(self, type_classes, is_nullable=False):
197 if not isinstance(type_classes, (list, tuple)) or not type_classes:
198 raise TypeError(f'В конструктор прокси для обработки объединений типов не передано типов')
200 names = ', '.join([i.__name__ for i in type_classes])
202 super().__init__(f'Union[{names}]', None, is_nullable)
204 self.type_classes = tuple(sorted(map(
205 lambda x: x if isinstance(x, BaseTypeDescriptor) else ScalarTypeDescriptor(x),
207 ), key=lambda x: x.union_sort_order))
209 def check(self, val):
210 for t in self.type_classes:
214 self.event_description = f'Значение "{val}" типа "{type(val).__name__}" не соответствует ' \
215 f'ни одному типу из моего набора'
218 def __call__(self, val):
220 return self.raise_nullable()
227 for t in self.type_classes:
231 except (TypeError, ValueError) as e:
232 errs.append(f'{repr(t)}: {e}')
234 raise ValueError(f'Не удалось преобразовать значение "{val}" типа "{type(val).__name__}" '
235 f'ни к одному из имеющихся типов: ' + '\n'.join(errs))
238 class DictTypeDescriptor(BaseTypeDescriptor):
242 def __init__(self, key_class, value_class, is_nullable=False):
243 super().__init__(f'Dict[{key_class.__name__}, {value_class.__name__}]', None, is_nullable)
245 if isinstance(key_class, BaseTypeDescriptor):
246 self.key_class = key_class
249 self.key_class = ScalarTypeDescriptor(key_class)
251 if isinstance(value_class, BaseTypeDescriptor):
252 self.value_class = value_class
255 self.value_class = ScalarTypeDescriptor(value_class)
257 def check(self, val):
261 except (TypeError, ValueError) as e:
262 self.event_description = f'Не удалось преобразовать переданное значение в словарь: {e}'
265 for k, v in d.items():
266 if not self.key_class.check(k):
267 return self.check_fail(f'В паре ["{k}": "{v}"] ключ не соответствует ожидаемому типу: '
268 f'{self.key_class.event_description}')
270 if not self.value_class.check(v):
271 return self.check_fail(f'В паре ["{k}": "{v}"] значение не соответствует ожидаемому типу: '
272 f'{self.value_class.event_description}')
276 def __call__(self, val):
280 except (TypeError, ValueError) as e:
281 raise ValueError(f'Не удалось преобразовать переданное значение в словарь: {e}')
284 for k, v in d.items():
285 p.append((self.key_class(k), self.value_class(v)))
290 def get_type_describer(t) -> BaseTypeDescriptor:
291 if type(t).__name__ == '_GenericAlias':
295 except AttributeError:
296 raise TypeError(f'Неизвестный тип хранения внутренних типов для представителя сложного '
297 f'типа "_GenericAlias": {t}')
299 if t.__name__ == 'List':
304 raise ValueError(f'Тип {t} не содержит в себе типа своих элементов')
306 return IterableTypeDescriptor(
308 value_class=get_type_describer(_t)
311 elif t.__name__ == 'Tuple':
313 raise ValueError(f'Тип {t} не содержит в себе пояснений по составу хранящихся в нём типов')
318 return IterableTypeDescriptor(
319 iterator_class=tuple,
320 value_class=get_type_describer(_t)
324 return TupleTypeDescriptor(
328 elif t.__name__ == 'Dict':
330 raise ValueError(f'Неожиданное количество значений типа в составном типе Dict: '
331 f'{len(_args)} values=({_args})')
333 return DictTypeDescriptor(
334 key_class=get_type_describer(_args[0]),
335 value_class=get_type_describer(_args[1])
339 raise ValueError(f'Неизвестный представитель типа "_GenericAlias": {t.__name__}')
341 elif type(t).__name__ == '_UnionGenericAlias':
342 if t.__name__ not in ('Union', 'Optional'):
343 raise TypeError(f'Неизвестный подтип _UnionGenericAlias: {t.__name__}')
346 if t.__name__ == 'Optional':
352 except AttributeError:
353 raise TypeError(f'Неизвестный тип хранения внутренних типов для представителя сложного '
354 f'типа "_UnionGenericAlias": {t}')
357 raise ValueError('Не указан ни один тип в конструкции Union')
359 type_classes = tuple(map(lambda x: get_type_describer(x), [ i for i in _args if i is not None]))
361 return UnionTypeDescriptor(
362 type_classes=type_classes,
367 return ScalarTypeDescriptor(type_class=t)