py.lib
2022-08-27
Parent:b8d559c989d6
py.lib/type_utils/type_descriptor.py
+ Возможность удаления cookie по полному объекту (полезно, когда выставляются особые параметры на cookie, и их не удаётся удалить по имени)
| awgur@38 | 1 # coding: utf-8 |
| awgur@38 | 2 |
| awgur@38 | 3 class TypeDescriptorInterface: |
| awgur@38 | 4 """\ |
| awgur@38 | 5 Определяем общий интерфейс для описателей типов |
| awgur@38 | 6 """ |
| awgur@38 | 7 def __init__(self, name, type_class, is_nullable=False): |
| awgur@38 | 8 self.__name__ = name |
| awgur@38 | 9 self.t_class = type_class |
| awgur@38 | 10 self.is_nullable = is_nullable |
| awgur@38 | 11 self.event_description = '' |
| awgur@38 | 12 self.union_sort_order = 3 |
| awgur@38 | 13 |
| awgur@38 | 14 def raise_nullable(self, msg=''): |
| awgur@38 | 15 msg = msg if not msg else f': {msg}' |
| awgur@38 | 16 |
| awgur@38 | 17 if not self.is_nullable: |
| awgur@38 | 18 raise ValueError(f'Передача значения "None" переменной, которая этого не ожидает{msg}') |
| awgur@38 | 19 |
| awgur@38 | 20 def __call__(self, val): |
| awgur@38 | 21 """\ |
| awgur@38 | 22 Реализация приведения типа |
| awgur@38 | 23 """ |
| awgur@38 | 24 raise NotImplemented() |
| awgur@38 | 25 |
| awgur@38 | 26 def __repr__(self): |
| awgur@38 | 27 if self.t_class is None: |
| awgur@38 | 28 return self.__name__ |
| awgur@38 | 29 |
| awgur@38 | 30 else: |
| awgur@38 | 31 return f'{self.__name__}({self.t_class.__name__})' |
| awgur@38 | 32 |
| awgur@38 | 33 def check(self, val): |
| awgur@38 | 34 """\ |
| awgur@38 | 35 Реализация проверки соответствия типу |
| awgur@38 | 36 """ |
| awgur@38 | 37 raise NotImplemented() |
| awgur@38 | 38 |
| awgur@38 | 39 |
| awgur@38 | 40 class BaseTypeDescriptor(TypeDescriptorInterface): |
| awgur@38 | 41 """\ |
| awgur@38 | 42 Базовый класс, объявляющий общий интерфейс классов адаптеров проверки и преобразования типов |
| awgur@38 | 43 """ |
| awgur@38 | 44 def check(self, val): |
| awgur@38 | 45 event_description = '' |
| awgur@38 | 46 res = False |
| awgur@38 | 47 if val is None: |
| awgur@38 | 48 event_description = 'Передано значение "None"' |
| awgur@38 | 49 res = self.is_nullable |
| awgur@38 | 50 |
| awgur@38 | 51 elif isinstance(self.t_class, TypeDescriptorInterface): |
| awgur@38 | 52 res = self.t_class.check(val) |
| awgur@38 | 53 event_description = self.t_class.event_description |
| awgur@38 | 54 |
| awgur@38 | 55 else: |
| awgur@38 | 56 event_description = f'Требуется тип "{self.t_class.__name__}" а получаем "{type(val).__name__}"' |
| awgur@38 | 57 res = isinstance(val, self.t_class) |
| awgur@38 | 58 |
| awgur@38 | 59 if not res: |
| awgur@38 | 60 self.event_description = event_description |
| awgur@38 | 61 |
| awgur@38 | 62 return res |
| awgur@38 | 63 |
| awgur@38 | 64 def check_fail(self, msg): |
| awgur@38 | 65 self.event_description = msg |
| awgur@38 | 66 return False |
| awgur@38 | 67 |
| awgur@38 | 68 |
| awgur@38 | 69 class ScalarTypeDescriptor(BaseTypeDescriptor): |
| awgur@38 | 70 """\ |
| awgur@38 | 71 Реализация адаптера над простыми типами |
| awgur@38 | 72 """ |
| awgur@39 | 73 |
| awgur@38 | 74 def __init__(self, type_class, is_nullable=False): |
| awgur@38 | 75 super().__init__(type_class.__name__, type_class, is_nullable) |
| awgur@39 | 76 if type_class in (int, float, bool, complex): |
| awgur@38 | 77 self.union_sort_order = 0 |
| awgur@38 | 78 |
| awgur@38 | 79 elif type_class == str: |
| awgur@38 | 80 self.union_sort_order = 2 |
| awgur@38 | 81 |
| awgur@38 | 82 else: |
| awgur@38 | 83 self.union_sort_order = 1 |
| awgur@38 | 84 |
| awgur@38 | 85 def __call__(self, val): |
| awgur@38 | 86 if val is None: |
| awgur@38 | 87 return self.raise_nullable() |
| awgur@38 | 88 |
| awgur@38 | 89 if self.check(val): |
| awgur@38 | 90 return val |
| awgur@38 | 91 |
| awgur@39 | 92 if self.union_sort_order == 1: |
| awgur@39 | 93 if isinstance(val, (list, tuple)): |
| awgur@39 | 94 return self.t_class(*val) |
| awgur@39 | 95 |
| awgur@39 | 96 elif isinstance(val, dict): |
| awgur@39 | 97 return self.t_class(**val) |
| awgur@39 | 98 |
| awgur@38 | 99 return self.t_class(val) |
| awgur@38 | 100 |
| awgur@38 | 101 |
| awgur@38 | 102 class IterableTypeDescriptor(BaseTypeDescriptor): |
| awgur@38 | 103 """\ |
| awgur@38 | 104 Реализация адаптера над последовательностями |
| awgur@38 | 105 """ |
| awgur@38 | 106 def __init__(self, iterator_class, value_class, is_nullable=False): |
| awgur@38 | 107 super().__init__( |
| awgur@38 | 108 name=f'{iterator_class.__name__}[{value_class.__name__}]', |
| awgur@38 | 109 type_class=value_class, |
| awgur@38 | 110 is_nullable=is_nullable |
| awgur@38 | 111 ) |
| awgur@38 | 112 |
| awgur@38 | 113 self.iterator_class = iterator_class |
| awgur@38 | 114 |
| awgur@38 | 115 def check(self, vals): |
| awgur@38 | 116 idx = 0 |
| awgur@38 | 117 for val in vals: |
| awgur@38 | 118 if not super().check(val): |
| awgur@38 | 119 return self.check_fail(f'Элемент {idx}: {self.event_description}') |
| awgur@38 | 120 |
| awgur@38 | 121 else: |
| awgur@38 | 122 idx += 1 |
| awgur@38 | 123 |
| awgur@38 | 124 return True |
| awgur@38 | 125 |
| awgur@38 | 126 def __call__(self, vals): |
| awgur@38 | 127 if vals is None: |
| awgur@38 | 128 return self.raise_nullable() |
| awgur@38 | 129 |
| awgur@38 | 130 res = [] |
| awgur@38 | 131 for val in vals: |
| awgur@38 | 132 if val is None: |
| awgur@38 | 133 self.raise_nullable(f'Элемент "{len(res)}"') |
| awgur@38 | 134 |
| awgur@38 | 135 try: |
| awgur@38 | 136 res.append(self.t_class(val)) |
| awgur@38 | 137 |
| awgur@38 | 138 except (TypeError, ValueError) as e: |
| awgur@38 | 139 raise ValueError(f'Не удалось преобразовать элемент "{len(res)}" со значением "{val}" ' |
| awgur@38 | 140 f'в результирующий тип: {e}') |
| awgur@38 | 141 |
| awgur@38 | 142 return self.iterator_class(res) |
| awgur@38 | 143 |
| awgur@38 | 144 |
| awgur@38 | 145 class TupleTypeDescriptor(BaseTypeDescriptor): |
| awgur@38 | 146 """\ |
| awgur@38 | 147 Адаптер над кортежами |
| awgur@38 | 148 """ |
| awgur@38 | 149 def __init__(self, type_classes, is_nullable=False): |
| awgur@38 | 150 if not isinstance(type_classes, (list, tuple)) or not type_classes: |
| awgur@38 | 151 raise TypeError(f'В конструктор прокси для обработки кортежей не передано типов кортежа') |
| awgur@38 | 152 |
| awgur@38 | 153 names = ', '.join([i.__name__ for i in type_classes]) |
| awgur@38 | 154 |
| awgur@38 | 155 super().__init__(f'Tuple[{names}]', None, is_nullable) |
| awgur@38 | 156 |
| awgur@38 | 157 self.type_classes = tuple(map( |
| awgur@38 | 158 lambda x: x if isinstance(x, TypeDescriptorInterface) else ScalarTypeDescriptor(x), |
| awgur@38 | 159 type_classes |
| awgur@38 | 160 )) |
| awgur@38 | 161 |
| awgur@38 | 162 def check(self, vals): |
| awgur@38 | 163 if not isinstance(vals, tuple): |
| awgur@38 | 164 return self.check_fail(f'Переданная переменная не является кортежем, а относится к типу: ' |
| awgur@38 | 165 f'{type(vals).__name__}') |
| awgur@38 | 166 |
| awgur@38 | 167 if len(vals) != len(self.type_classes): |
| awgur@38 | 168 return self.check_fail(f'Не достаточно элементов в кортеже: ' |
| awgur@38 | 169 f'имеется={len(vals)} нужно={len(self.type_classes)}') |
| awgur@38 | 170 |
| awgur@38 | 171 for i in range(len(self.type_classes)): |
| awgur@38 | 172 if not self.type_classes[i].check(vals[i]): |
| awgur@38 | 173 return self.check_fail(f'Элемент {i}, значение "{vals[i]}": ' |
| awgur@38 | 174 f'{self.type_classes[i].event_description}') |
| awgur@38 | 175 |
| awgur@38 | 176 def __call__(self, vals): |
| awgur@38 | 177 if vals is None: |
| awgur@38 | 178 return self.raise_nullable() |
| awgur@38 | 179 |
| awgur@38 | 180 if not isinstance(vals, tuple): |
| awgur@38 | 181 raise ValueError(f'Переданная переменная не является кортежем, а относится к типу: ' |
| awgur@38 | 182 f'{type(vals).__name__}') |
| awgur@38 | 183 |
| awgur@38 | 184 if len(vals) != len(self.type_classes): |
| awgur@38 | 185 raise ValueError(f'Не достаточно элементов в кортеже: ' |
| awgur@38 | 186 f'имеется={len(vals)} нужно={len(self.type_classes)}') |
| awgur@38 | 187 |
| awgur@38 | 188 res = [] |
| awgur@38 | 189 for i in range(len(self.type_classes)): |
| awgur@38 | 190 try: |
| awgur@38 | 191 res.append(self.type_classes[i](vals[i])) |
| awgur@38 | 192 |
| awgur@38 | 193 except (ValueError, TypeError) as e: |
| awgur@38 | 194 raise ValueError(f'Не удалось привести к результирующему виду ' |
| awgur@38 | 195 f'элемент {i} со значением "{vals[i]}": {e}') |
| awgur@38 | 196 |
| awgur@38 | 197 return tuple(res) |
| awgur@38 | 198 |
| awgur@38 | 199 |
| awgur@38 | 200 class UnionTypeDescriptor(BaseTypeDescriptor): |
| awgur@38 | 201 """\ |
| awgur@38 | 202 Адаптер для объединения типов |
| awgur@38 | 203 """ |
| awgur@38 | 204 def __init__(self, type_classes, is_nullable=False): |
| awgur@38 | 205 if not isinstance(type_classes, (list, tuple)) or not type_classes: |
| awgur@38 | 206 raise TypeError(f'В конструктор прокси для обработки объединений типов не передано типов') |
| awgur@38 | 207 |
| awgur@38 | 208 names = ', '.join([i.__name__ for i in type_classes]) |
| awgur@38 | 209 |
| awgur@38 | 210 super().__init__(f'Union[{names}]', None, is_nullable) |
| awgur@38 | 211 |
| awgur@38 | 212 self.type_classes = tuple(sorted(map( |
| awgur@38 | 213 lambda x: x if isinstance(x, BaseTypeDescriptor) else ScalarTypeDescriptor(x), |
| awgur@38 | 214 type_classes |
| awgur@38 | 215 ), key=lambda x: x.union_sort_order)) |
| awgur@38 | 216 |
| awgur@38 | 217 def check(self, val): |
| awgur@38 | 218 for t in self.type_classes: |
| awgur@38 | 219 if t.check(val): |
| awgur@38 | 220 return True |
| awgur@38 | 221 |
| awgur@38 | 222 self.event_description = f'Значение "{val}" типа "{type(val).__name__}" не соответствует ' \ |
| awgur@38 | 223 f'ни одному типу из моего набора' |
| awgur@38 | 224 return False |
| awgur@38 | 225 |
| awgur@38 | 226 def __call__(self, val): |
| awgur@38 | 227 if val is None: |
| awgur@38 | 228 return self.raise_nullable() |
| awgur@38 | 229 |
| awgur@38 | 230 if self.check(val): |
| awgur@38 | 231 return val |
| awgur@38 | 232 |
| awgur@38 | 233 errs = [] |
| awgur@38 | 234 |
| awgur@38 | 235 for t in self.type_classes: |
| awgur@38 | 236 try: |
| awgur@38 | 237 return t(val) |
| awgur@38 | 238 |
| awgur@38 | 239 except (TypeError, ValueError) as e: |
| awgur@38 | 240 errs.append(f'{repr(t)}: {e}') |
| awgur@38 | 241 |
| awgur@38 | 242 raise ValueError(f'Не удалось преобразовать значение "{val}" типа "{type(val).__name__}" ' |
| awgur@38 | 243 f'ни к одному из имеющихся типов: ' + '\n'.join(errs)) |
| awgur@38 | 244 |
| awgur@38 | 245 |
| awgur@38 | 246 class DictTypeDescriptor(BaseTypeDescriptor): |
| awgur@38 | 247 """\ |
| awgur@38 | 248 Адаптер словарей |
| awgur@38 | 249 """ |
| awgur@38 | 250 def __init__(self, key_class, value_class, is_nullable=False): |
| awgur@38 | 251 super().__init__(f'Dict[{key_class.__name__}, {value_class.__name__}]', None, is_nullable) |
| awgur@38 | 252 |
| awgur@38 | 253 if isinstance(key_class, BaseTypeDescriptor): |
| awgur@38 | 254 self.key_class = key_class |
| awgur@38 | 255 |
| awgur@38 | 256 else: |
| awgur@38 | 257 self.key_class = ScalarTypeDescriptor(key_class) |
| awgur@38 | 258 |
| awgur@38 | 259 if isinstance(value_class, BaseTypeDescriptor): |
| awgur@38 | 260 self.value_class = value_class |
| awgur@38 | 261 |
| awgur@38 | 262 else: |
| awgur@38 | 263 self.value_class = ScalarTypeDescriptor(value_class) |
| awgur@38 | 264 |
| awgur@38 | 265 def check(self, val): |
| awgur@38 | 266 try: |
| awgur@38 | 267 d = dict(val) |
| awgur@38 | 268 |
| awgur@38 | 269 except (TypeError, ValueError) as e: |
| awgur@38 | 270 self.event_description = f'Не удалось преобразовать переданное значение в словарь: {e}' |
| awgur@38 | 271 return False |
| awgur@38 | 272 |
| awgur@38 | 273 for k, v in d.items(): |
| awgur@38 | 274 if not self.key_class.check(k): |
| awgur@38 | 275 return self.check_fail(f'В паре ["{k}": "{v}"] ключ не соответствует ожидаемому типу: ' |
| awgur@38 | 276 f'{self.key_class.event_description}') |
| awgur@38 | 277 |
| awgur@38 | 278 if not self.value_class.check(v): |
| awgur@38 | 279 return self.check_fail(f'В паре ["{k}": "{v}"] значение не соответствует ожидаемому типу: ' |
| awgur@38 | 280 f'{self.value_class.event_description}') |
| awgur@38 | 281 |
| awgur@38 | 282 return True |
| awgur@38 | 283 |
| awgur@38 | 284 def __call__(self, val): |
| awgur@38 | 285 try: |
| awgur@38 | 286 d = dict(val) |
| awgur@38 | 287 |
| awgur@38 | 288 except (TypeError, ValueError) as e: |
| awgur@38 | 289 raise ValueError(f'Не удалось преобразовать переданное значение в словарь: {e}') |
| awgur@38 | 290 |
| awgur@38 | 291 p = [] |
| awgur@38 | 292 for k, v in d.items(): |
| awgur@38 | 293 p.append((self.key_class(k), self.value_class(v))) |
| awgur@38 | 294 |
| awgur@38 | 295 return dict(p) |
| awgur@38 | 296 |
| awgur@38 | 297 |
| awgur@38 | 298 def get_type_describer(t) -> BaseTypeDescriptor: |
| awgur@38 | 299 if type(t).__name__ == '_GenericAlias': |
| awgur@38 | 300 try: |
| awgur@38 | 301 _args = t.__args__ |
| awgur@38 | 302 |
| awgur@38 | 303 except AttributeError: |
| awgur@38 | 304 raise TypeError(f'Неизвестный тип хранения внутренних типов для представителя сложного ' |
| awgur@38 | 305 f'типа "_GenericAlias": {t}') |
| awgur@38 | 306 |
| awgur@38 | 307 if t.__name__ == 'List': |
| awgur@38 | 308 try: |
| awgur@38 | 309 _t = _args[0] |
| awgur@38 | 310 |
| awgur@38 | 311 except IndexError: |
| awgur@38 | 312 raise ValueError(f'Тип {t} не содержит в себе типа своих элементов') |
| awgur@38 | 313 |
| awgur@38 | 314 return IterableTypeDescriptor( |
| awgur@38 | 315 iterator_class=list, |
| awgur@38 | 316 value_class=get_type_describer(_t) |
| awgur@38 | 317 ) |
| awgur@38 | 318 |
| awgur@38 | 319 elif t.__name__ == 'Tuple': |
| awgur@38 | 320 if not _args: |
| awgur@38 | 321 raise ValueError(f'Тип {t} не содержит в себе пояснений по составу хранящихся в нём типов') |
| awgur@38 | 322 |
| awgur@38 | 323 if len(_args) == 1: |
| awgur@38 | 324 _t = _args[0] |
| awgur@38 | 325 |
| awgur@38 | 326 return IterableTypeDescriptor( |
| awgur@38 | 327 iterator_class=tuple, |
| awgur@38 | 328 value_class=get_type_describer(_t) |
| awgur@38 | 329 ) |
| awgur@38 | 330 |
| awgur@38 | 331 else: |
| awgur@38 | 332 return TupleTypeDescriptor( |
| awgur@38 | 333 type_classes=_args |
| awgur@38 | 334 ) |
| awgur@38 | 335 |
| awgur@38 | 336 elif t.__name__ == 'Dict': |
| awgur@38 | 337 if len(_args) != 2: |
| awgur@38 | 338 raise ValueError(f'Неожиданное количество значений типа в составном типе Dict: ' |
| awgur@38 | 339 f'{len(_args)} values=({_args})') |
| awgur@38 | 340 |
| awgur@38 | 341 return DictTypeDescriptor( |
| awgur@38 | 342 key_class=get_type_describer(_args[0]), |
| awgur@38 | 343 value_class=get_type_describer(_args[1]) |
| awgur@38 | 344 ) |
| awgur@38 | 345 |
| awgur@38 | 346 else: |
| awgur@38 | 347 raise ValueError(f'Неизвестный представитель типа "_GenericAlias": {t.__name__}') |
| awgur@38 | 348 |
| awgur@38 | 349 elif type(t).__name__ == '_UnionGenericAlias': |
| awgur@38 | 350 if t.__name__ not in ('Union', 'Optional'): |
| awgur@38 | 351 raise TypeError(f'Неизвестный подтип _UnionGenericAlias: {t.__name__}') |
| awgur@38 | 352 |
| awgur@38 | 353 nullable = False |
| awgur@38 | 354 if t.__name__ == 'Optional': |
| awgur@38 | 355 nullable = True |
| awgur@38 | 356 |
| awgur@38 | 357 try: |
| awgur@38 | 358 _args = t.__args__ |
| awgur@38 | 359 |
| awgur@38 | 360 except AttributeError: |
| awgur@38 | 361 raise TypeError(f'Неизвестный тип хранения внутренних типов для представителя сложного ' |
| awgur@38 | 362 f'типа "_UnionGenericAlias": {t}') |
| awgur@38 | 363 |
| awgur@38 | 364 if len(_args) == 0: |
| awgur@38 | 365 raise ValueError('Не указан ни один тип в конструкции Union') |
| awgur@38 | 366 |
| awgur@38 | 367 type_classes = tuple(map(lambda x: get_type_describer(x), [ i for i in _args if i is not None])) |
| awgur@38 | 368 |
| awgur@38 | 369 return UnionTypeDescriptor( |
| awgur@38 | 370 type_classes=type_classes, |
| awgur@38 | 371 is_nullable=nullable |
| awgur@38 | 372 ) |
| awgur@38 | 373 |
| awgur@38 | 374 else: |
| awgur@38 | 375 return ScalarTypeDescriptor(type_class=t) |