Подпроцессы
Исходный код: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py
В этом разделе описаны высокоуровневые API asyncio с async/await для создания подпроцессов и управления ими.
Вот пример того, как 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())
См. также подраздел Примеры.
Создание подпроцессов
-
async asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, limit=65536, **kwds) -
Создаёт подпроцесс.
Аргумент limit задаёт предел буфера для оболочек
StreamReaderдляstdoutиstderr(если аргументам stdout и stderr переданsubprocess.PIPE).Возвращает экземпляр
Process.Другие параметры описаны в документации
loop.subprocess_exec().Если объект процесса будет удалён сборщиком мусора, пока процесс ещё выполняется, дочерний процесс будет уничтожен.
Изменено в версии 3.10: Параметр loop удалён.
-
async asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, limit=65536, **kwds) -
Выполняет команду оболочки cmd.
Аргумент limit задаёт предел буфера для оболочек
StreamReaderдляstdoutиstderr(если аргументам stdout и stderr переданsubprocess.PIPE).Возвращает экземпляр
Process.Другие параметры описаны в документации
loop.subprocess_shell().Если объект процесса будет удалён сборщиком мусора, пока процесс ещё выполняется, дочерний процесс будет уничтожен.
Важно
Приложение должно самостоятельно обеспечить правильное экранирование всех пробельных символов и специальных символов, чтобы избежать уязвимостей типа инъекция в оболочку. Функцию
shlex.quote()можно использовать для правильного экранирования пробельных символов и специальных символов оболочки в строках, используемых для формирования команд оболочки.Изменено в версии 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.
Если аргументу stdin передан PIPE, атрибут
Process.stdinбудет ссылаться на экземплярStreamWriter.Если аргументам stdout или stderr передан PIPE, атрибуты
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, похожий на API класса
subprocess.Popen, однако между ними есть несколько существенных различий:- в отличие от Popen, у экземпляров Process нет аналога метода
poll(); - у методов
communicate()иwait()нет параметра timeout: используйте функциюwait_for(); - метод
Process.wait()является асинхронным, тогда как методsubprocess.Popen.wait()реализован как блокирующий активный цикл ожидания; - параметр universal_newlines не поддерживается.
Этот класс не является потокобезопасным.
См. также раздел Подпроцессы и потоки.
-
async wait() -
Ожидает завершения дочернего процесса.
Устанавливает и возвращает атрибут
returncode.Примечание
При использовании
stdout=PIPEилиstderr=PIPEэтот метод может привести к взаимной блокировке, если дочерний процесс выводит настолько много данных, что ему приходится ждать, пока буфер канала ОС освободит место для новых данных. При использовании каналов вызовите методcommunicate(), чтобы избежать этой ситуации.
-
async communicate(input=None) -
Взаимодействует с процессом:
- отправляет данные в stdin (если input не равен
None); - закрывает stdin;
- читает данные из stdout и stderr до достижения EOF;
- ожидает завершения процесса.
Необязательный аргумент input — это данные (объект
bytes), которые будут отправлены дочернему процессу.Возвращает кортеж
(stdout_data, stderr_data).Если при записи input в stdin возникает исключение
BrokenPipeErrorилиConnectionResetError, оно игнорируется. Такая ситуация возникает, когда процесс завершается до того, как все данные записаны в stdin.Чтобы отправить данные в stdin процесса, его нужно создать с помощью
stdin=PIPE. Аналогично, чтобы получить в кортеже результата значения, отличные отNone, процесс нужно создать с аргументамиstdout=PIPEи/илиstderr=PIPE.Обратите внимание: прочитанные данные буферизуются в памяти, поэтому не используйте этот метод, если объём данных велик или не ограничен.
Изменено в версии 3.12: stdin также закрывается при вызове
input=None. - отправляет данные в stdin (если input не равен
-
send_signal(signal) -
Отправляет дочернему процессу сигнал signal.
Примечание
В Windows
SIGTERMявляется псевдонимомterminate(). Процессам, запущенным с параметром creationflags, включающимCREATE_NEW_PROCESS_GROUP, можно отправлятьCTRL_C_EVENTиCTRL_BREAK_EVENT.
-
terminate() -
Останавливает дочерний процесс.
В системах POSIX этот метод отправляет дочернему процессу сигнал
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означает, что процесс ещё не завершился.Для процессов, созданных с помощью
create_subprocess_exec(), отрицательное значение-Nозначает, что дочерний процесс был завершён сигналомN(только POSIX).Для процессов, созданных с помощью
create_subprocess_shell(), код возврата отражает код завершения самой оболочки (например,/bin/sh), которая может преобразовывать сигналы в такие коды, как128+N. Подробности см. в документации оболочки (например, в разделе «Код завершения» руководства Bash).
- в отличие от Popen, у экземпляров Process нет аналога метода
Подпроцессы и потоки
Стандартный цикл событий asyncio по умолчанию поддерживает запуск подпроцессов из разных потоков.
В Windows подпроцессы поддерживаются только в ProactorEventLoop (по умолчанию); SelectorEventLoop не поддерживает подпроцессы.
Обратите внимание: у альтернативных реализаций цикла событий могут быть собственные ограничения; ознакомьтесь с их документацией.
См. также
Примеры
Пример использования класса Process для управления подпроцессом и класса StreamReader для чтения данных из его стандартного потока вывода.
Подпроцесс создаётся функцией create_subprocess_exec():
import asyncio
import sys
async def get_date():
code = 'import datetime as dt; print(dt.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}")
См. также тот же пример, написанный с использованием низкоуровневых API.
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/asyncio-subprocess.html