Подпроцессы

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


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

Вот пример того, как asyncio может выполнить команду shell и получить ее результат:

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())

См. также подраздел Examples.

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

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

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

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

Возвращает экземпляр Process.

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

Изменено в версии 3.10: Удален параметр loop.

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

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

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

Возвращает экземпляр Process.

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

Важно

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

Изменено в версии 3.10: Удален параметр loop.

Примечание

Подпроцессы доступны для Windows, если используется ProactorEventLoop. Подробности см. в разделе Subprocess Support on Windows.

См.также

asyncio также имеет следующие низкоуровневые API для работы с подпроцессами: loop.subprocess_exec(), loop.subprocess_shell(), loop.connect_read_pipe(), loop.connect_write_pipe(), а также Subprocess Transports и Subprocess Protocols.

Константы

asyncio.subprocess.PIPE

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

Если в аргумент stdin передан PIPE, атрибут 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 не поддерживается.

Этот класс является not thread safe.

См. также раздел Subprocess and Threads.

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).

Если при записи ввода в stdin возникает исключение BrokenPipeError или ConnectionResetError, оно игнорируется. Это состояние возникает, когда процесс завершается до того, как все данные будут записаны в 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() или await process.stderr.read(). Это позволяет избежать тупиковых ситуаций из-за того, что потоки приостанавливают чтение или запись и блокируют дочерний процесс.

pid

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

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

returncode

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

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

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

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

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

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

В UNIX child watchers используются для ожидания завершения подпроцесса, более подробную информацию смотрите в Наблюдатели за процессом.

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

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

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

См.также

Раздел Concurrency and multithreading in asyncio.

Примеры

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

Подпроцесс создается функцией create_subprocess_exec():

import asyncio
import sys

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

    # Create the subprocess; redirect the standard output
    # into a pipe.
    proc = await asyncio.create_subprocess_exec(
        sys.executable, '-c', code,
        stdout=asyncio.subprocess.PIPE)

    # Read one line of output.
    data = await proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Wait for the subprocess exit.
    await proc.wait()
    return line

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

См. также same example, написанные с использованием низкоуровневых API.

Вернуться на верх