Подпроцессы¶
Исходный код: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py
В этом разделе описываются высокоуровневые API async/await asyncio для создания и управления подпроцессами.
Вот пример того, как asyncio может выполнить команду shell и получить ее результат:
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())
См. также подраздел Examples.
Создание подпроцессов¶
-
coroutine
asyncio.
create_subprocess_exec
(program, *args, stdin=None, stdout=None, stderr=None, limit=None, **kwds)¶ Создайте подпроцесс.
Аргумент limit устанавливает предел буфера для оберток
StreamReader
дляProcess.stdout
иProcess.stderr
(еслиsubprocess.PIPE
передан в аргументах stdout и stderr).Возвращает экземпляр
Process
.Другие параметры см. в документации к
loop.subprocess_exec()
.Изменено в версии 3.10: Удален параметр loop.
-
coroutine
asyncio.
create_subprocess_shell
(cmd, stdin=None, stdout=None, stderr=None, limit=None, **kwds)¶ Выполните команду оболочки cmd.
Аргумент limit устанавливает предел буфера для оберток
StreamReader
дляProcess.stdout
иProcess.stderr
(еслиsubprocess.PIPE
передан в аргументах stdout и stderr).Возвращает экземпляр
Process
.Другие параметры см. в документации к
loop.subprocess_shell()
.Важно
Приложение несет ответственность за то, чтобы все пробельные и специальные символы были заключены в кавычки должным образом, чтобы избежать уязвимостей shell injection. Функция
shlex.quote()
может быть использована для правильной экранировки пробелов и специальных символов оболочки в строках, которые будут использоваться для построения команд оболочки.Изменено в версии 3.10: Удален параметр loop.
Примечание
Подпроцессы доступны для Windows, если используется ProactorEventLoop
. Подробности см. в разделе Subprocess Support on Windows.
См.также
asyncio также имеет следующие низкоуровневые API для работы с подпроцессами: loop.subprocess_exec()
, loop.subprocess_shell()
, loop.connect_read_pipe()
, loop.connect_write_pipe()
, а также Subprocess Transports и Subprocess Protocols.
Константы¶
-
asyncio.subprocess.
PIPE
¶ Может быть передан в параметры stdin, stdout или stderr.
Если в аргумент stdin передан PIPE, атрибут
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 не поддерживается.
Этот класс является not thread safe.
См. также раздел Subprocess and Threads.
-
coroutine
wait
()¶ Дождитесь завершения дочернего процесса.
Установить и вернуть атрибут
returncode
.Примечание
Этот метод может зайти в тупик при использовании
stdout=PIPE
илиstderr=PIPE
, когда дочерний процесс генерирует так много выходных данных, что блокируется, ожидая, пока буфер трубы ОС примет больше данных. Используйте методcommunicate()
при использовании труб, чтобы избежать этого состояния.
-
coroutine
communicate
(input=None)¶ Взаимодействуйте с процессом:
отправить данные на stdin (если input не
None
);считывать данные из stdout и stderr, пока не будет достигнуто EOF;
дождитесь завершения процесса.
Необязательный аргумент input - это данные (объект
bytes
), которые будут отправлены дочернему процессу.Возвращает кортеж
(stdout_data, stderr_data)
.Если при записи ввода в stdin возникает исключение
BrokenPipeError
илиConnectionResetError
, оно игнорируется. Это состояние возникает, когда процесс завершается до того, как все данные будут записаны в stdin.Если необходимо отправить данные в stdin процесса, процесс должен быть создан с аргументом
stdin=PIPE
. Аналогично, чтобы получить в кортеже результатов что-либо кромеNone
, процесс должен быть создан с аргументамиstdout=PIPE
и/илиstderr=PIPE
.Обратите внимание, что считанные данные буферизируются в памяти, поэтому не используйте этот метод, если размер данных большой или неограниченный.
-
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).
Подпроцесс и потоки¶
Стандартный цикл событий asyncio по умолчанию поддерживает запуск подпроцессов из разных потоков.
В Windows подпроцессы предоставляются только ProactorEventLoop
(по умолчанию), SelectorEventLoop
не имеет поддержки подпроцессов.
В UNIX child watchers используются для ожидания завершения подпроцесса, более подробную информацию смотрите в Наблюдатели за процессом.
Изменено в версии 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())'
# Create the subprocess; redirect the standard output
# into a pipe.
proc = await asyncio.create_subprocess_exec(
sys.executable, '-c', code,
stdout=asyncio.subprocess.PIPE)
# Read one line of output.
data = await proc.stdout.readline()
line = data.decode('ascii').rstrip()
# Wait for the subprocess exit.
await proc.wait()
return line
date = asyncio.run(get_date())
print(f"Current date: {date}")
См. также same example, написанные с использованием низкоуровневых API.