Ответы пользователя по тегу AIOHTTP
  • Как правильно реализовать очередь?

    Vindicar
    @Vindicar
    RTFM!
    Я вижу в тегах aiohttp, так что предполагаю, что код у тебя асинхронный.
    Мне тут как-то доводилось отвечать на подобный вопрос, может, и тебе пригодится...
    Идея простая - ты держишь долгоиграющую задачу, которая мониторит очередь запросов, и выбирает запросы из очереди один за другим. При этом каждый элемент очереди содержит future, в которое будет помещен результат работы корутины, и которое получает код, обратившийся к ресурсу.
    Я попробовал оформить это в виде декоратора, который автоматически троттлит обращения к функции. Возможно, код неоптимален, и его придётся допилить.
    Код

    import typing
    import asyncio
    import functools
    
    
    def throttled(delay: float, measure: typing.Literal['end_to_start', 'start_to_start'] = 'start_to_start'):
        def decorator(actual_func: typing.Coroutine) -> typing.Coroutine:
            queue = None
            task = None
            
            async def _single_query(future, args, kwargs):
                try:
                    result = await actual_func(*args, **kwargs)  # тут делаем асинхронное обращение к сервису
                except BaseException as err:
                    future.set_exception(err)  # была ошибка - теперь await future выкинет исключение
                else:
                    future.set_result(result)  # полуен результат - await future вернёт его
            
            async def _work_loop():
                nonlocal queue
                nonlocal task
                while True:
                    try:
                        # ждем, пока не придёт запрос, или пока не закончится таймаут
                        future, args, kwargs = await asyncio.wait_for(queue.get(), delay)
                    except asyncio.TimeoutError:  # новые запросы долго не приходят, сворачиваем работу, чтобы не тратить ресурсы
                        queue = None
                        task = None
                        return
                    single_task = _single_query(future, args, kwargs)
                    if measure == 'start_to_start':
                        asyncio.create_task(single_task)
                    else:
                        await single_task
                    queue.task_done()  # каждому успешному get() соответствует task_done()
                    await asyncio.sleep(delay)
            
            @functools.wraps(actual_func)
            async def query(*args, **kwargs):
                nonlocal queue  # обращение к переменной выше уровнем, но не глобальной
                nonlocal task
                future = asyncio.Future()  # Future просигналит, когда наш запрос будет обслужен
                if task is None:  # либо это первый запрос, либо запросы долго не приходили, и мы свернули работу
                    queue = asyncio.Queue()
                    task = asyncio.create_task(_work_loop())
                await queue.put((future, args, kwargs))
                return await future
            
            return query
        
        return decorator



    Пример использования:
    # delay - минимальный интервал между запросами в секундах
    # measure - как мерять интервалы между запросами: начало-начало или конец-начало
    @throttled(delay=5.0, measure='start_to_start')
    async def my_coroutine(*args, **kwargs) -> ReturnValue:
        ...

    При этом если задекорировать несколько функций, каждая из них будет иметь свою очередь задач.
    Минус - задача мониторинга будет висеть некоторое время после последнего запроса. А именно, пока таймаут между запросами не истечет.
    Ответ написан
    3 комментария
  • Когда закроется соединение aiohttp через контекстный менеджер?

    Vindicar
    @Vindicar
    RTFM!
    Технически - да, во втором быстрее.
    Но разница будет микросекундная. Обращение к словарю - быстрая операция, по сравнению даже с парсингом JSON, а он, в свою очередь - быстр по сравнению с сетью.
    Ответ написан
    2 комментария
  • Здравствуйте. Как создать удаление правил на Aiogram?

    Vindicar
    @Vindicar
    RTFM!
    Gagak,
    1. Документация тебя выведет на два метода.
    а) bot.delete_message() подходит, если тебе известно только id сообщения.
    б) Message.delete() это обёртка над предыдущим. Удобно, если у тебя есть объект сообщения (экземпляр класса Message), который нужно удалить.
    Первый метод вообще гуглится первой строкой по "aiogram delete message".
    2. Тебе нужно хранить список пользователей, которые обращались к твоему боту, например, в таблице БД.
    Хранить можно в виде пар id пользователя - id сообщения с правилами. При каждом обращении к боту проверяем наличие пользователя в списке.
    Если пользователя нет в списке, он обращается в первый раз - отправляем сообщение с правилами, заносим в таблицу id пользователя и id сообщения.
    Если пользователь есть в списке и у него есть id сообщения - проверяем, согласился ли он. Если согласился, удаляем сообщение по id (если получится - сообщение старше 48 часов уже не удалить), затем очищаем id сообщения (например ставим NULL), но оставляем пользователя в списке.
    Если пользователь есть в списке и у него id сообщения пустое (NULL), то он уже согласился с правилами, можно работать.
    Ответ написан
  • Как реализовать проект на selenium + aiogram +aiohttp?

    Vindicar
    @Vindicar
    RTFM!
    Есть пакет arsenic, можешь попробовать его.
    Ну или метод на основе run_in_executor(), это позволит завернуть синхронный код в отдельном потоке в обычную асинхронную задачу.
    Единственное, что добавлю...
    Сам новичок в питоне, да и вообщем в программирование

    Учиться программированию на таких вещах - всё равно что учиться вождению на болиде Формулы 1. Соберёшь кучу граблей, и знать и язык, и технологию в итоге будешь хуже среднего. Очень советую обкатать Питон, программирование вообще и асинхронное программирование в частности на более простых примерах.
    Ответ написан
    Комментировать
  • Aiogram: Как сохранить введенные данные пользователя?

    Vindicar
    @Vindicar
    RTFM!
    Ну алгоритм всегда один и тот же.
    Тебе нужно для каждого пользователя бота хранить шаг, на котором он находится, и все введённые ранее данные.
    Тогда по получению текста от пользователя ищем его ID в хранилище, смотрим, на каком он шаге, и пытаемся интерпретировать полученный текст в зависимости от этого.

    А вот где хранить данные - вопрос реализации. Можно сделать словарь вида "ID пользователя - набор данных", можно сделать БД. Принцип это не меняет.
    Ответ написан
    Комментировать
  • Почему не работает семафор?

    Vindicar
    @Vindicar
    RTFM!
    Потому что процесс обращения к серверу разбит на две части.
    Одна - установка соединения, SSL-рукопожатие, формирование и отправка запроса, а также редиректы. Это - get()
    Вторая - загрузка и декодирование тела ответа. Это - text().
    Ты прячешь под семафор только вторую часть, но вполне возможно, что она занимает пренебрежимо мало времени по сравнению с первой. К слову, если хочешь ограничить одновременнные подключения к серверу, то точно нужно прятать под семафор первую часть тоже.
    Ответ написан
  • Как достать вложенный обьект JSON в aiohttp?

    Vindicar
    @Vindicar
    RTFM!
    После парсинга JSON объект превратится в комбинацию из списков и словарей питона.
    Как получить значение в словаре по известному ключу?

    И да, твой пример кривой. Не должно быть одинаковых ключей.
    Ответ написан