Документация Python неофициальный перевод
Содержание страницы

ПодпроцессыSubprocesses

Исходный код: 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())

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

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

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() для других параметров.

Важно

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

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

Примечание

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

См. также

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

КонстантыConstants

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.

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

Функции 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 не поддерживается.

This class is не является потокобезопасным.

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

coroutine wait()

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

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

Примечание

Этот метод может привести к взаимоблокировке при использовании stdout=PIPE или stderr=PIPE, если дочерний процесс генерирует настолько много вывода, что он блокируется в ожидании, пока буфер системного канала (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() или await process.stderr.read(). Это позволяет избежать взаимоблокировок, возникающих из-за приостановки чтения или записи потоков и блокировки дочернего процесса.

pid

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

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

returncode

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

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

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

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

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

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

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

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

Запуск подпроцесса с неактивным текущим наблюдателем дочерних процессов вызывает RuntimeError.

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

ПримерыExamples

Пример использования класса 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.