Содержание страницы
18.5.6. Подпроцесс¶Subprocess
Исходный код: Lib/asyncio/subprocess.py
18.5.6.1. Цикл событий Windows¶Windows 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.Popen¶Create 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()позволяет правильно экранировать пробельные символы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.Этот метод – корутина.
См. также
Методы AbstractEventLoop.connect_read_pipe() и
AbstractEventLoop.connect_write_pipe().
18.5.6.4. Константы¶Constants
-
asyncio.subprocess.PIPE¶ Специальное значение, которое можно использовать в качестве аргумента stdin, stdout или stderr\nдля
create_subprocess_shell()иcreate_subprocess_exec(), и\nуказывающее, что должен быть открыт канал к стандартному потоку.
-
asyncio.subprocess.STDOUT¶ Специальное значение, которое можно использовать в качестве аргумента stderr для\n
create_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дочернему процессу. В Windowskill()является псевдонимом для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()