Подпроцессы¶
Исходный код: 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.