Содержание страницы
18.5.6. Подпроцесс¶Subprocess
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. Другие параметры описаны в BaseEventLoop.subprocess_exec().
Возвращает экземпляр процесс.
Эта функция – корутина.
- coroutine asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)¶
Выполняет команду оболочки cmd.
Параметр limit задаёт лимит буфера, передаваемый StreamReader. Другие параметры описаны в BaseEventLoop.subprocess_shell().
Возвращает экземпляр процесс.
Приложение должно самостоятельно гарантировать, что все пробелы и метасимволы экранированы надлежащим образом, чтобы избежать уязвимостей shell injection. Функция shlex.quote() позволяет корректно экранировать пробелы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.
Эта функция – корутина.
Используйте методы BaseEventLoop.connect_read_pipe() и BaseEventLoop.connect_write_pipe() для подключения каналов.
18.5.6.3. Создание подпроцесса: низкоуровневый API с использованием subprocess.Popen¶Create a subprocess: low-level API using subprocess.Popen
Запуск подпроцессов асинхронно с использованием модуля подпроцесс.
- coroutine BaseEventLoop.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, которые не следует указывать вовсе.
Возвращает пару (транспорт, протокол), где транспорт – экземпляр BaseSubprocessTransport.
Этот метод является корутиной.
Описание параметров см. в конструкторе класса subprocess.Popen.
- coroutine BaseEventLoop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)¶
Создаёт подпроцесс из cmd, который является строкой символов или байтовой строкой, закодированной в кодировку файловой системы, с использованием синтаксиса «shell» платформы. Это похоже на класс стандартной библиотеки subprocess.Popen, вызываемый с shell=True.
Параметр protocol_factory должен создавать экземпляр подкласса класса asyncio.SubprocessProtocol.
Подробнее об остальных аргументах см. в subprocess_exec().
Возвращает пару (транспорт, протокол), где транспорт – экземпляр BaseSubprocessTransport.
Приложение должно самостоятельно гарантировать, что все пробелы и метасимволы экранированы надлежащим образом, чтобы избежать уязвимостей shell injection. Функция shlex.quote() позволяет корректно экранировать пробелы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.
Этот метод является корутиной.
См. также
Методы BaseEventLoop.connect_read_pipe() и BaseEventLoop.connect_write_pipe().
18.5.6.4. Константы¶Constants
- 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.
18.5.6.5. Процесс¶Process
- 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 реализован как цикл активного ожидания.
Этот класс не потокобезопасен. См. также раздел\nПодпроцессы и потоки.
- coroutine wait()¶
Ожидает завершения дочернего процесса. Устанавливает и возвращает атрибут returncode.
Этот метод является корутиной.
Примечание
Это приведёт к взаимоблокировке при использовании stdout=PIPE или stderr=PIPE и если дочерний процесс генерирует достаточно выходных данных в канал, так что он блокируется в ожидании, пока буфер канала ОС примет больше данных. Используйте метод communicate() при работе с каналами, чтобы избежать этого.
- coroutine communicate(input=None)¶
Взаимодействие с процессом: отправка данных в stdin. Чтение данных из stdout и stderr до достижения конца файла. Ожидание завершения процесса. Необязательный аргумент input должен содержать данные для отправки дочернему процессу, или None, если данные не должны отправляться. Тип input должен быть bytes.
communicate() возвращает кортеж (stdout_data, stderr_data).
Если при записи input в stdin возникает исключение BrokenPipeError или ConnectionResetError, оно игнорируется. Это происходит, когда процесс завершается до того, как все данные будут записаны в stdin.
Обратите внимание: чтобы отправить данные в stdin процесса, необходимо создать объект Process с stdin=PIPE. Аналогично, чтобы получить в результирующем кортеже что-либо, кроме None, нужно также указать stdout=PIPE и/или stderr=PIPE.
Этот метод является корутиной.
Примечание
Прочитанные данные буферизируются в памяти, поэтому не следует использовать этот метод, если\nразмер данных велик или неограничен.
Изменено в версии 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 API TerminateProcess().
- kill()¶
Завершает дочерний процесс. В ОС Posix функция отправляет SIGKILL дочернему процессу. В Windows kill() является псевдонимом для 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).
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
Пример протокола подпроцесса для получения вывода подпроцесса и ожидания его завершения. Подпроцесс создаётся методом BaseEventLoop.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()