Задать вопрос
Ответы пользователя по тегу Асинхронное программирование
  • Асинхронные функции и цикл событий. RuntimeWarning: coroutine 'Application.initialize' was never awaited?

    Vindicar
    @Vindicar
    RTFM!
    :0: RuntimeWarning: coroutine 'Application.shutdown' was never awaited
    :0: RuntimeWarning: coroutine 'Application.initialize' was never awaited

    Ты вызываешь указанные функции как обычные (Application.initialize()), а это корутины. Их так вызывать бесполезно. Если ты их вызываешь внутри другой корутины (async def функции), то можешь исправить вызов на await Application.initialize(). Если же ты пытаешься вызвать их из обычной функции (просто def), то все становится куда сложнее...
    Ответ написан
    Комментировать
  • Как обрабатывать ошибки в асинхронном коде?

    Vindicar
    @Vindicar
    RTFM!
    try: 
        loop.create_task(exc())
    except ZeroDivisionError as ex:
        print(f"Ошибка {ex} обработана")

    1. Ты создаёшь задачу на базе корутины exc(). Созданная задача не выполнится немедленно, а только встанет в очередь исполнения (хотя в питоне 3.12 это поведение можно изменить, но по умолчанию это так). При этом с корутиной ассоциируется future-объект, который находится в состоянии "ожидание", так как корутина ещё не завершила работу.
    2. Ты проверяешь, не возникло ли исключение в процессе создания задачи. Это может произойти, только если exc() - не корутина. В остальных случаях операция будет успешна независимо от содержания exc(), так как см. пункт 1.
    3. Ты вызываешь loop.run_forever(). Рабочий цикл (loop) смотрит в очередь исполнения, и видит в нём только корутину exc(). Она получает управление.
    4. Корутина exc() выбрасывает исключение, но не ловит его. Ассоциированный с корутиной future-объект переходит из состояния "ожидание" в состояние "отказ", и сохраняет информацию об исключении. exc() завершает работу.
    5. Рабочий цикл проверяет, кто хранит ссылку на task - и понимает, что никто. Как следствие, даже в будущем никто не сможет узнать, что корутина выкинула исключение. Поскольку рабочий цикл - штука типовая, он понятия не имеет, что делала наша корутина и как надо реагировать на исключение в ней. А потому единственный вариант для него - написать в журнал работы о непойманном исключении и надеяться, что программист это увидит и поправит.
    task = asyncio.create_task(exc())
    try:
        # await asyncio.gather(task)  # <- gather() не нужно, если у тебя одна задача
        await task
    except ZeroDivisionError as ex:
        print(f"Ошибка {ex} обработана")

    1. Ты создаёшь задачу (успешно), а потом с помощью await-вызова просишь дождаться её завершения и получить результат.
    2. Корутина main() приостанавливает своё выполнение и сохраняет свой контекст, а также встаёт в очередь, ожидая, когда сработает future-объект, связанный с task.
    3. Рабочий цикл (loop) asyncio смотрит в очередь выполнения и видит, что корутина exc() готова выполняться (она не находится в await вызове, а только начала работу).
    4. Корутина exc() получает управление, выполняется, и генерирует исключение, которое не поймано внутри этой корутины. Future-объект, связанный с этой корутиной, переходит из состояния "ожидание" в состояние "отказ", и сохраняет информацию об исключении. Корутина exc() завершает выполнение. При этом о её future-объекте знает корутина main(), поэтому рабочий цикл не дёргается по этому поводу - у main() будет возможность отреагировать на происходящее.
    5. Рабочий цикл (loop) смотрит в очередь выполнения и видит, что там только корутина main(), причём она готова выполняться - future-объект, который она ждёт, более не находится в состоянии ожидания.
    6. Корутина main() получает управление и восстанавливает свой контекст, продолжая с того места, где она остановилась. Так как future-объект находится в состоянии "отказ", оператор await читает из него информацию об исключении и перевыбрасывает это исключение внутри main().
    7. Это исключение обрабатывается блоком try-except в main() как обычно.
    Ответ написан
    Комментировать
  • Как асинхронно создать QR код?

    Vindicar
    @Vindicar
    RTFM!
    Потому что создание QR-кода - это вычислительная операция, а не операция ввода/вывода. Вычислительные операции не имеет смысла делать асинхронными. Если создание QR-кода занимает у тебя достаточно длительное время, чтобы это было проблемой для остальной программы - засунь этот код в поток через run_in_executor(). Тогда у тебя будет асинхронный future-объект, который можно awaitить как обычно.
    Ответ написан
    4 комментария
  • Почему asyncio.current_task() не передается в функцию?

    Vindicar
    @Vindicar
    RTFM!
    Цитата из документации:
    Return the currently running Task instance, or None if no task is running.

    Как я это понимаю, если ты запланировал выполнение функции как задачи через asyncio.create_task(), то эта функция, и все, вызываемые в ней, смогут получить объект задачи, вызвав asyncio.current_task().
    Если же управление в текущую функции было передано без использования задач на любом из уровней, только через await вызовы, то current_task() вернёт None.

    Но вообще довольно странно. Эксперимент показывает, что хотя бы одна задача должна быть.
    Вот код

    import asyncio
    
    async def print_task():
        print(asyncio.current_task())
    
    
    async def foobar():
        await print_task()
    
    
    async def main():
        print('Direct call')
        await foobar()
        print('create_task')
        task = asyncio.create_task(foobar())
        await task
    
    # asyncio.run(main())  # даст такой же результат
    asyncio.get_event_loop().run_until_complete(main())

    На питоне 3.11 код выводит два объекта задачи, один создаётся run() или run_until_complete(), а другой - create_task(). Тогда получается, что получить None невозможно.
    Ответ написан
    Комментировать
  • Как правильно обработать exception PasswordHashInvalidError?

    Vindicar
    @Vindicar
    RTFM!
    Огранизовать цикл while True, при вводе правильного пароля (т.е. если не было исключения) делать break.
    while True:
        try:
            attempt_stuff_and_things()
        except SomeException as err:
            react_to_error(err)
        else:  # исключения не было
            break
    Ответ написан
    Комментировать
  • Как использовать cfscrape асинхронно?

    Vindicar
    @Vindicar
    RTFM!
    loop.run_in_executor() чтобы запустить код в отдельном потоке, и обернуть этот поток в асинхронный таск.
    Ответ написан
  • Почему асинхронный телебот увеличивает кол-во потоков?

    Vindicar
    @Vindicar
    RTFM!
    while True:
                print(1)
                await asyncio.sleep(1)

    Корутина может выполняться в нескольких экземплярах параллельно. У тебя в коде вечный цикл, поэтому старые запущенные экземпляры никогда не завершаются, так что при запуске новых у тебя появляется всё больше и больше экземпляров.
    Ответ написан
  • Как запустить две функции параллельно в asyncio?

    Vindicar
    @Vindicar
    RTFM!
    Намудрил-то, намудрил-то...
    background_func() сделай асинхронной, run_in_executor() в ней нафиг не нужен, можно будет просто сделать await bot.send_message(...).
    time.sleep(), соответственно, меняешь на await asyncio.sleep().
    executor.start_polling() вызывай прямо в теле программы, без выкрутасов.

    Для запуска функции при старте бота опиши её как
    async def bot_startup(dp: Dispatcher):
    и передай её в параметре on_startup при вызове start_polling().
    А внутри bot_startup уже делай что нужно, например, asyncio.create_task(background_func()).
    Обрати внимание, без await - тогда background_func() будет работать параллельно боту.
    Ответ написан
    2 комментария
  • Как вернуть значения из синхронного колбека в corotine несколько раз?

    Vindicar
    @Vindicar
    RTFM!
    Я вижу два потенциальных источника проблем.
    1. Ты говоришь, что библиотека будет дёргать callback при изменении значения свойства. А когда она будет это делать?
    Ей ведь нужно выполнить для этого какой-то код, причём наверняка синхронный. Пусть какой-то метод, который проверит очередь событий, извлечёт оттуда новые и дёрнет соответствующие callbak'и. Этот код точно вызывается?
    2. Ты делаешь await iface.call_method('bla'). Ты уверен, что обновление свойства произойдёт после завершения await? Если оно произойдёт до, то твой вызов wait_property() не увидит обновлений. Возможно, стоит вешать callback до вызова call_method()?
    Ответ написан
    Комментировать
  • Почему код не работает асинхронно?

    Vindicar
    @Vindicar
    RTFM!
    async def parse_card(card):
        d = dict()
        # из карточки берется html
        # "вставляется" в объект супа
        # и возвращается словарь
        # внутри этой функции не используются await
        return d


    Ну а ты что хочешь-то? Асинхронность в питоне предназначена для распараллеливания операций ввода-вывода (и сводимых к ним), а не для параллельного выполнения расчётов. У тебя parse_card() всё равно что синхронная.
    Ответ написан
    1 комментарий
  • Как запустить выполнение асинхронной функции с определенной частотой выполнения?

    Vindicar
    @Vindicar
    RTFM!
    Храни в asyncio.Queue очередь запросов к API. Отдельная задача пусть выбирает запросы из очереди, отправляет, получает ответ и оповещает о результате. Например, так.
    import asyncio
    import typing
    
    class ThrottledResource:
        def __init__(self, delay: float):
            self._delay = delay
            self._queue = asyncio.Queue()
            self._task = None
        
        def start(self):
            self._task = asyncio.create_task(self._work_loop)
        
        def stop(self):
            self._task.cancel()
            self._task = None
    
        # этот метод вызывается клиентским кодом, получает параметры и возвращает отклик спустя время.
        async def query(self, params):
            future = asyncio.Future()  # Future просигналит, когда наш запрос будет обслужен
            await self._queue.put((future, params))
            result = await future  # корутина спит, пока запрос не обслужат
            return result
    
        async def _work_loop(self):
            while True:
                future, params = await. self._queue.get()  # ждем, пока не придёт запрос
                try:
                    result = await call_api(params)  # тут делаем асинхронное обращение к сервису
                except Exception as err:
                    future.set_exception(err)  # была ошибка - теперь await future выкинет исключение
                else:
                    future.set_result(result)  # полуен результат - await future вернёт его
                self._queue.task_done()  # каждому успешному get() соответствует task_done()
                asyncio.sleep(self._delay)  # можно учесть, сколько времени делался запрос. Но стоит ли?

    Код примерный, но идею передаёт. Использоваться будет как-то так
    api = ThrottledResource(delay=1.0)
    api.start()
    ...
    result = await api.query(params)  # await подождёт, пока не дойдёт очередь до нашего запроса


    Нужно добавить обработку ошибок, корректное завершение работы при наличии задач в очереди, и так далее.
    Технически вместо класса можно было реализовать это всё в виде декоратора над replier(), но это уже на вкус и цвет.
    Ответ написан
    6 комментариев
  • Насколько критично использование pydantic в асинхронных приложениях?

    Vindicar
    @Vindicar
    RTFM!
    Не стоит спользовать длительные синхронные вызовы в асинхронном коде.
    Во-первых, валидация - это CPU-bound задача, а не IO-bound. Её можно вынести в поток, но и только. Просто асинхронно её не выполнишь.
    Во-вторых, если у тебя не ChatGPT данные валидирует, скорее всего время выполнения валидации будет пренебрежимо малым.
    Ответ написан
    Комментировать
  • Можно ли узнать какая корутина вызвала ошибку в asyncio.gather?

    Vindicar
    @Vindicar
    RTFM!
    Вообще-то gather() возвращает исключения и результаты в том же порядке, в котором переданы корутины. Всегда.
    Но ты сам себе выстрелил в ногу вот этим: *{f1(), f2()}
    Ты указал литерал множества, а это не упорядоченная коллекция. Так что в каком порядке были переданы корутины - хз.
    Положи их в кортеж или в список перед отдачей в gather(), и проблема уйдёт - элемент выходного списка с индексом i будет соответствовать корутине с индексом i в исходном кортеже/списке.
    Ответ написан
    Комментировать
  • Фундаментальное отличие async await в python и javascript?

    Vindicar
    @Vindicar
    RTFM!
    Разницы нет, в том числе в твоём описании.
    Текущая корутина при await-вызове приостанавливается, её состояние сохраняется.
    При этом реактор может заниматься другими вещами (в частности, выполнением await-вызова).
    Когда await-вызов завершиться, состояние корутины будет восстановлено, и она продолжит выполнение.
    Ответ написан
    4 комментария
  • Как получить результат выполнения задачи в Asyncio?

    Vindicar
    @Vindicar
    RTFM!
    await tay
    Угадай с трёх раз, что возвращает это выражение?
    Правильно. Значение, которое вернёт корутина, запущенная в рамках задачи. Просто присвой его переменной.
    А вообще в твоём случае и задачи-то не требуются.
    Освой asyncio.gather().
    Она позволит выполнить переданные корутины одновременно, и вернёт массив их результатов.
    results = await asyncio.gather(coro1(x,y), coro2(a,b), ...)
    res1, res2, ... = results
    Ответ написан
    Комментировать
  • Не будет ли этот код блокировать асинхронный код?

    Vindicar
    @Vindicar
    RTFM!
    Проблема в том, что в Питоне потоки немножко увечные (ключевое слово GIL), а потому реально работают только в двух случаях:
    1. Исполняемый длительный код написан не на питоне, а принадлежит одному из бинарных модулей
    2. Исполняемый код большую часть времени ждёт (например, операции ввода/вывода).
    В случае с многопроцессностью проблем меньше, но появляетя проблема обмена данными между процессами.

    Я бы запустил несколько процессов воркеров, которые умеют рендерить предоставленные данные и возвращать график как массив байт, содержащий изображение. Тогда основной бот кидает задание в некую очередь, один из воркеров просыпается, рендерит задание, и кладёт ответ в другую очередь. Словом, паттерн поставщик-потребитель. Конечно, могут быть проблемы с тем, чтобы подружить multiprocessing.Queue с асинхронным кодом, но имхо так всё равно изящнее.
    Более того, если искомые данные лежат в БД или ином внешнем источнике - можно нагрузить воркеров их извлечением. Тогда задание будет содержать в себе только критерии выборки данных.
    Ответ написан
  • Как сделать асинхронное выполнение функций в Python3?

    Vindicar
    @Vindicar
    RTFM!
    inpit() асинхронным сделать можно как показано выше.

    А дальше
    async def sender():
        while True:
            ...
    
    async def getter():
        ...
    
    async def main():
        asyncio.create_task(sender())
        await getter()
    
    asyncio.run(main())
    Ответ написан
    Комментировать
  • Пишу бота для телеграм (telebot), проблема с асинхронными функциями. В чем причина ошибки?

    Vindicar
    @Vindicar
    RTFM!
    1. await так не пишется.
    x = await asyncio.create_task(startq(message))
    2. Зачем ты делаешь await?
    asyncio.create_task() создаёт корутину, которая будет выполняться конкуррентно с текущей (как бы параллельно)
    await приостанавливает выполнение текущей корутины, пока не завершится вызываемая (справа от await).
    Т.е. await create_task(...) - это не очень осмысленно: сначала запускаешь параллельную корутину, а потом всё равно стоишь и ждёшь её.
    Если тебе нужно запустить корутину startq() и дождаться её выполнения (ну и получить возвращаемое значение) - пиши просто x = await startq(message)
    Если тебе нужно запустить корутину startq() и пусть она дальше выполняется сама по себе, тебе от неё ничего не надо - достаточно будет asyncio.create_task(startq(message))
    Ответ написан
    1 комментарий