Подпроцессы
В разделе рассказывается о 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())
См. также подраздел Примеры.
Создание подпроцессов
-
coroutine
asyncio.
create_subprocess_exec
(program, *args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds) Создать подпроцессы.
Аргумент limit устанавливает предел буфера для
StreamReader
оболочек дляProcess.stdout
иProcess.stderr
(еслиsubprocess.PIPE
передается аргументам stdout и stderr).Возвращает
Process
сущность.Другие параметры см. в документации
loop.subprocess_exec()
.Устарело с версии 3.8, будет удалено в 3.10 версии.: Параметр loop.
-
coroutine
asyncio.
create_subprocess_shell
(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds) Выполнить команду cmd оболочки.
Аргумент limit устанавливает предел буфера для
StreamReader
оболочек дляProcess.stdout
иProcess.stderr
(еслиsubprocess.PIPE
передается аргументам stdout и stderr).Возвращает
Process
сущность.Другие параметры см. в документации
loop.subprocess_shell()
.Важно
Приложение несёт ответственность за то, чтобы все пробелы и специальные символы экранировались надлежащим образом, чтобы избежать уязвимостей шелл инъекции . Функция
shlex.quote()
может использоваться для правильного экранирования пробелов и специальных символов оболочки в строки, которые будут использоваться для создания команд оболочки.Устарело с версии 3.8, будет удалено в 3.10 версии.: Параметр loop.
Примечание
Подпроцессы доступны для Windows, если используется ProactorEventLoop
.
Подробности см. в
поддержке подпроцессов в Windows.
См.также
У asyncio также есть следующие API низкоуровневое, чтобы работать с
подпроцессами: loop.subprocess_exec()
, loop.subprocess_shell()
,
loop.connect_read_pipe()
, loop.connect_write_pipe()
, а также
Транспорты подпроцессов и
Протоколы подпроцессов.
Константы
-
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
будет использоваться для соответствующего потока подпроцесса.
Взаимодействие с подпроцессами
И 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 не поддерживается.
Класс не потокобезопасен.
См. также раздел Подпроцессы и потоки.
-
coroutine
wait
() Дождаться завершения дочернего процесса.
Установить возвращаемый атрибут
returncode
.Примечание
Этот метод может взаимоблокироваться при использовании
stdout=PIPE
илиstderr=PIPE
, и дочерний процесс генерирует столько выходных данных, что блокирует ожидание, когда буфер пайп ОС примет больше данных. Используйтеcommunicate()
метод при использовании пайпы, чтобы избежать этого условия.
-
coroutine
communicate
(input=None) Взаимодействие с процессами:
- Послать данные в stdin (если input не является
None
); - Чтение данных из stdout и stderr до тех пор, пока не будет достигнуто EOF;
- Дождаться завершения процесса.
Необязательный аргумент input — это данные (объект
bytes
), которые будут отправлены нижестоящему процессу.Возвращает кортеж
(stdout_data, stderr_data)
.Если или
BrokenPipeError
или исключениеConnectionResetError
вызваны, записывая input в stdin, исключение игнорируется. Это условие возникает при завершении процесса перед записью всех данных в stdin.Если требуется отправить данные в stdin процесса, процесс необходимо создать с помощью
stdin=PIPE
. Аналогично, чтобы получить что-либо, кромеNone
в кортеже результатов, процесс должен быть создан сstdout=PIPE
и/илиstderr=PIPE
аргументами.Следует отметить, что считанные данные буферизуются в памяти, поэтому не используйте эту метод, если размер данных большой или неограниченный.
- Послать данные в stdin (если input не является
-
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).
- В отличие от Popen, у Process сущности нет аналога
Подпроцессы и потоки
Стандартный asyncio событийный цикл по умолчанию поддерживает выполнение подпроцессов из разных потоков.
В Windows подпроцессы предоставляются только ProactorEventLoop
(по умолчанию),
у SelectorEventLoop
нет поддержки подпроцессов.
В UNIX наблюдатели за дочерними процессами используют ожидания завершения подпроцессов. См. Наблюдатели за процессами для получения дополнительной информации.
Изменено в версии 3.8: UNIX переключилась на использование ThreadedChildWatcher
для создания
подпроцессов из разных потоков без каких-либо ограничений.
Порождение подпроцесса с помощью неактивного текущего дочернего наблюдателя вызывает RuntimeError
.
Обратите внимание, что у альтернативных реализаций событийного цикла могут быть собственные ограничения; см. документацию.
См.также
Примеры
Пример, используя 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.