Подпроцессы
Исходный код: 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=None, **kwds) -
Создать подпроцесс.
Аргумент limit задаёт предел буфера для обёрток
StreamReaderдляProcess.stdoutиProcess.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=None, **kwds) -
Выполнить команду оболочки cmd.
Аргумент limit задаёт предел буфера для обёрток
StreamReaderдляProcess.stdoutиProcess.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 для остановки дочернего процесса вызывается функция API Win32
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())'
# 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/asyncio-subprocess.html