Подпроцессы¶
Исходный код: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py
В этом разделе описываются высокоуровневые API async/await asyncio для создания подпроцессов и управления ими.
Вот пример того, как 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())
Смотрите также подраздел 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.
Если параметр 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, аналогичный API класса
subprocess.Popen
, но есть некоторые заметные отличия:в отличие от Popen, экземпляры процессов не имеют эквивалента методу
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
object), которые будут отправлены дочернему процессу.Возвращает кортеж
(stdout_data, stderr_data)
.Если при записи input в 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 этот метод отправляет
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 дочерние наблюдатели используются для ожидания завершения подпроцесса, смотрите Наблюдатели за процессом для получения дополнительной информации.
Изменено в версии 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.