def call_api(url: str, *, method: str, error_msg: str, debug: bool, **kwargs) -> Optional[Any]: """ Выполнить POST запрос """ payload = form_request(method=method, **kwargs) if debug: # запомнить, что мы запросили, скрыть секреты if 'secret_key' in kwargs: kwargs['secret_key'] = '***' logger.debug(f'---> {method}({dict_as_args(kwargs)})') r = requests.post(url, json=payload) if r.status_code == 200: body = r.json() if 'result' in body: result = body['result'] if debug: # запомнить, что мы получили logger.debug(f'<--- {method}: <' + str(result)[0:100] + '>') return result else: post_mortem = body['error'] else: post_mortem = r.content.decode('utf-8') logger.critical(post_mortem) fail(error_msg, reason=ConnectionError) return None
def __init__(self, source: Optional[Collection] = None, window: int = 0, sentinel: Any = object()): """ Создание экземпляра. Можно задать базовую коллекцию, размер и объект для пустых слотов :param source: исходная коллекция элементов, на базе которой надо собрать экземпляр :param window: максимальное количество элементов (ширина окна вычисления) :param sentinel: элемент для заполнения пустых ячеек (можно добавить свой) """ self._sentinel = sentinel self._data = [] self._len = 0 self._index = 0 if source: # есть коллекция-прототип из которой можно взять данные if window: self.window = window else: self.window = len(source) self.restore() self.populate(source) elif window: # создаём пустую карусель self.window = window self.restore() else: fail( f'Для создания экземпляра {s_type(self)} необходимо либо указать длину и/или \n' 'дать коллекцию из которой будет собран экземпляр.', reason=ValueError)
def get_instance(cls) -> T: """ Получить валидный экземпляр """ if not cls.__instance: fail(f'Класс {s_type(cls)} ещё не имеет хранимого экземпляра!', reason=ValueError) return cls.__instance
def unregister(cls) -> None: """ Удалить валидный экземпляр """ if not cls.has_instance(): fail(f'Класс {s_type(cls)} ещё не имеет хранимого экземпляра!', reason=ValueError) cls.__instance = None
def register_force(cls, instance: T) -> None: """ Добавить валидный экземпляр """ if cls.__instance: fail(f'Класс {s_type(cls)} уже имеет хранимый экземпляр!', reason=ValueError) cls.__instance = instance
def register(cls, instance: T) -> None: """ Добавить валидный экземпляр """ if not isinstance(instance, cls): fail( f'Класс {s_type(cls)} не добавляет посторонние типы!\n' 'При необходимости используйте метод register_force', reason=ValueError) cls.register_force(instance)
def __init__(self, *args, **kwargs): """ Автоматическая инициация """ if args: fail( f'{s_type(self)} подразумевает создание только из именованных атрибутов', reason=ValueError) for key, value in kwargs.items(): setattr(self, key, value)
def get_instance(mcs, required_cls): """ Обращение от потомка за экземпляром себя """ if isinstance(required_cls, type): if required_cls in mcs._instances: return mcs._instances[required_cls] else: if type(required_cls) in mcs._instances: return mcs._instances[type(required_cls)] fail(f'Метакласс {mcs} не содержит экземпляров {s_type(required_cls)}!', reason=KeyError)
def wrapper(*args, **kwargs) -> Any: """ Враппер """ iteration = 0 while True: try: result = func(*args, **kwargs) break except case as exc: iteration += 1 if verbose: logger.warning(f'{exc} в функции {func.__name__}' f' (итерация {iteration})') if repeats and iteration > repeats: fail(exc) time.sleep(delay)
def __setitem__(self, key: Union[int, slice], value: Any) -> None: """ Записать элемент по индексу. Обеспечивается обычный доступ к внутреннему хранилищу, просто со смещением индекса :param key: ключ индексации, только int :param value: данные для записи (любой тип) """ if not isinstance(key, int): fail( f"Тип {s_type(self)} поддерживает работу только с индексами типа int!", reason=IndexError) if not self._len: fail( f'Попытка присвоить элемент по индексу {key} в пустой объект {s_type(self)}', reason=IndexError) self._data[self.get_real_index(key)] = value return
def __getitem__(self, item: Union[int, slice]) -> Any: """ Обратиться к элементу по индексу. Обеспечивается обычный доступ к внутреннему хранилищу, просто со смещением индекса :param item: ключ индексации :return: содержимое внутреннего хранилища """ if isinstance(item, int): if not self._len: fail( f'Попытка получить элемент из пустого объекта {s_type(self)}', reason=IndexError) result = self._data[self.get_real_index(item)] if result is self._sentinel: fail( f'В экземпляре {s_type(self)} нет элемента с индексом {item!r}', reason=IndexError) return result if isinstance(item, slice): return self.get_contents()[item] fail( f"Тип {s_type(self)} поддерживает работу только с индексами int и slice!", reason=IndexError)
def extract_json(cls, config_name: str, path: str) -> dict: """ Вытащить данные из JSON файла """ if not os.path.exists(path): fail(f'Не найден файл конфигурации: "{path}"', reason=FileNotFoundError) with open(path, mode='r', encoding='utf-8') as file: data = json.load(file) if config_name not in data: fail(f'В конфиге не представлен раздел "{config_name}"', reason=KeyError) config = {**data.get(cls.default_config_name, {}), **data[config_name]} if config: return config fail( f'Не удалось загрузить конфигурацию "{config_name}" из файла: "{path}"', reason=KeyError)