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

18.5.6. ПодпроцессSubprocess

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

18.5.6.1. Цикл событий WindowsWindows event loop

В Windows цикл событий по умолчанию – SelectorEventLoop, который не поддерживает подпроцессы. Вместо него следует использовать ProactorEventLoop. Пример использования в Windows:

import asyncio, sys

if sys.platform == 'win32':
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)

18.5.6.2. Создание подпроцесса: высокоуровневый API с использованием процессаCreate a subprocess: high-level API using Process

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

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

Параметр limit устанавливает лимит буфера, передаваемый в StreamReader. Дополнительные параметры см. в AbstractEventLoop.subprocess_exec().

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

Эта функция – корутина.

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

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

Параметр limit устанавливает лимит буфера, передаваемый в StreamReader. Дополнительные параметры см. в AbstractEventLoop.subprocess_shell().

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

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

Эта функция – корутина.

Используйте методы AbstractEventLoop.connect_read_pipe() и AbstractEventLoop.connect_write_pipe() для подключения каналов.

18.5.6.3. Создание подпроцесса: низкоуровневый API с использованием subprocess.PopenCreate a subprocess: low-level API using subprocess.Popen

Асинхронный запуск подпроцессов с помощью модуля subprocess.

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

Создание подпроцесса из одного или нескольких строковых аргументов (строки символов или байтовые строки, закодированные в кодировку файловой системы), где первая строка определяет выполняемую программу, а остальные строки – её аргументы. (Таким образом, вместе строковые аргументы образуют значение sys.argv программы, если предположить, что это скрипт Python.) Это похоже на класс subprocess.Popen из стандартной библиотеки, вызываемый с shell=False и списком строк, передаваемым в качестве первого аргумента; однако Popen принимает один аргумент – список строк, а subprocess_exec() принимает несколько строковых аргументов.

Параметр protocol_factory должен создавать экземпляр подкласса класса asyncio.SubprocessProtocol.

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

  • stdin: либо файлоподобный объект, представляющий канал, который будет подключён к стандартному потоку ввода подпроцесса с помощью connect_write_pipe(), либо константа subprocess.PIPE (по умолчанию). По умолчанию создаётся и подключается новый канал.

  • stdout: либо файлоподобный объект, представляющий канал, который будет подключён к стандартному потоку вывода подпроцесса с помощью connect_read_pipe(), либо константа subprocess.PIPE (по умолчанию). По умолчанию создаётся и подключается новый канал.

  • stderr: либо файлоподобный объект, представляющий канал, который будет подключён к стандартному потоку ошибок подпроцесса с помощью connect_read_pipe(), либо одна из констант subprocess.PIPE (по умолчанию) или subprocess.STDOUT. По умолчанию создаётся и подключается новый канал. Если указано subprocess.STDOUT, стандартный поток ошибок подпроцесса будет подключён к тому же каналу, что и стандартный поток вывода.

  • Все остальные именованные аргументы передаются в subprocess.Popen без интерпретации, за исключением bufsize, universal_newlines и shell, которые не следует указывать вовсе.

Возвращает пару (transport, protocol), где транспорт – экземпляр BaseSubprocessTransport.

Этот метод – корутина.

Параметры см. в конструкторе класса subprocess.Popen.

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

Создание подпроцесса из cmd – строки символов или байтовой строки, закодированной в кодировку файловой системы, с использованием синтаксиса оболочки платформы. Это похоже на класс subprocess.Popen из стандартной библиотеки, вызываемый с shell=True.

Параметр protocol_factory должен создавать экземпляр подкласса класса asyncio.SubprocessProtocol.

Подробнее об остальных аргументах см. в subprocess_exec().

Возвращает пару (transport, protocol), где транспорт – экземпляр BaseSubprocessTransport.

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

Этот метод – корутина.

18.5.6.4. КонстантыConstants

asyncio.subprocess.PIPE

Специальное значение, которое можно использовать в качестве аргумента stdin, stdout или stderr\nдля create_subprocess_shell() и create_subprocess_exec(), и\nуказывающее, что должен быть открыт канал к стандартному потоку.

asyncio.subprocess.STDOUT

Специальное значение, которое можно использовать в качестве аргумента stderr для\ncreate_subprocess_shell() и create_subprocess_exec(), и\nуказывающее, что стандартный поток ошибок должен направляться в тот же дескриптор, что и стандартный вывод.

asyncio.subprocess.DEVNULL

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

18.5.6.5. ПроцессProcess

class asyncio.subprocess.Process

Подпроцесс, созданный функцией create_subprocess_exec() или\ncreate_subprocess_shell().

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

  • Нет явного метода poll()

  • Методы communicate() и\nwait() не принимают параметр timeout:\nиспользуйте функцию wait_for()

  • Параметр universal_newlines не поддерживается (поддерживаются только строки байтов)

  • Метод wait() класса\nProcess является асинхронным, тогда как\nметод wait() класса Popen\nреализован как активный цикл ожидания.

Этот класс не потокобезопасен. См. также раздел\nПодпроцессы и потоки.

coroutine wait()

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

Этот метод является корутиной.

Примечание

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

coroutine communicate(input=None)

Взаимодействие с процессом: отправить данные в стандартный ввод. Читать данные из стандартного вывода и стандартного вывода ошибок до достижения конца файла. Ожидать завершения процесса. Необязательный аргумент input должен содержать данные для отправки дочернему процессу, или None, если данные не нужно отправлять. Тип input должен быть байтовым.

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

Если исключение BrokenPipeError или ConnectionResetError возникает\nпри записи input в stdin, это исключение игнорируется. Оно\nвозникает, когда процесс завершается до того, как все данные будут записаны в stdin.

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

Этот метод является корутиной.

Примечание

Прочитанные данные буферизируются в памяти, поэтому не следует использовать этот метод, если\nразмер данных велик или неограничен.

Изменено в версии 3.4.2: Метод теперь игнорирует BrokenPipeError и\nConnectionResetError.

send_signal(signal)

Отправляет сигнал signal дочернему процессу.

Примечание

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

terminate()

Остановить дочерний процесс. В POSIX-системах метод отправляет signal.SIGTERM\nдочернему процессу. В Windows для остановки дочернего процесса вызывается функция Win32 API\nTerminateProcess().

kill()

Убить дочерний процесс. В POSIX-системах функция отправляет SIGKILL\nдочернему процессу. В Windows kill() является псевдонимом для terminate().

stdin

Поток стандартного ввода (StreamWriter), None если процесс\nбыл создан с stdin=None.

stdout

Поток стандартного вывода (StreamReader), None если процесс\nбыл создан с stdout=None.

stderr

Поток стандартного вывода ошибок (StreamReader), None если процесс\nбыл создан с stderr=None.

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

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

pid

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

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

returncode

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

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

18.5.6.6. Подпроцессы и потокиSubprocess and threads

asyncio поддерживает запуск подпроцессов из разных потоков, но есть ограничения:

  • Цикл событий должен выполняться в главном потоке.

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

Класс asyncio.subprocess.Process не является потокобезопасным.

18.5.6.7. Примеры подпроцессовSubprocess examples

18.5.6.7.1. Подпроцесс с использованием транспорта и протоколаSubprocess using transport and protocol

Пример протокола подпроцесса, используемого для получения вывода подпроцесса и ожидания его завершения. Подпроцесс создаётся методом AbstractEventLoop.subprocess_exec():

import asyncio
import sys

class DateProtocol(asyncio.SubprocessProtocol):
    def __init__(self, exit_future):
        self.exit_future = exit_future
        self.output = bytearray()

    def pipe_data_received(self, fd, data):
        self.output.extend(data)

    def process_exited(self):
        self.exit_future.set_result(True)

@asyncio.coroutine
def get_date(loop):
    code = 'import datetime; print(datetime.datetime.now())'
    exit_future = asyncio.Future(loop=loop)

    # Создать подпроцесс, управляемый протоколом DateProtocol,
    # перенаправить стандартный вывод в канал
    create = loop.subprocess_exec(lambda: DateProtocol(exit_future),
                                  sys.executable, '-c', code,
                                  stdin=None, stderr=None)
    transport, protocol = yield from create

    # Ожидать завершения подпроцесса с помощью метода process_exited()
    # протокола
    yield from exit_future

    # Закрыть канал stdout
    transport.close()

    # Прочитать вывод, собранный методом pipe_data_received()
    # протокола
    data = bytes(protocol.output)
    return data.decode('ascii').rstrip()

if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

date = loop.run_until_complete(get_date(loop))
print("Current date: %s" % date)
loop.close()

18.5.6.7.2. Подпроцесс с использованием потоков данныхSubprocess using streams

Пример использования класса Process для управления подпроцессом и класса StreamReader для чтения из стандартного вывода. Подпроцесс создаётся функцией create_subprocess_exec():

import asyncio.subprocess
import sys

@asyncio.coroutine
def get_date():
    code = 'import datetime; print(datetime.datetime.now())'

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

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

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

if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

date = loop.run_until_complete(get_date())
print("Current date: %s" % date)
loop.close()