Подпроцессы
1. Цикл событий Windows
В Windows по умолчанию используется цикл событий SelectorEventLoop, который не поддерживает подпроцессы. Вместо него следует использовать ProactorEventLoop. Пример использования в Windows:
import asyncio, sys
if sys.platform == 'win32':
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
См. также
2. Создание подпроцесса: API высокого уровня с использованием 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.Приложению необходимо убедиться, что все пробелы и метасимволы корректно заключены в кавычки, чтобы избежать уязвимостей shell injection. Функция
shlex.quote()может использоваться для правильного экранирования пробелов и метасимволов оболочки в строках, используемых для построения команд оболочки.Эта функция является корутинной.
Для подключения каналов используйте методы AbstractEventLoop.connect_read_pipe() и AbstractEventLoop.connect_write_pipe().
3. Создание подпроцесса: API низкого уровня с использованием 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), где transport – экземплярBaseSubprocessTransport.Этот метод является корутинной.
См. конструктор класса
subprocess.Popenдля параметров. -
stdin: либо объект-подобный файлу, представляющий канал, подключаемый к стандартному вводу подпроцесса с использованием
-
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), где transport – экземплярBaseSubprocessTransport.Приложению необходимо убедиться, что все пробелы и метасимволы корректно заключены в кавычки, чтобы избежать уязвимостей shell injection. Функция
shlex.quote()может использоваться для правильного экранирования пробелов и метасимволов оболочки в строках, используемых для построения команд оболочки.Этот метод является корутинной.
См. также
Методы AbstractEventLoop.connect_read_pipe() и AbstractEventLoop.connect_write_pipe().
4. Константы
-
asyncio.subprocess.PIPE -
Специальное значение, которое может использоваться в качестве аргументов stdin, stdout или stderr для
create_subprocess_shell()иcreate_subprocess_exec()и указывает на то, что должен быть открыт канал к стандартному потоку.
-
asyncio.subprocess.STDOUT -
Специальное значение, которое может использоваться в качестве аргумента stderr для
create_subprocess_shell()иcreate_subprocess_exec()и указывает, что стандартная ошибка должна отправляться в тот же поток, что и стандартный вывод.
-
asyncio.subprocess.DEVNULL -
Специальное значение, которое можно использовать в качестве аргумента stdin, stdout или stderr для функций
create_subprocess_shell()иcreate_subprocess_exec()и указывает, что будет использован специальный файлos.devnull.
5. Процесс
-
class asyncio.subprocess.Process -
Подпроцесс, созданный функциями
create_subprocess_exec()илиcreate_subprocess_shell().API класса
Processразработан близким к API классаsubprocess.Popen, но есть некоторые отличия:- Нет явного метода
poll() - Методы
communicate()иwait()не принимают параметр timeout: используйте функциюwait_for() - Параметр universal_newlines не поддерживается (поддерживаются только байтовые строки)
- Метод
wait()классаProcessявляется асинхронным, в то время как методwait()классаPopenреализован как цикл с проверкой.
Этот класс не потокобезопасен. Смотрите также раздел Подпроцессы и потоки.
-
coroutine wait() -
Ожидание завершения дочернего процесса. Установка и возврат атрибута
returncode.Этот метод является генератором.
Примечание
Это приведет к тупику при использовании
stdout=PIPEилиstderr=PIPEи дочерний процесс генерирует достаточно вывода в канал, так что он блокируется в ожидании, пока буфер канала ОС примет больше данных. Используйте методcommunicate()при использовании каналов, чтобы избежать этого.
-
coroutine communicate(input=None) -
Взаимодействие с процессом: Отправка данных в стандартный ввод. Чтение данных из стандартного вывода и стандартной ошибки до достижения конца файла. Ожидание завершения процесса. Дополнительный аргумент input должен содержать данные для отправки дочернему процессу, или
None, если данные не должны отправляться. Тип input должен быть байтовым.Метод
communicate()возвращает кортеж(stdout_data, stderr_data).Если при записи input в стандартный ввод возникает исключение
BrokenPipeErrorилиConnectionResetError, исключение игнорируется. Это происходит, когда процесс завершается до того, как все данные будут записаны в стандартный ввод.Обратите внимание, что если вы хотите отправить данные в стандартный ввод процесса, вам нужно создать объект Process с
stdin=PIPE. Аналогично, чтобы получить что-то кромеNoneв кортеже результата, вам нужно указатьstdout=PIPEи/илиstderr=PIPE.Этот метод является генератором.
Примечание
Считанные данные буферизуются в памяти, поэтому не используйте этот метод, если размер данных большой или неограничен.
Изменено в версии 3.4.2: Метод теперь игнорирует
BrokenPipeErrorиConnectionResetError.
-
send_signal(signal) -
Отправляет сигнал signal дочернему процессу.
Примечание
В Windows
SIGTERMявляется псевдонимом дляterminate().CTRL_C_EVENTиCTRL_BREAK_EVENTмогут быть отправлены процессам, запущенным с параметром creationflags, который включаетCREATE_NEW_PROCESS_GROUP.
-
terminate() -
Остановить дочерний процесс. В операционных системах Posix метод отправляет
signal.SIGTERMдочернему процессу. В Windows вызывается функция Win32 APITerminateProcess()для остановки дочернего процесса.
-
kill() -
Убить дочерний процесс. В операционных системах Posix функция отправляет
SIGKILLдочернему процессу. В Windowskill()является псевдонимом дляterminate().
-
stdin -
Поток стандартного ввода (
StreamWriter),Noneесли процесс был создан сstdin=None.
-
stdout -
Поток стандартного вывода (
StreamReader),Noneесли процесс был создан сstdout=None.
-
stderr -
Поток стандартной ошибки (
StreamReader),Noneесли процесс был создан сstderr=None.
Предупреждение
Используйте метод
communicate()вместо.stdin.write,.stdout.readили.stderr.read, чтобы избежать тупиков из-за прерывания чтения или записи потоков, блокирующих дочерний процесс.-
pid -
Идентификатор процесса.
Обратите внимание, что для процессов, созданных функцией
create_subprocess_shell(), этот атрибут представляет собой идентификатор процесса порожденной оболочки.
-
returncode -
Код возврата процесса при его завершении. Значение
Noneуказывает, что процесс еще не завершился.Отрицательное значение
-Nуказывает, что дочерний процесс был завершен сигналомN(только Unix).
- Нет явного метода
6. Подпроцессы и потоки
asyncio поддерживает запуск подпроцессов из разных потоков, но есть ограничения:
- Цикл событий должен выполняться в главном потоке
- Наблюдатель дочернего процесса должен быть создан в главном потоке, до выполнения дочерних процессов из других потоков. Вызовите функцию
get_child_watcher()в главном потоке для создания наблюдателя дочернего процесса.
Класс asyncio.subprocess.Process не является потокобезопасным.
См. также
7. Примеры дочерних процессов
7.1. Дочерний процесс с использованием транспорта и протокола
Пример протокола подпроцесса, используемого для получения вывода подпроцесса и ожидания завершения подпроцесса. Подпроцесс создаётся методом 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)
# Create the subprocess controlled by the protocol DateProtocol,
# redirect the standard output into a pipe
create = loop.subprocess_exec(lambda: DateProtocol(exit_future),
sys.executable, '-c', code,
stdin=None, stderr=None)
transport, protocol = yield from create
# Wait for the subprocess exit using the process_exited() method
# of the protocol
yield from exit_future
# Close the stdout pipe
transport.close()
# Read the output which was collected by the pipe_data_received()
# method of the protocol
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()
7.2. Подпроцесс, использующий потоки
Пример использования класса Process для управления подпроцессом и класса StreamReader для чтения со стандартного вывода. Подпроцесс создаётся функцией create_subprocess_exec():
import asyncio.subprocess
import sys
@asyncio.coroutine
def get_date():
code = 'import datetime; print(datetime.datetime.now())'
# Create the subprocess, redirect the standard output into a pipe
create = asyncio.create_subprocess_exec(sys.executable, '-c', code,
stdout=asyncio.subprocess.PIPE)
proc = yield from create
# Read one line of output
data = yield from proc.stdout.readline()
line = data.decode('ascii').rstrip()
# Wait for the subprocess exit
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()
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/asyncio-subprocess.html