Бегуны

Источник: Lib/asyncio/runners.py

В этом разделе описаны высокоуровневые примитивы asyncio для выполнения кода asyncio.

Они строятся поверх event loop с целью упростить использование асинхронного кода в распространенных сценариях.

Запуск программы asyncio

asyncio.run(coro, *, debug=None, loop_factory=None)

Выполните coroutine coro и верните результат.

Эта функция запускает переданную корутину, заботясь об управлении циклом событий asyncio, завершении работы асинхронных генераторов и закрытии исполнителя.

Эта функция не может быть вызвана, если в том же потоке запущен другой цикл событий asyncio.

Если значение debug равно True, цикл событий будет выполняться в режиме отладки. False отключает режим отладки в явном виде. None используется для соблюдения глобальных настроек Режим отладки.

Если loop_factory не None, то он используется для создания нового цикла событий; в противном случае используется asyncio.new_event_loop(). В конце цикл закрывается. Эта функция должна использоваться в качестве основной точки входа для программ asyncio, и в идеале должна вызываться только один раз. Рекомендуется использовать loop_factory для настройки цикла событий вместо политик. Передача asyncio.EventLoop позволяет запускать asyncio без системы политик.

Исполнителю дается таймаут на выключение продолжительностью 5 минут. Если исполнитель не завершил работу в течение этого времени, выдается предупреждение и исполнитель закрывается.

Пример:

async def main():
    await asyncio.sleep(1)
    print('hello')

asyncio.run(main())

Added in version 3.7.

Изменено в версии 3.9: Обновлено для использования loop.shutdown_default_executor().

Изменено в версии 3.10: По умолчанию значение debug равно None, чтобы соблюдались глобальные настройки режима отладки.

Изменено в версии 3.12: Добавлен параметр loop_factory.

Менеджер контекста бегуна

class asyncio.Runner(*, debug=None, loop_factory=None)

Менеджер контекста, который упрощает многочисленные вызовы асинхронных функций в одном и том же контексте.

Иногда несколько асинхронных функций верхнего уровня должны вызываться в одних и тех же event loop и contextvars.Context.

Если значение debug равно True, цикл событий будет выполняться в режиме отладки. False отключает режим отладки в явном виде. None используется для соблюдения глобальных настроек Режим отладки.

Для переопределения создания цикла можно использовать loop_factory. В обязанности loop_factory входит установка созданного цикла в качестве текущего. По умолчанию используется asyncio.new_event_loop(), который устанавливается в качестве текущего цикла события, а если loop_factory - None, то asyncio.set_event_loop().

В принципе, пример asyncio.run() можно переписать с использованием бегунка:

async def main():
    await asyncio.sleep(1)
    print('hello')

with asyncio.Runner() as runner:
    runner.run(main())

Added in version 3.11.

run(coro, *, context=None)

Выполните coroutine coro во встроенном цикле.

Возвращает результат работы корутины или поднимает ее исключение.

Необязательный аргумент context, содержащий только ключевое слово, позволяет указать пользовательский contextvars.Context для запуска coro. Если None, то используется контекст по умолчанию.

Эта функция не может быть вызвана, если в том же потоке запущен другой цикл событий asyncio.

close()

Закройте бегунок.

Завершение работы асинхронных генераторов, выключение исполнителя по умолчанию, закрытие цикла событий и освобождение встроенного contextvars.Context.

get_loop()

Возвращает цикл событий, связанный с экземпляром бегуна.

Примечание

Runner использует стратегию ленивой инициализации, его конструктор не инициализирует базовые низкоуровневые структуры.

Вложенные петля и контекст создаются при входе в тело with или при первом вызове run() или get_loop().

Обработка прерывания клавиатуры

Added in version 3.11.

Когда signal.SIGINT поднимается Ctrl-C, исключение KeyboardInterrupt по умолчанию поднимается в главном потоке. Однако это не работает с asyncio, поскольку может прервать работу внутренних функций asyncio и привести к зависанию программы при выходе из нее.

Чтобы смягчить эту проблему, asyncio обрабатывает signal.SIGINT следующим образом:

  1. asyncio.Runner.run() устанавливает пользовательский обработчик signal.SIGINT до выполнения любого пользовательского кода и удаляет его при выходе из функции.

  2. Параметр Runner создает главную задачу для переданной coroutine для ее выполнения.

  3. Когда signal.SIGINT поднимается Ctrl-C, пользовательский обработчик сигналов отменяет основную задачу, вызывая asyncio.Task.cancel(), который поднимает asyncio.CancelledError внутри основной задачи. Это приводит к разворачиванию стека Python, блоки try/except и try/finally могут быть использованы для очистки ресурсов. После отмены основной задачи asyncio.Runner.run() поднимает KeyboardInterrupt.

  4. Пользователь может написать жесткий цикл, который не может быть прерван asyncio.Task.cancel(), в этом случае второй следующий Ctrl-C немедленно поднимает KeyboardInterrupt, не отменяя основную задачу.