py.lib
43:6f8bea109183
Go to Latest
py.lib/type_utils/type_descriptor.py
. Наведение порядка в коде логирования
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 Реализация адаптера над простыми типами
74 def __init__(self, type_class, is_nullable=False):
75 super().__init__(type_class.__name__, type_class, is_nullable)
76 if type_class in (int, float, bool, complex):
77 self.union_sort_order = 0
79 elif type_class == str:
80 self.union_sort_order = 2
83 self.union_sort_order = 1
85 def __call__(self, val):
87 return self.raise_nullable()
92 if self.union_sort_order == 1:
93 if isinstance(val, (list, tuple)):
94 return self.t_class(*val)
96 elif isinstance(val, dict):
97 return self.t_class(**val)
99 return self.t_class(val)
102 class IterableTypeDescriptor(BaseTypeDescriptor):
104 Реализация адаптера над последовательностями
106 def __init__(self, iterator_class, value_class, is_nullable=False):
108 name=f'{iterator_class.__name__}[{value_class.__name__}]',
109 type_class=value_class,
110 is_nullable=is_nullable
113 self.iterator_class = iterator_class
115 def check(self, vals):
118 if not super().check(val):
119 return self.check_fail(f'Элемент {idx}: {self.event_description}')
126 def __call__(self, vals):
128 return self.raise_nullable()
133 self.raise_nullable(f'Элемент "{len(res)}"')
136 res.append(self.t_class(val))
138 except (TypeError, ValueError) as e:
139 raise ValueError(f'Не удалось преобразовать элемент "{len(res)}" со значением "{val}" '
140 f'в результирующий тип: {e}')
142 return self.iterator_class(res)
145 class TupleTypeDescriptor(BaseTypeDescriptor):
147 Адаптер над кортежами
149 def __init__(self, type_classes, is_nullable=False):
150 if not isinstance(type_classes, (list, tuple)) or not type_classes:
151 raise TypeError(f'В конструктор прокси для обработки кортежей не передано типов кортежа')
153 names = ', '.join([i.__name__ for i in type_classes])
155 super().__init__(f'Tuple[{names}]', None, is_nullable)
157 self.type_classes = tuple(map(
158 lambda x: x if isinstance(x, TypeDescriptorInterface) else ScalarTypeDescriptor(x),
162 def check(self, vals):
163 if not isinstance(vals, tuple):
164 return self.check_fail(f'Переданная переменная не является кортежем, а относится к типу: '
165 f'{type(vals).__name__}')
167 if len(vals) != len(self.type_classes):
168 return self.check_fail(f'Не достаточно элементов в кортеже: '
169 f'имеется={len(vals)} нужно={len(self.type_classes)}')
171 for i in range(len(self.type_classes)):
172 if not self.type_classes[i].check(vals[i]):
173 return self.check_fail(f'Элемент {i}, значение "{vals[i]}": '
174 f'{self.type_classes[i].event_description}')
176 def __call__(self, vals):
178 return self.raise_nullable()
180 if not isinstance(vals, tuple):
181 raise ValueError(f'Переданная переменная не является кортежем, а относится к типу: '
182 f'{type(vals).__name__}')
184 if len(vals) != len(self.type_classes):
185 raise ValueError(f'Не достаточно элементов в кортеже: '
186 f'имеется={len(vals)} нужно={len(self.type_classes)}')
189 for i in range(len(self.type_classes)):
191 res.append(self.type_classes[i](vals[i]))
193 except (ValueError, TypeError) as e:
194 raise ValueError(f'Не удалось привести к результирующему виду '
195 f'элемент {i} со значением "{vals[i]}": {e}')
200 class UnionTypeDescriptor(BaseTypeDescriptor):
202 Адаптер для объединения типов
204 def __init__(self, type_classes, is_nullable=False):
205 if not isinstance(type_classes, (list, tuple)) or not type_classes:
206 raise TypeError(f'В конструктор прокси для обработки объединений типов не передано типов')
208 names = ', '.join([i.__name__ for i in type_classes])
210 super().__init__(f'Union[{names}]', None, is_nullable)
212 self.type_classes = tuple(sorted(map(
213 lambda x: x if isinstance(x, BaseTypeDescriptor) else ScalarTypeDescriptor(x),
215 ), key=lambda x: x.union_sort_order))
217 def check(self, val):
218 for t in self.type_classes:
222 self.event_description = f'Значение "{val}" типа "{type(val).__name__}" не соответствует ' \
223 f'ни одному типу из моего набора'
226 def __call__(self, val):
228 return self.raise_nullable()
235 for t in self.type_classes:
239 except (TypeError, ValueError) as e:
240 errs.append(f'{repr(t)}: {e}')
242 raise ValueError(f'Не удалось преобразовать значение "{val}" типа "{type(val).__name__}" '
243 f'ни к одному из имеющихся типов: ' + '\n'.join(errs))
246 class DictTypeDescriptor(BaseTypeDescriptor):
250 def __init__(self, key_class, value_class, is_nullable=False):
251 super().__init__(f'Dict[{key_class.__name__}, {value_class.__name__}]', None, is_nullable)
253 if isinstance(key_class, BaseTypeDescriptor):
254 self.key_class = key_class
257 self.key_class = ScalarTypeDescriptor(key_class)
259 if isinstance(value_class, BaseTypeDescriptor):
260 self.value_class = value_class
263 self.value_class = ScalarTypeDescriptor(value_class)
265 def check(self, val):
269 except (TypeError, ValueError) as e:
270 self.event_description = f'Не удалось преобразовать переданное значение в словарь: {e}'
273 for k, v in d.items():
274 if not self.key_class.check(k):
275 return self.check_fail(f'В паре ["{k}": "{v}"] ключ не соответствует ожидаемому типу: '
276 f'{self.key_class.event_description}')
278 if not self.value_class.check(v):
279 return self.check_fail(f'В паре ["{k}": "{v}"] значение не соответствует ожидаемому типу: '
280 f'{self.value_class.event_description}')
284 def __call__(self, val):
288 except (TypeError, ValueError) as e:
289 raise ValueError(f'Не удалось преобразовать переданное значение в словарь: {e}')
292 for k, v in d.items():
293 p.append((self.key_class(k), self.value_class(v)))
298 def get_type_describer(t) -> BaseTypeDescriptor:
299 if type(t).__name__ == '_GenericAlias':
303 except AttributeError:
304 raise TypeError(f'Неизвестный тип хранения внутренних типов для представителя сложного '
305 f'типа "_GenericAlias": {t}')
307 if t.__name__ == 'List':
312 raise ValueError(f'Тип {t} не содержит в себе типа своих элементов')
314 return IterableTypeDescriptor(
316 value_class=get_type_describer(_t)
319 elif t.__name__ == 'Tuple':
321 raise ValueError(f'Тип {t} не содержит в себе пояснений по составу хранящихся в нём типов')
326 return IterableTypeDescriptor(
327 iterator_class=tuple,
328 value_class=get_type_describer(_t)
332 return TupleTypeDescriptor(
336 elif t.__name__ == 'Dict':
338 raise ValueError(f'Неожиданное количество значений типа в составном типе Dict: '
339 f'{len(_args)} values=({_args})')
341 return DictTypeDescriptor(
342 key_class=get_type_describer(_args[0]),
343 value_class=get_type_describer(_args[1])
347 raise ValueError(f'Неизвестный представитель типа "_GenericAlias": {t.__name__}')
349 elif type(t).__name__ == '_UnionGenericAlias':
350 if t.__name__ not in ('Union', 'Optional'):
351 raise TypeError(f'Неизвестный подтип _UnionGenericAlias: {t.__name__}')
354 if t.__name__ == 'Optional':
360 except AttributeError:
361 raise TypeError(f'Неизвестный тип хранения внутренних типов для представителя сложного '
362 f'типа "_UnionGenericAlias": {t}')
365 raise ValueError('Не указан ни один тип в конструкции Union')
367 type_classes = tuple(map(lambda x: get_type_describer(x), [ i for i in _args if i is not None]))
369 return UnionTypeDescriptor(
370 type_classes=type_classes,
375 return ScalarTypeDescriptor(type_class=t)