Потоки

Исходный код: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py


В этом разделе описываются async/await asyncio API высокого уровня для создания подпроцессов и управления ими.

Пример того, как asyncio может запустить команду оболочки и получить ее результат:

import asyncio

async def run(cmd):
    proc = await asyncio.create_subprocess_shell(
        cmd,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE)

    stdout, stderr = await proc.communicate()

    print(f'[{cmd!r} exited with {proc.returncode}]')
    if stdout:
        print(f'[stdout]\n{stdout.decode()}')
    if stderr:
        print(f'[stderr]\n{stderr.decode()}')

asyncio.run(run('ls /zzz'))

Напечатает:

['ls /zzz' exited with 1]
[stderr]
ls: /zzz: No such file or directory

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

async def main():
    await asyncio.gather(
        run('ls /zzz'),
        run('sleep 1; echo "hello"'))

asyncio.run(main())

См. также подраздел Примеры.

Создание подпроцессов

coroutine asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)

Создать подпроцессы.

Аргумент limit устанавливает предел буфера для StreamReader оболочек для Process.stdout и Process.stderr (если subprocess.PIPE передается аргументам stdout и stderr).

Возвращает Process сущность.

Другие параметры см. в документации loop.subprocess_exec().

Deprecated since version 3.8, will be removed in version 3.10: Параметр loop.

coroutine asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)

Выполните команду cmd оболочки.

Аргумент limit устанавливает предел буфера для StreamReader оболочек для Process.stdout и Process.stderr (если subprocess.PIPE передается аргументам stdout и stderr).

Возвращает Process сущность.

Другие параметры см. в документации loop.subprocess_shell().

Важно

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

Deprecated since version 3.8, will be removed in version 3.10: Параметр loop.

Примечание

Подпроцессы доступны для Windows, если используется ProactorEventLoop. Подробности см. В поддержке подпроцессов в Windows.

См.также

У asyncio также есть следующие API низкоуровневое, чтобы работать с подпроцессами: loop.subprocess_exec(), loop.subprocess_shell(), loop.connect_read_pipe(), loop.connect_write_pipe(), а также Транспорты подпроцессов и Протоколы подпроцессов.

Константы

asyncio.subprocess.PIPE

Может передаваться в параметры stdin, stdout или stderr.

Если PIPE передается в stdin аргумент, атрибут Process.stdin указывает на StreamWriter сущность.

Если PIPE передается stdout или stderr аргументам атрибуты Process.stdout и Process.stderr указывают на StreamReader сущности.

asyncio.subprocess.STDOUT

Специальное значение, которое можно используемый в качестве аргумента stderr и указывает, что стандартная ошибка должна быть перенаправлена в стандартный вывод.

asyncio.subprocess.DEVNULL

Специальное значение, которое можно используемый в качестве аргумента stdin, stdout или stderr для функций создания процесса. Это означает, что специальный файловый os.devnull будет используемый для соответствующего потока подпроцессы.

Взаимодействие с подпроцессами

И create_subprocess_exec() и create_subprocess_shell() функционируют возвращает сущности Process класс. Process представляет собой обертку высокого уровня, которая позволяет общаться с подпроцессами и наблюдать за их завершением.

class asyncio.subprocess.Process

Объект, охватывающий процессы ОС, созданные функциями create_subprocess_exec() и create_subprocess_shell().

Этот класс разработан так, чтобы иметь сходный API с subprocess.Popen класс, но есть некоторые заметные отличия:

  • В отличие от Popen, Process сущности не имеют аналога poll() метод;
  • communicate() и wait() методы не имеют параметра timeout: используйте функцию wait_for();
  • Process.wait() метод является асинхронным, тогда как subprocess.Popen.wait() метод реализуется как блокирующий цикл занятости;
  • Параметр universal_newlines не поддерживается.

Класс не потокобезопасно.

См. также раздел Подпроцессы и потоки.

coroutine wait()

Дождитесь завершения дочернего процесса.

Установить возвращает атрибут returncode.

Примечание

Этот метод может взаимоблокироваться при использовании stdout=PIPE или stderr=PIPE, и дочерний процесс генерирует столько выходных данных, что блокирует ожидание, когда буфер пайп ОС примет больше данных. Используйте communicate() метод при использовании пайпы, чтобы избежать этого условия.

coroutine communicate(input=None)

Взаимодействие с процессами:

  1. Послать данные в stdin (если input не является None);
  2. Считывание данных из stdout и stderr до тех пор, пока не будет достигнуто EOF;
  3. Дождитесь завершения процесса.

Необязательный аргумент input - это данные (объект bytes), которые будут отправлены нижестоящему процессу.

Возвращает кортеж (stdout_data, stderr_data).

Если или BrokenPipeError или исключение ConnectionResetError подняты, сочиняя input в stdin, исключение проигнорировано. Это условие возникает при завершении процесса перед записью всех данных в stdin.

Если требуется отправить данные в stdin процесса, процесс необходимо создать с помощью stdin=PIPE. Аналогично, чтобы получить что-либо, кроме None в кортеже результатов, процесс должен быть создан с stdout=PIPE и/или stderr=PIPE аргументами.

Следует отметить, что считанные данные буферизуются в памяти, поэтому не используйте эту метод, если размер данных большой или неограниченный.

send_signal(signal)

Передача сигнала, signal дочернему процессу.

Примечание

В Windows SIGTERM является алиас для terminate(). CTRL_C_EVENT и CTRL_BREAK_EVENT могут быть отправлены в процессы, запущенные с параметром creationflags, который включает в себя CREATE_NEW_PROCESS_GROUP.

terminate()

Остановите дочерний процесс.

В системах POSIX этот метод отправляет signal.SIGTERM дочернему процессу.

В Windows Win32 API TerminateProcess() вызывается для остановки дочернего процесса.

kill()

Убить дочерний процесс.

В системах POSIX этот метод отправляет SIGKILL дочернему процессу.

В Windows этот метод является алиас для terminate().

stdin

Стандартный входной поток (StreamWriter) или None, был ли процесс создан с помощью stdin=None.

stdout

Стандартный выходной поток (StreamReader) или None, был ли процесс создан с помощью stdout=None.

stderr

Стандартный поток ошибок (StreamReader) или None, был ли процесс создан с помощью stderr=None.

Предупреждение

Используйте communicate() метод, а не process.stdin.write(), await process.stdout.read() or await process.stderr.read. Это позволяет избежать взаимоблокировок из-за того, что потоки приостанавливают чтение или запись и блокируют дочерний процесс.

pid

Идентификационный номер процесса (PID).

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

returncode

Возвращает код процесса при его завершении.

Значение None указывает, что процесс еще не завершен.

Отрицательное значение -N указывающее на то, что дочерний элемент был завершен N сигнала (только POSIX).

Подпроцессы и потоки

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

В Windows подпроцессы предоставляются только ProactorEventLoop (по умолчанию), SelectorEventLoop не имеет поддержки подпроцессов.

В UNIX вотчеры за дочерними процессами используемый ожидания завершения подпроцессы см. Вотчеры процесса для получения дополнительной информации.

Изменено в версии 3.8: UNIX переключилась на использование ThreadedChildWatcher для создания подпроцессов из разных потоков без каких-либо ограничений.

Порождение подпроцесса с помощью неактивного текущего дочернего наблюдателя вызывает RuntimeError.

Обратите внимание, что альтернативные реализации событийный цикл могут иметь собственные ограничения; см. документацию.

Примеры

Пример, используя Process класс, чтобы управлять подпроцессы и StreamReader класс, чтобы читать от его стандартного вывода.

Создание подпроцессы осуществляется с помощью функции create_subprocess_exec():

import asyncio
import sys

async def get_date():
    code = 'import datetime; print(datetime.datetime.now())'

    # Создание подпроцесса и перенаправление стандартный вывод
    # в пайп.
    proc = await asyncio.create_subprocess_exec(
        sys.executable, '-c', code,
        stdout=asyncio.subprocess.PIPE)

    # Прочитайте одну строку вывода.
    data = await proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Дождитесь завершения подпроцесса.
    await proc.wait()
    return line

date = asyncio.run(get_date())
print(f"Current date: {date}")

См. также пример, написанные с помощью низкоуровневое API.