Событийный цикл


Предисловие

Цикл событийный — это ядро любого asyncio приложения. Событийные циклы запускают асинхронные задачи и обратные вызовы, выполняют сетевые операции ввода-вывода и запускают подпроцессы.

Разработчики приложений обычно вызывают высокоуровневые asyncio функции, например asyncio.run(), и им редко нужно ссылаться на объект цикла или вызывать его методы. Раздел предназначен в основном для авторов низкоуровневого кода, библиотек и фреймворков, которым требуется более тонкий контроль над поведением событийного цикла.

Получение цикла событий

Следующие далее низкоуровневые функции используются для получения, установки или создания цикла событий:

asyncio.get_running_loop()

Возвращает запущенный цикл событий в текущем потоке ОС.

Если нет запущенного цикла событий, вызывается RuntimeError. Функция может вызвана только из корутины или обратного вызова.

Добавлено в версии 3.7.

asyncio.get_event_loop()

Получает текущий цикл событий.

Если в текущем основным потоке ОС не установлен текущий цикл событий, а set_event_loop() ещё не был вызван, asyncio создаст новый цикл событий и установит его как текущий.

Поскольку у функции сложное поведение (особенно при использовании настраиваемых политик цикла событий), вызов функции get_running_loop() лучше, чем get_event_loop() в корутинах и обратных вызовах.

Рекомендуется вызывать функцию asyncio.run() вместо использования функций нижнего уровня для создания и закрытия цикла событий вручную.

asyncio.set_event_loop(loop)

Устанавливает loop как текущий цикл событий для текущего потока ОС.

asyncio.new_event_loop()

Создает новый объект цикла событий.

Обратите внимание, что поведение функций get_event_loop(), set_event_loop() и new_event_loop() можно изменить установив настраиваемую политику цикла событий.

Содержание

На странице документации содержится следующие разделы:

Методы цикла событий

У циклов событий есть низкоуровневое API для следующих целей:

Запуск и остановка цикла

loop.run_until_complete(future)

Выполняется, пока не завершится future (экземпляр Future).

Если аргумент — объект корутины, он неявно планируется для запуска как asyncio.Task.

Возвращает результат Future или вызвать его исключение.

loop.run_forever()

Запускает цикл обработки событий, пока не будет вызван stop().

Если stop() вызывается до вызова run_forever(), цикл опрашивает селектор ввода-вывода один раз с нулевым таймаутом, запускает все обратные вызовы, запланированные в ответ на события ввода-вывода (и те, которые уже были запланированы), а затем завершается.

Если stop() вызывается во время работы run_forever(), цикл выполнит текущий пакет обратных вызовов и затем завершится. Обратите внимание, что новые обратные вызовы, запланированные обратными вызовами, не будут выполняться; вместо этого они будут запущены при следующем вызове run_forever() или run_until_complete().

loop.stop()

Останавливает цикл событий.

loop.is_running()

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

loop.is_closed()

Возвращает True, если цикл событий был закрыт.

loop.close()

Закрывает цикл событий.

При вызове этой функции цикл не должен выполняться. Все ожидающие обратные вызовы будут отброшены.

Метод очищает все очереди и завершает работу исполнителя, но не дожидается завершения работы исполнителя.

Метод идемпотентен и необратим. Никакие другие методы не должны вызываться после закрытия цикла событий.

coroutine loop.shutdown_asyncgens()

Планирует закрытие всех открытых в данный момент объектов асинхронного генератора вызовом aclose(). После вызова данного метода цикл событий вызывает предупреждение, если итерируется новый асинхронный генератор. Его следует использовать для надежного завершения всех запланированных асинхронных генераторов.

Обратите внимание, что при использовании asyncio.run() нет необходимости вызывать эту функцию.

Пример:

try:
    loop.run_forever()
finally:
    loop.run_until_complete(loop.shutdown_asyncgens())
    loop.close()

Добавлено в версии 3.6.

Планирование обратных вызовов

loop.call_soon(callback, *args, context=None)

Планирует callback колбэк, вызываемый с аргументами args на следующей итерации цикла событий.

Обратные вызовы (Callbacks) вызываются в том порядке, в котором они зарегистрированы. Каждый обратный вызов будет вызываться ровно один раз.

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

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

Метод непотокобезопасный.

loop.call_soon_threadsafe(callback, *args, context=None)

Потокобезопасный вариант call_soon(). Должен использоваться для планирования обратных вызовов из другого потока.

См. раздел документации конкурентность и многопоточность.

Изменено в версии 3.7: Был добавлен параметр context только для ключевых параметров. См. PEP 567 для получения более подробной информации.

Примечание

Большинство функций планирования asyncio не позволяют передавать ключевые аргументы. Для этого используйте functools.partial():

# запланировать "print("Hello", flush=True)"
loop.call_soon(
    functools.partial(print, "Hello", flush=True))

Использование частичных объектов обычно более удобно, чем использование лямбда-выражений, поскольку asyncio может лучше отображать частичные объекты в сообщениях об отладке и ошибках.

Планирование отложенных обратных вызовов

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

loop.call_later(delay, callback, *args, context=None)

Планирует callback, вызываемый после заданного количества delay секунд (может быть либо int, либо float).

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

callback будет вызван ровно один раз. Если два обратных вызова запланированы на одно и то же время, порядок, в котором они вызываются, не определён.

Необязательные позиционные args будут переданы обратному вызову при его вызове. Если нужно вызвать callback с ключевыми аргументами, используйте functools.partial().

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

Изменено в версии 3.7: Был добавлен только ключевой параметр context. См. PEP 567 для получения подробной информации.

Изменено в версии 3.8: В Python 3.7 и более ранних версиях с реализацией цикла событий по умолчанию delay не могла превышать одного дня. Это было исправлено в Python 3.8.

loop.call_at(when, callback, *args, context=None)

Планирует callback, вызываемый в заданную абсолютную метку времени, when (int или float), используя ту же временную ссылку, что и loop.time().

Поведение данного метода такое же, как у call_later().

Возвращается экземпляр asyncio.TimerHandle, который можно использовать для отмены обратного вызова.

Изменено в версии 3.7: Был добавлен только ключевой параметр context. См. PEP 567 для получения подробной информации.

Изменено в версии 3.8: В Python 3.7 и ранее с реализацией цикла событий по умолчанию разница между when и текущим временем не могла превышать одного дня. Это было исправлено в Python 3.8.

loop.time()

Возвращает текущее время в виде значения float в соответствии с внутренними монотонными часами цикла событий.

Примечание

Изменено в версии 3.8: В Python 3.7 и более ранних версиях таймауты (относительная delay или абсолютная when) не должны превышать одного дня. Это было исправлено в Python 3.8.

См.также

Функция asyncio.sleep().

Создание Футур и Задач

loop.create_future()

Создает объект asyncio.Future, прикрепленный к циклу событий.

Это предпочтительный способ создания Футур (Futures) в asyncio. Он позволяет сторонним циклам событий предоставлять альтернативные реализации объекта Футуры (с лучшей производительностью или инструментарием).

Добавлено в версии 3.5.2.

loop.create_task(coro, *, name=None)

Планирует выполнение корутины. Возвращает объект Task.

Сторонние циклы событий могут использовать собственный подкласс Task для взаимодействия. В этом случае тип результата будет подклассом Task.

Если указан аргумент name, а не None, он устанавливается как имя задачи с использованием Task.set_name().

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

loop.set_task_factory(factory)

Задаёт фабрику задач, которая будет использоваться loop.create_task().

Если factoryNone, будет установлена фабрика задач по умолчанию. В противном случае factory должен быть вызываемым с сигнатурой, соответствующей (loop, coro), где loop — это ссылка на активный цикл событий, а coro — объект корутины. Вызываемый объект должен возвращать asyncio.Future-совместимый объект.

loop.get_task_factory()

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

Открытие сетевых подключений

coroutine loop.create_connection(protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None, happy_eyeballs_delay=None, interleave=None)

Открывает соединение потокового транспорта с заданным адресом, указанным host и port.

Семейство сокетов может быть AF_INET или AF_INET6 в зависимости от host (или аргумента family, если он указан).

Тип сокета будет SOCK_STREAM.

protocol_factory должен быть вызываемым, возвращающим реализацию протокола asyncio.

Метод попытается установить соединение в фоновом режиме. В случае успеха возвращается пара (transport, protocol).

Хронологический синопсис основной операции:

  1. Соединение установлено, и для него создаётся транспорт.
  2. protocol_factory вызывается без аргументов и ожидается, что он вернёт экземпляр протокола.
  3. Экземпляр протокола связывается с транспортом путём вызова его метода connection_made().
  4. В случае успеха возвращается кортеж (transport, protocol).

Созданный транспорт является зависящим от реализации двунаправленным потоком.

Другие аргументы:

  • ssl: если задано и не ложно, создаётся SSL/TLS транспорт (по умолчанию создаётся простой TCP транспорт). Если ssl объект класса ssl.SSLContext, контекст используется для создания транспорта; если sslTrue, используется контекст по умолчанию, возвращенный из ssl.create_default_context().

  • server_hostname устанавливает или переопределяет имя хоста, с которым будет сопоставляться сертификат целевого сервера. Следует передавать, только если ssl не None. По умолчанию используется значение аргумента host. Если host пуст, по умолчанию нет, и вы должны передать значение server_hostname. Если server_hostname пустая строка, сопоставление имён хостов отключено (что представляет собой серьезную угрозу безопасности, допускающую потенциальные атаки типа «злоумышленник в середине»).

  • family, proto, flags — это необязательное семейство адресов, протокол и флаги, передаваемые getaddrinfo() для разрешения host. Если задано, все они должны быть целыми числами из соответствующих констант модуля socket.

  • если задан happy_eyeballs_delay, включает Happy Eyeballs для данного соединения. Это должно быть число с плавающей запятой, представляющее время в секундах для ожидания завершения попытки подключения перед запуском следующей попытки параллельно. Это «Задержка попытки подключения», как определено в RFC 8305. Разумное значение по умолчанию, рекомендованное RFC — 0.25 (250 миллисекунд).

  • interleave управляет переупорядочиванием адресов, когда имя хоста преобразуется в несколько IP-адресов. Если 0 или не указано, переупорядочивание не выполняется, и адреса проверяются в порядке, возвращаемом getaddrinfo(). Если задано положительное целое число, адреса чередуются по семейству адресов, и данное целое число интерпретируется как «Счетчик первого семейства адресов», как определено в RFC 8305. По умолчанию — 0, если happy_eyeballs_delay не указан, и 1, если он указан.

  • если задан sock, то должен быть подключённым объектом socket.socket, используемый транспортом. Если указан sock, не следует указывать ни host, port, family, proto, flags, happy_eyeballs_delay, interleave, ни local_addr.

  • если задан local_addr, то это кортеж (local_host, local_port), используемый для локальной привязки сокета. local_host и local_port ищутся getaddrinfo(), аналогично host и port.

  • ssl_handshake_timeout — (для TLS-соединения) время в секундах, в течение которого нужно дождаться завершения подтверждения TLS, прежде чем разорвать соединение. 60.0 секунда, если None (по умолчанию).

Добавлено в версии 3.8: Добавлены параметры happy_eyeballs_delay и interleave.

Алгоритм Happy Eyeballs: успех с хостами двойного стека. Когда работает путь и протокол IPv4 сервера, но путь и протокол IPv6 сервера не работают, клиентское приложение с двойным стеком испытывает значительную задержку соединения по сравнению с клиентом, работающим только с IPv4. Это нежелательно, потому что из-за этого клиент с двойным стеком будет хуже работать с пользователем. Документ определяет требования к алгоритмам, уменьшающую видимую для пользователя задержку, и предоставляет алгоритм.

Для получения дополнительной информации см. rfc 6555

Добавлено в версии 3.7: Параметр ssl_handshake_timeout.

Изменено в версии 3.6: Параметр сокета TCP_NODELAY установлен по умолчанию для всех TCP-соединений.

Изменено в версии 3.5: Добавлена поддержка SSL/TLS в ProactorEventLoop.

См.также

Функция open_connection() — это альтернативный API высокого уровня. Она возвращает пару (StreamReader, StreamWriter), используемые в async/await коде.

coroutine loop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_address=None, reuse_port=None, allow_broadcast=None, sock=None)

Примечание

Параметр reuse_address больше не поддерживается, поскольку использование SO_REUSEADDR представляет серьёзную проблему безопасности для UDP. Явная передача reuse_address=True вызовет исключение.

Когда несколько процессов с разными UID назначают сокеты одинаковому адресу сокета UDP с SO_REUSEADDR, входящие пакеты могут случайным образом распределяться между сокетами.

Для поддерживаемых платформ reuse_port может использоваться как замена аналогичной функциональности. С reuse_port вместо него используется SO_REUSEPORT, что специально предотвращает назначение сокетов процессам с разными UID на один и тот же адрес сокета.

Создает дейтаграммное соединение.

Семейство сокетов может быть AF_INET, AF_INET6 или AF_UNIX, в зависимости от host (или аргумента family, если он предоставлен).

Тип сокета будет SOCK_DGRAM.

protocol_factory — вызываемый, возвращающий реализацию протокола.

В случае успеха возвращается кортеж (transport, protocol).

Другие аргументы:

  • если задан local_addr, то это кортеж (local_host, local_port), используемый для локальной привязки сокета. local_host и local_port ищутся getaddrinfo().
  • если задан remote_addr, то это кортеж (remote_host, remote_port), используемый для подключения сокета к удаленному адресу. remote_host и remote_port ищутся getaddrinfo().
  • family, proto, flags — это необязательное семейство адресов, протокол и флаги, которые необходимо передать в getaddrinfo() для разрешения host. Если задано, все они должны быть целыми числами из соответствующих констант модуля socket.
  • reuse_port сообщает ядру разрешить привязку этой конечной точки к тому же порту, к которому привязаны другие существующие конечные точки, при условии, что все они устанавливают данный флаг при создании. Параметр не поддерживается в Windows и некоторых системах Unix. Если константа SO_REUSEPORT не определена, возможность не поддерживается.
  • allow_broadcast сообщает ядру разрешить этой конечной точке отправлять сообщения на широковещательный адрес.
  • sock можно дополнительно указать, чтобы использовать уже существующий, уже подключённый объект socket.socket, который будет использоваться транспортом. Если указано, local_addr и remote_addr следует пропустить (должно быть None).

См. примеры Клиентский эхо протокол UDP и Протокол эхо-сервера UDP.

Изменено в версии 3.4.4: Были добавлены параметры family, proto, flags, reuse_address, reuse_port, allow_broadcast и sock.

Изменено в версии 3.8.1: Параметр reuse_address больше не поддерживается из соображений безопасности.

Изменено в версии 3.8: Добавлена поддержка Windows.

coroutine loop.create_unix_connection(protocol_factory, path=None, *, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None)

Создает соединение Unix.

Семейство сокетов будет AF_UNIX; тип сокета будет SOCK_STREAM.

В случае успеха возвращается кортеж (transport, protocol).

path — это обязательное имя сокета домена Unix, если не указан параметр sock. Поддерживаются абстрактные сокеты Unix, пути str, bytes и Path.

См. документацию метода loop.create_connection() для получения информации об аргументах.

Доступность: Unix.

Добавлено в версии 3.7: Параметр ssl_handshake_timeout.

Изменено в версии 3.7: Параметр path теперь может быть путеподобным объектом.

Создание сетевых серверов

coroutine loop.create_server(protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, start_serving=True)

Создает TCP-сервер (тип сокета SOCK_STREAM), прослушивающий port адреса host.

Возвращает объект Server.

Аргументы:

  • protocol_factory — вызываемый, возвращающий реализацию протокола.
  • Параметр host может иметь несколько типов, которые определяют, где сервер будет прослушивать:
    • Если host — строка, TCP-сервер привязан к одному сетевому интерфейсу, указанному host.
    • Если host — последовательность строк, TCP-сервер привязан ко всем сетевым интерфейсам, указанным в этой последовательности.
    • Если host — пустая строка или None, предполагаются все интерфейсы, и будет возвращен список из нескольких сокетов (скорее всего, один для IPv4, а другой — для IPv6).
  • Для family можно задать значение socket.AF_INET или AF_INET6, чтобы сокет использовал IPv4 или IPv6. Если не установлен, family будет определяться по имени хоста (по умолчанию AF_UNSPEC).
  • flags — это битовая маска для getaddrinfo().
  • sock можно дополнительно указать для использования уже существующего объекта сокета. Если указано, host и port указывать не нужно.
  • backlog — это максимальное количество подключений в очереди, переданных listen() (по умолчанию 100).
  • ssl может быть установлен на экземпляр SSLContext, чтобы включить TLS по принятым соединениям.
  • reuse_address указывает ядру повторно использовать локальный сокет в состоянии TIME_WAIT, не дожидаясь истечения его естественного тайм-аута. Если не указано, будет автоматически установлено значение True в Unix.
  • reuse_port сообщает ядру разрешить привязку этой конечной точки к тому же порту, к которому привязаны другие существующие конечные точки, при условии, что все они устанавливают данный флаг при создании. Опция не поддерживается в Windows.
  • ssl_handshake_timeout — (для TLS-сервера) время в секундах, в течение которого необходимо дождаться завершения рукопожатия TLS, прежде чем разорвать соединение. 60.0 секунды, если None (по умолчанию).
  • start_serving, установленный на True (по умолчанию), заставляет созданный сервер немедленно начать принимать соединения. Если установлено значение False, пользователь должен ждать на Server.start_serving() или Server.serve_forever(), чтобы сервер начал принимать соединения.

Добавлено в версии 3.7: Добавлены параметры ssl_handshake_timeout и start_serving.

Изменено в версии 3.6: Параметр сокета TCP_NODELAY установлен по умолчанию для всех TCP-соединений.

Изменено в версии 3.5: Добавлена поддержка SSL/TLS в ProactorEventLoop.

Изменено в версии 3.5.1: Параметр host может быть последовательностью строк.

См.также

Функция start_server() — это альтернативный API более высокого уровня, возвращающий пару используемую в async/await коде StreamReader и StreamWriter.

coroutine loop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, start_serving=True)

Аналогичен loop.create_server(), но работает с семейством сокетов AF_UNIX.

path — это имя сокета домена Unix, и он обязателен, если не указан аргумент sock. Поддерживаются абстрактные сокеты Unix, пути str, bytes и Path.

См. документацию метода loop.create_server() для получения информации об аргументах метода.

Доступность: Unix.

Добавлено в версии 3.7: Параметры ssl_handshake_timeout и start_serving.

Изменено в версии 3.7: Параметр path теперь может быть объектом Path.

coroutine loop.connect_accepted_socket(protocol_factory, sock, *, ssl=None, ssl_handshake_timeout=None)

Оборачивает уже принятое соединение в пару транспорт/протокол.

Метод может использоваться серверами, которые принимают соединения вне asyncio, но используют asyncio для их обработки.

Параметры:

  • protocol_factory должен быть вызываемым, возвращающим реализацию протокола.
  • sock — это уже существующий объект сокета, возвращенный из socket.accept.
  • ssl можно установить на SSLContext, чтобы включить SSL через принятые соединения.
  • ssl_handshake_timeout — это (для SSL-соединения) время в секундах, в течение которого нужно дождаться завершения подтверждения SSL, прежде чем разорвать соединение. 60.0 секунды, если None (по умолчанию).

Возвращает пару (transport, protocol).

Добавлено в версии 3.7: Параметр ssl_handshake_timeout.

Добавлено в версии 3.5.3.

Передача файлов

coroutine loop.sendfile(transport, file, offset=0, count=None, *, fallback=True)

Отправить file через transport. Возвращает общее количество отправленных байтов.

В методе используется высокопроизводительный os.sendfile(), если он доступен.

file должен быть обычным файловым объектом, открытым в двоичном режиме.

offset указывает, с чего начать чтение файла. Если указано, count — это общее количество байтов для передачи в отличие от отправки файла до достижения EOF. Положение файла всегда обновляется, даже если данный метод вызывает ошибку, и file.tell() можно использовать для получения фактического количества отправленных байтов.

Установленный на True fallback, заставляет asyncio вручную читать и отправлять файл, когда платформа не поддерживает системный вызов sendfile (например, Windows или сокет SSL в Unix).

Вызывает SendfileNotAvailableError, если система не поддерживает системный вызов sendfile и fallbackFalse.

Добавлено в версии 3.7.

Обновление TLS

coroutine loop.start_tls(transport, protocol, sslcontext, *, server_side=False, server_hostname=None, ssl_handshake_timeout=None)

Обновляет существующее транспортное соединение до TLS.

Возвращает новый экземпляр транспорта, protocol которого должен сразу начать использоваться после ожидания (await). Экземпляр transport, переданный методу start_tls. Повторно никогда не должен использоваться.

Параметры:

  • экземпляры transport и protocol, возвращаемые такими методами, как create_server() и create_connection().
  • sslcontext: настроенный экземпляр SSLContext.
  • server_side передаёт True при обновлении соединения на стороне сервера (например, созданного create_server()).
  • server_hostname: устанавливает или переопределяет имя хоста, с которым будет сопоставляться сертификат целевого сервера.
  • ssl_handshake_timeout — (для TLS-соединения) время в секундах, в течение которого нужно дождаться завершения TLS рукопожатия, прежде чем разорвать соединение. 60.0 секунд, если None (по умолчанию).

Добавлено в версии 3.7.

Наблюдение за файловыми дескрипторами

loop.add_reader(fd, callback, *args)

Запускает мониторинг дескриптора файла fd на предмет доступности для чтения и вызывает callback с указанными аргументами, как только fd станет доступным для чтения.

loop.remove_reader(fd)

Прекращает отслеживание дескриптора файла fd доступности для чтения.

loop.add_writer(fd, callback, *args)

Запускает мониторинг дескриптора файла fd доступности записи и вызывает callback с указанными аргументами, как только fd станет доступным для записи.

Для передачи ключевых аргументов callback используйте functools.partial().

loop.remove_writer(fd)

Прекращает отслеживание дескриптора файла fd доступности записи.

См. также раздел Поддержка платформы для ознакомления с некоторыми ограничениями данных методов.

Непосредственная работа с объектами сокетов

В общем, использующие API на основе транспорта реализации протокола, такие как loop.create_connection() и loop.create_server(), работают быстрее реализаций, работающих с сокетами напрямую. Однако бывают случаи, когда производительность не критична и удобнее работать напрямую с объектами socket.

coroutine loop.sock_recv(sock, nbytes)

Получает до nbytes от sock. Асинхронная версия socket.recv().

Возвращает полученные данные в виде байтового объекта.

sock должен быть неблокирующим сокетом.

Изменено в версии 3.7: Несмотря на то, что метод всегда был документирован как метод корутин, выпуски до Python 3.7 возвращали Future. Начиная с Python 3.7 это метод async def.

coroutine loop.sock_recv_into(sock, buf)

Получает данные из sock в буфер buf. Смоделирована по методу блокировки socket.recv_into().

Возвращает количество байтов, записанных в буфер.

sock должен быть неблокирующим сокетом.

Добавлено в версии 3.7.

coroutine loop.sock_sendall(sock, data)

Отправляет data в сокет sock. Асинхронная версия socket.sendall().

Метод продолжает отправку в сокет до тех пор, пока не будут отправлены все data в данных или пока не возникнет ошибка. None возвращается в случае успеха. При ошибке вызывается исключение. Кроме того, невозможно определить, сколько данных, если таковые имеются, было успешно обработано принимающей стороной соединения.

sock должен быть неблокирующим сокетом.

Изменено в версии 3.7: Несмотря на то, что метод всегда документировался как метод корутины, до Python 3.7 он возвращал Future. Начиная с Python 3.7, это метод async def.

coroutine loop.sock_connect(sock, address)

Подключает sock к удаленному сокету по address.

Асинхронная версия socket.connect().

sock должен быть неблокирующим сокетом.

Изменено в версии 3.5.2: address больше не требует разрешения. sock_connect попытается проверить, разрешен ли уже address, вызвав в socket.inet_pton(). В противном случае для разрешения address будет использоваться loop.getaddrinfo().

coroutine loop.sock_accept(sock)

Принимает соединение. Смоделирована по методу блокировки socket.accept().

Сокет должен быть привязан к адресу и прослушивать соединения. Возвращаемое значение — пара (conn, address), где conn — это новый объект сокета, используемый для отправки и получения данных в соединении, а address — это адрес, привязанный к сокету на другом конце соединения.

sock должен быть неблокирующим сокетом.

Изменено в версии 3.7: Несмотря на то, что метод всегда документировался как метод корутины, до Python 3.7 он возвращал Future. Начиная с Python 3.7, это метод async def.

См.также

loop.create_server() и start_server().

coroutine loop.sock_sendfile(sock, file, offset=0, count=None, *, fallback=True)

Если возможно, отправляет файл высокопроизводительным os.sendfile. Возвращает общее количество отправленных байтов.

Асинхронная версия socket.sendfile().

sock должен быть неблокирующим socket.SOCK_STREAM socket.

file должен быть обычным файловым объектом, открытым в двоичном режиме.

offset указывает, с чего начать чтение файла. Если указано, count — это общее количество байтов для передачи в отличие от отправки файла до достижения EOF. Положение файла всегда обновляется, даже если данный метод вызывает ошибку, и file.tell() можно использовать для получения фактического количества отправленных байтов.

fallback, если задано значение True, заставляет asyncio вручную читать и отправлять файл, когда платформа не поддерживает системный вызов sendfile (например, Windows или сокет SSL в Unix).

Вызывает SendfileNotAvailableError, если система не поддерживает системный вызов sendfile, а fallbackFalse.

sock должен быть неблокирующим сокетом.

Добавлено в версии 3.7.

DNS

coroutine loop.getaddrinfo(host, port, *, family=0, type=0, proto=0, flags=0)

Асинхронная версия socket.getaddrinfo().

coroutine loop.getnameinfo(sockaddr, flags=0)

Асинхронная версия socket.getnameinfo().

Изменено в версии 3.7: Оба метода getaddrinfo и getnameinfo всегда были задокументированы для возврата корутины (сопрограммы), но до Python 3.7 они фактически возвращали объекты asyncio.Future. Начиная с Python 3.7 оба метода являются корутинами.

Работа с конвейерами (pipe)

coroutine loop.connect_read_pipe(protocol_factory, pipe)

Регистрирует конец pipe для чтения в цикле событий.

protocol_factory должен быть вызываемым, возвращающим реализацию asyncio протокола.

pipeфайловый объект.

Возвращает пару (transport, protocol), где transport поддерживает интерфейс ReadTransport, а protocol — это объект, созданный с помощью protocol_factory.

В цикле событий SelectorEventLoop pipe устанавливается в неблокирующий режим.

coroutine loop.connect_write_pipe(protocol_factory, pipe)

Регистрирует конец pipe для записи в цикле событий.

protocol_factory должен быть вызываемым, возвращающим реализацию протокол asyncio.

pipeфайловый объект.

Возвращает пару (transport, protocol), где transport поддерживает интерфейс WriteTransport, а protocol — это объект, созданный с помощью protocol_factory.

В цикле событий SelectorEventLoop pipe устанавливается в неблокирующий режим.

Примечание

SelectorEventLoop не поддерживает вышеуказанные методы в Windows. Вместо этого для Windows использовать ProactorEventLoop.

См.также

Методы loop.subprocess_exec() и loop.subprocess_shell().

Unix сигналы

loop.add_signal_handler(signum, callback, *args)

Устанавливает callback в качестве обработчика сигнала signum.

Обратный вызов будет вызываться циклом вместе с другими обратными вызовами в очереди и запускаемыми корутинами этого цикла событий. В отличие от обработчиков сигналов, зарегистрированных функцией signal.signal(), зарегистрированный данной функцией обратный вызов, может взаимодействовать с циклом событий.

Вызывает ValueError, если номер сигнала недействителен или не обнаруживается. Вызывает RuntimeError, если есть проблема с настройкой обработчика.

Используйте functools.partial() для передачи ключевых аргументов для callback.

Как и signal.signal(), функция должна вызываться в основном потоке.

loop.remove_signal_handler(sig)

Удаляет обработчик сигнала sig.

Возвращает True, если обработчик сигнала был удален, или False, если обработчик не был установлен для данного сигнала.

Доступность: Unix.

См.также

Модуль signal.

Выполнение кода в пулах потоков или процессов

awaitable loop.run_in_executor(executor, func, *args)

Организовывает func функции в указанном исполнителе.

Аргумент executor должен быть экземпляром concurrent.futures.Executor. Если исполнитель — None, то по умолчанию используется executor.

Пример:

import asyncio
import concurrent.futures

def blocking_io():
    # Файловые операции (такие как журналирование) могут блокировать
    # событийный цикл: запустив их в пуле потоков.
    with open('/dev/urandom', 'rb') as f:
        return f.read(100)

def cpu_bound():
    # Связанные с процессором операции блокируют событийного цикла обработки:
    # в общем, лучше запускать их в
    # пуле процессов.
    return sum(i * i for i in range(10 ** 7))

async def main():
    loop = asyncio.get_running_loop()

    ## Опции:

    # 1. Запуск в исполнителе цикла по умолчанию:
    result = await loop.run_in_executor(
        None, blocking_io)
    print('default thread pool', result)

    # 2. Запуск в пользовательском пуле потоков:
    with concurrent.futures.ThreadPoolExecutor() as pool:
        result = await loop.run_in_executor(
            pool, blocking_io)
        print('custom thread pool', result)

    # 3. Запуск в пользовательском пуле процессов:
    with concurrent.futures.ProcessPoolExecutor() as pool:
        result = await loop.run_in_executor(
            pool, cpu_bound)
        print('custom process pool', result)

asyncio.run(main())

Метод возвращает объект asyncio.Future.

Используйте functools.partial() для передачи ключевых аргументов для func.

Изменено в версии 3.5.3: loop.run_in_executor() больше не настраивает max_workers созданного им исполнителя пула потоков, вместо этого оставляя его исполнителю пула потоков (ThreadPoolExecutor) для установки значения по умолчанию.

loop.set_default_executor(executor)

Устанавливает executor в качестве исполнителя по умолчанию, используемого run_in_executor(). executor должен быть экземпляром ThreadPoolExecutor.

Не рекомендуется, начиная с версии 3.8: Использование исполнителя, не являющегося экземпляром ThreadPoolExecutor, устарело и вызовет ошибку в Python 3.9.

executor должен быть экземпляром concurrent.futures.ThreadPoolExecutor.

API обработки ошибок

Позволяет настроить обработку исключений в цикле событий.

loop.set_exception_handler(handler)

Устанавливает handler как новый обработчик исключений цикла событий.

Если handlerNone, будет установлен обработчик исключений по умолчанию. В противном случае handler должен быть вызываемым с сигнатурой, соответствующей (loop, context), где loop — это ссылка на активный цикл событий, а context — это объект dict, содержащий сведения об исключении (подробные сведения о контексте см. в документации call_exception_handler()).

loop.get_exception_handler()

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

Добавлено в версии 3.5.2.

loop.default_exception_handler(context)

Обработчик исключений по умолчанию.

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

context параметр имеет то же значение, что и в call_exception_handler().

loop.call_exception_handler(context)

Вызывает текущий обработчик исключения цикла событий.

context — это объект dict, содержащий следующие ключи (новые ключи могут быть введены в будущих версиях Python):

  • «message»: сообщение об ошибке;
  • «exception» (необязательно): объект исключения;
  • «future» (необязательно): экземпляр asyncio.Future;
  • «handle» (необязательно): экземпляр asyncio.Handle;
  • «protocol» (необязательно): экземпляр Протокола;
  • «transport» (необязательно): экземпляр Транспорта;
  • «socket» (необязательно): экземпляр socket.socket.

Примечание

Метод не следует перегружать в циклах событий подкласса. Для настраиваемой обработки исключений использовать метод set_exception_handler().

Включение режима отладки

loop.get_debug()

Получает режим отладки (bool) цикла событий.

Значение по умолчанию — True, если для переменной среды PYTHONASYNCIODEBUG задана непустая строка, в противном случае — False.

loop.set_debug(enabled: bool)

Устанавливает режим отладки цикла событий.

Изменено в версии 3.7: Новый параметр командной строки -X dev теперь также можно использовать для включения режима отладки.

Запущенные подпроцессы

Рассмотренные в этом подразделе методы, относятся к низкоуровневым. В обычном коде async/await рассмотрите возможность использования вместо них вспомогательных функций высокого уровня asyncio.create_subprocess_shell() и asyncio.create_subprocess_exec().

Примечание

Цикл событий asyncio в Windows по умолчанию не поддерживает подпроцессы. Подробнее см. Поддержка подпроцессов в Windows.

coroutine loop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

Создает подпроцесс из одного или нескольких строковых аргументов, указанных в args.

args должен быть списком строк, представленных:

Первая строка указывает исполняемый файл программы, а остальные строки указывают аргументы. Вместе строковые аргументы образуют argv программы.

Это похоже на класс стандартной библиотеки subprocess.Popen, вызываемый с shell=False и списком строк, переданных в качестве первого аргумента; однако, если Popen принимает единственный аргумент, который является списком строк, subprocess_exec принимает несколько строковых аргументов.

protocol_factory должен быть вызываемым, возвращающим подкласс класса asyncio.SubprocessProtocol.

Прочие параметры:

  • stdin может быть любым из них:

    • файловый объект, представляющий канал, который должен быть подключён к стандартному входному потоку подпроцесса с использованием connect_write_pipe()
    • константа subprocess.PIPE (по умолчанию), которая создаст новый конвейер и соединяет его
    • значение None, которое заставит подпроцесс наследовать файловый дескриптор этого процесса
    • константа subprocess.DEVNULL, которая указывает, что будет использоваться специальный файл os.devnull
  • stdout может быть любым из них:

    • файловый объект, представляющий конвейер, который должен быть подключён к стандартному потоку вывода подпроцесса с использованием connect_write_pipe()
    • константа subprocess.PIPE (по умолчанию), которая создаст новый конвейер и соединяет его
    • значение None, которое заставит подпроцесс наследовать дескриптор файла от этого процесса
    • константа subprocess.DEVNULL, указывающая, что будет использоваться специальный файл os.devnull
  • stderr может быть любым из них:

    • файловый объект, представляющий канал, который должен быть подключён к стандартному потоку ошибок подпроцесса с использованием connect_write_pipe()
    • константа subprocess.PIPE (по умолчанию), которая создаст новый конвейер и соединяет его
    • значение None, которое заставит подпроцесс наследовать файловый дескриптор этого процесса
    • константа subprocess.DEVNULL, которая указывает, что будет использоваться специальный файл os.devnull
    • константа subprocess.STDOUT, которая соединит стандартный поток ошибок со стандартным потоком вывода процесса
  • Все остальные ключевые аргументы передаются в subprocess.Popen без интерпретации, за исключением bufsize, universal_newlines, shell, text, encoding и errors которые вообще не должны указываться.

    API подпроцесса asyncio не поддерживает декодирование потоков как текста. bytes.decode() можно использовать для преобразования байтов, возвращаемых из потока, в текст.

См. конструктор класса subprocess.Popen для документации по другим аргументам.

Возвращает пару (transport, protocol), где transport соответствует базовому классу asyncio.SubprocessTransport, а protocol — объект, экземпляр которого был создан protocol_factory.

coroutine loop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

Создает подпроцесс из cmd, который может быть строкой str или bytes, закодированной в кодировке файловой системы, используя синтаксис «оболочки» платформы.

Это похоже на класс стандартной библиотеки subprocess.Popen, который называется shell=True.

protocol_factory должен быть вызываемым, возвращающим подкласс класса SubprocessProtocol.

См. subprocess_exec() для получения дополнительных сведений об оставшихся аргументах.

Возвращает пару (transport, protocol), где transport соответствует базовому классу SubprocessTransport, а protocol — это объект, созданный protocol_factory.

Примечание

Приложение несёт ответственность за то, чтобы все пробелы и специальные символы были указаны в кавычках, чтобы избежать уязвимостей инъекция оболочки. Функцию shlex.quote() можно использовать для правильного экранирования пробелов и специальных символов в строках, которые будут использоваться для создания команд оболочки.

Дескрипторы обратного вызова

class asyncio.Handle

Объект-оболочка обратного вызова, возвращаемый loop.call_soon(), loop.call_soon_threadsafe().

cancel()

Отменяет обратный вызов. Если обратный вызов уже был отменён или выполнен, метод не действует.

cancelled()

Возвращает True, если обратный вызов был отменен.

Добавлено в версии 3.7.

class asyncio.TimerHandle

Объект-оболочка обратного вызова, возвращаемый loop.call_later() и loop.call_at().

Класс наследуется от Handle.

when()

Возвращает запланированное время обратного вызова как float секунд.

Время — это абсолютная временная метка с той же привязкой ко времени, что и loop.time().

Добавлено в версии 3.7.

Server объекты

Server объекты создаются функциями loop.create_server(), loop.create_unix_server(), start_server() и start_unix_server().

Не создавайте экземпляр класса напрямую.

class asyncio.Server

Объекты Server — асинхронные менеджеры контекста. При использовании в операторе async with гарантируется, что объект Server закрыт и не принимает новые соединения после завершения оператора async with:

srv = await loop.create_server(...)

async with srv:
    # некоторый код

# В данный момент srv закрыт и больше не принимает новые подключения.

Изменено в версии 3.7: Объект Server — это асинхронный менеджер контекста, начиная с Python 3.7.

close()

Прекращение обслуживания: закрыть прослушивающие сокеты и установить для атрибута sockets значение None.

Сокеты, которые представляют существующие входящие клиентские соединения, остаются открытыми.

Сервер закрывается асинхронно, использовать корутину wait_closed(), чтобы дождаться закрытия сервера.

get_loop()

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

Добавлено в версии 3.7.

coroutine start_serving()

Начинает прием подключения.

Метод идемпотентен, поэтому его можно вызывать, когда сервер уже обслуживает.

start_serving только для ключевого параметра для loop.create_server() и asyncio.start_server() позволяет создать объект Server, который изначально не принимает соединения. В этом случае можно использовать Server.start_serving() или Server.serve_forever(), чтобы сервер начал принимать соединения.

Добавлено в версии 3.7.

coroutine serve_forever()

Начинает прием соединения, пока корутина не будет отменена. Отмена задачи serve_forever приводит к закрытию сервера.

Метод можно вызвать, если сервер уже принимает соединения. На один объект Server может существовать только одна задача serve_forever.

Пример:

async def client_connected(reader, writer):
    # Взаимодействие клиента с потоками
    # читения/записи. Например:
    await reader.readline()

async def main(host, port):
    srv = await asyncio.start_server(
        client_connected, host, port)
    await srv.serve_forever()

asyncio.run(main('127.0.0.1', 0))

Добавлено в версии 3.7.

is_serving()

Возвращает True, если сервер принимает новые соединения.

Добавлено в версии 3.7.

coroutine wait_closed()

Дожидается завершения метода close().

sockets

Список объектов socket.socket, которые прослушивает сервер.

Изменено в версии 3.7: До Python 3.7 Server.sockets использовался для непосредственного возврата внутреннего списка серверных сокетов. В 3.7 возвращается копия этого списка.

Реализации цикла событий

Asyncio поставляется с двумя разными реализациями цикла событий: SelectorEventLoop и ProactorEventLoop.

По умолчанию asyncio настроен на использование SelectorEventLoop в Unix и ProactorEventLoop в Windows.

class asyncio.SelectorEventLoop

Цикл событий на основе модуля selectors.

Использует наиболее эффективный селектор, доступный для данной платформы. Также можно вручную настроить точную используемую реализацию селектора:

import asyncio
import selectors

selector = selectors.SelectSelector()
loop = asyncio.SelectorEventLoop(selector)
asyncio.set_event_loop(loop)

Доступность: Unix, Windows.

class asyncio.ProactorEventLoop

Цикл событий для Windows, использующий «порты завершения ввода-вывода» (IOCP).

Доступность: Windows.

class asyncio.AbstractEventLoop

Абстрактный базовый класс для асинхронных циклов событий.

В разделе Методы цикла событий перечислены все методы, которые должна была определять альтернативная реализация AbstractEventLoop.

Примеры

Обратите внимание, что все примеры в этом разделе целенаправленно рассказывают, как использовать API-интерфейсы низкоуровневого цикла событий, такие как loop.run_forever() и loop.call_soon(). Современные приложения asyncio редко нужно писать таким образом; рассмотрите возможность использования функций высокого уровня, таких как asyncio.run().

«Привет, мир» с call_soon()

Пример использования метода loop.call_soon() для планирования обратного вызова. Обратный вызов отображает "Hello World", а затем останавливает цикл событий:

import asyncio

def hello_world(loop):
    """Коллбэк печатает 'Hello World' и останавливает событийный цикл"""
    print('Hello World')
    loop.stop()

loop = asyncio.get_event_loop()

# Запланировать вызов hello_world()
loop.call_soon(hello_world, loop)

# Блокирующий вызов прерываемый loop.stop()
try:
    loop.run_forever()
finally:
    loop.close()

См.также

Аналогичный пример Hello World, созданный корутиной и функцией run().

Показать текущую дату вызвав call_later()

Пример обратного вызова, отображающего текущую дату каждую секунду. Обратный вызов использует метод loop.call_later() для перепланирования себя через 5 секунд, а затем останавливает цикл событий:

import asyncio
import datetime

def display_date(end_time, loop):
    print(datetime.datetime.now())
    if (loop.time() + 1.0) < end_time:
        loop.call_later(1, display_date, end_time, loop)
    else:
        loop.stop()

loop = asyncio.get_event_loop()

# Запланировать первый вызов display_date()
end_time = loop.time() + 5.0
loop.call_soon(display_date, end_time, loop)

# Блокировка вызова прервана loop.stop()
try:
    loop.run_forever()
finally:
    loop.close()

См.также

Аналогичный пример текущей даты, созданный с помощью корутины и функции run().

Мониторинг событий чтения файловых дескрипторов

Ждет, пока файловый дескриптор получит некоторые данные от метода loop.add_reader(), а затем закрывает цикл обработки событий:

import asyncio
from socket import socketpair

# Создаёт пару связанных файловых дескрипторов
rsock, wsock = socketpair()

loop = asyncio.get_event_loop()

def reader():
    data = rsock.recv(100)
    print("Received:", data.decode())

    # Мы закончили: отменить регистрацию файлового дескриптора
    loop.remove_reader(rsock)

    # Отстановить событийный цикл
    loop.stop()

# Зарегистрировать дескриптор файла для события чтения
loop.add_reader(rsock, reader)

# Имитировать приём данных из сети
loop.call_soon(wsock.send, 'abc'.encode())

try:
    # Запуск событийного цикла
    loop.run_forever()
finally:
    # Завершаем работу. Закрыть сокеты и цикл обработки событий.
    rsock.close()
    wsock.close()
    loop.close()

См.также

Установка обработчиков сигналов SIGINT и SIGTERM

(Пример signals работает только в Unix.)

Регистрация обработчиков сигналов SIGINT и SIGTERM с помощью метода loop.add_signal_handler():

import asyncio
import functools
import os
import signal

def ask_exit(signame, loop):
    print("получил сигнал %s: exit" % signame)
    loop.stop()

async def main():
    loop = asyncio.get_running_loop()

    for signame in {'SIGINT', 'SIGTERM'}:
        loop.add_signal_handler(
            getattr(signal, signame),
            functools.partial(ask_exit, signame, loop))

    await asyncio.sleep(3600)

print("Цикл событий работает в течение 1 часа, нажмите Ctrl+C, чтобы прервать его.")
print(f"pid {os.getpid()}: отправьте SIGINT или SIGTERM для выхода.")

asyncio.run(main())