Подпроцессы
Исходный код: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py
В этом разделе описываются высокоуровневые asyncio-API 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 subprocess асинхронны, а 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, 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()для других параметров.Важно
Приложению необходимо убедиться, что все пробелы и специальные символы должным образом заключены в кавычки, чтобы избежать уязвимостей инъекции командной строки. Функция
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.
Если 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); - закрытие stdin;
- чтение данных из stdout и stderr, пока не будет достигнут конец файла;
- ожидание завершения процесса.
Необязательный аргумент 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().CTRL_C_EVENTиCTRL_BREAK_EVENTмогут быть отправлены в процессы, запущенные с параметром creationflags, включающимCREATE_NEW_PROCESS_GROUP.
-
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указывает, что процесс еще не завершился.Отрицательное значение
-Nуказывает, что дочерний процесс был завершен сигналомN(только POSIX).
- в отличие от Popen, экземпляры Process не имеют эквивалента методу
Дочерние процессы и потоки
Стандартная асинхронная событийная петля по умолчанию поддерживает запуск дочерних процессов из разных потоков.
В 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