В этом разделе рассматривается высокоуровневые API , используемый синтаксисом async
/await
для создания субпроцессов (subprocesses) и управления ими во время выполнения асинхронного кода.
Вот пример того, как модуль 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
Можно легко выполнять и контролировать несколько субпроцессов (subprocesses) параллельно, так как все функции субпроцесса asyncio
являются асинхронными, а модуль asyncio
предоставляет множество инструментов для работы с такими функциями.
Модифицируем приведенный выше пример для одновременного выполнения нескольких команд:
async def main(): await asyncio.gather( run('ls /zzz'), run('sleep 1; echo "hello"')) asyncio.run(main())
asyncio.create_subprocess_exec()
,cmd
asyncio.create_subprocess_shell()
,asyncio.subprocess
,asyncio.subprocess.Process()
,asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)
:Функция asyncio.create_subprocess_exec()
создает субпроцесс (subprocess) и возвращает экземпляр процесса. Представляет собой сопрограмму.
Аргумент limit
устанавливает ограничение буфера для оберток asyncio.StreamReader
для Process.stdout
и Process.stderr
, если asyncio.subprocess.PIPE
передается в аргументы stdout
и stderr
.
Смотрите документацию функции loop.subprocess_exec()
цикла событий, что бы узнать значение других аргументов функции asyncio.create_subprocess_exec()
.
Параметр цикла loop
устарел и не рекомендуется к использованию, будет удален в Python 3.10.
asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)
:Функция asyncio.create_subprocess_shell()
выполняет команду командной оболочки cmd
и возвращает экземпляр процесса. Представляет собой сопрограмму.
Аргумент limit
устанавливает ограничение буфера для оберток asyncio.StreamReader
для Process.stdout
и Process.stderr
, если asyncio.subprocess.PIPE
передается в аргументы stdout
и stderr
.
Смотрите документацию функции loop.subprocess_shell()
цикла событий, что бы узнать значение других аргументов функции asyncio.create_subprocess_shell()
.
Важно. Приложение несет ответственность за то, чтобы все пробелы и специальные символы были заключены в кавычки, чтобы избежать уязвимостей, связанных с внедрением оболочки. Можно использовать функцию shlex.quote()
для правильного экранирования пробелов и специальных символов оболочки в строках, которые будут использоваться для создания команд оболочки.
Параметр цикла loop
устарел и не рекомендуется к использованию, будет удален в Python 3.10.
Примечание. По умолчанию, реализация [цикла событий модуля asyncio
] в Windows не поддерживает субпроцессы. Субпроцессы доступны для Windows, если используется asyncio.ProactorEventLoop
, представляющий цикл событий для Windows (использует порты завершения ввода-вывода IOCP).
Также модуль asyncio
имеет следующие низкоуровневые API для работы с подпроцессами:- loop.subprocess_exec()
,- loop.subprocess_shell()
,- loop.connect_read_pipe()
,- loop.connect_write_pipe()
.
asyncio.subprocess.PIPE
:Константа asyncio.subprocess.PIPE
может быть передана в аргументы stdin
, stdout
или stderr
.
Если PIPE передается в аргумент stdin
, то атрибут Process.stdin
будет указывать на экземпляр asyncio.StreamWriter
.
Если PIPE передается аргументам stdout
или stderr
, атрибуты Process.stdout
и Process.stderr
будут указывать на экземпляры asyncio.StreamReader
.
asyncio.subprocess.STDOUT
:Константа asyncio.subprocess
это специальное значение, которое может использоваться как аргумент stderr
и указывает, что стандартная ошибка должна перенаправляться в стандартный вывод.
asyncio.subprocess.DEVNULL
:Константа asyncio.subprocess.DEVNULL
представляет собой специальное значение, которое можно использовать в качестве аргумента stdin
, stdout
или stderr
для функций создания субпроцессов. Константа указывает, что специальный файл os.devnull
будет использоваться для соответствующего потока подпроцесса.
Обе функции asyncio.create_subprocess_exec()
и asyncio.create_subprocess_shell()
возвращают экземпляры класса asyncio.subprocess.Process
.
Экземпляр Process
- это обертка высокого уровня, которая позволяет взаимодействовать с субпроцессами и следить за их завершением.
asyncio.subprocess.Process
:Класс asyncio.subprocess.Process
это объект, который охватывает процессы ОС, созданные функциями asyncio.create_subprocess_exec()
и asyncio.create_subprocess_shell()
(описаны выше).
Этот класс имеет API, аналогичное обычному классу subprocess.Popen
, но есть некоторые заметные отличия:
subprocess.Popen
, экземпляры asyncio.subprocess.Process
не имеют эквивалента методу Popen.poll()
;Process.communicate()
и Process.wait()
не имеют аргумента timeout
тайм-аут. Что бы воспользоваться тайм-аутом - используйте функцию asyncio.wait_for()
;Process.wait()
является асинхронным, тогда как метод Popen.wait()
реализован как блокирующий цикл;universal_newlines
не поддерживается.Этот класс не является потокобезопасным.
asyncio.subprocess.Process
:Process.wait()
- ожидает завершения дочернего процесса,Process.communicate()
- взаимодействует с процессом,Process.send_signal()
- посылает сигнал дочернему процессу,Process.terminate()
- останавливает дочерний процесс,Process.kill()
- убивает дочерний процесс,Process.stdin
- стандартный входной поток,Process.stdout
- стандартный выходной поток,Process.stderr
- стандартный поток ошибок,Process.returncode
- код возврата субпроцесса,Process.wait()
:Метод Process.wait()
представляет собой сопрограмму и ждет завершения дочернего процесса. Устанавливает и возвращает атрибут Process.returncode
кода возврата.
Примечание. Метод Process.wait()
может привести к взаимоблокировке при использовании stdout=asyncio.subprocess.PIPE
или stderr=asyncio.subprocess.PIPE
. Дочерний процесс генерирует так много выходных данных, что он блокирует ожидание, пока буфер канала ОС не примет больше данных. Чтобы избежать такого поведения, используйте метод Process.communicate()
при использовании каналов PIPE
.
Process.communicate(input=None)
:Метод Process.communicate()
представляет собой сопрограмму и взаимодействует с процессом:
stdin
(если входной сигнал не равен None
);stdout
и stderr
до тех пор, пока не будет достигнут EOF;Возвращает кортеж вида (stdout_data, stderr_data)
.
Необязательный аргумент input
- это данные (объект байтов), которые будут отправлены дочернему процессу.
Если при записи ввода в stdin
возникает исключение BrokenPipeError
или ConnectionResetError
, то исключение игнорируется. Это условие возникает, когда процесс завершается до того, как все данные будут записаны в стандартный ввод.
Если требуется отправить данные на stdin
процесса, то процесс должен быть создан с помощью stdin=asyncio.subprocess.PIPE
. Аналогично, чтобы получить в кортеже результатов что-либо, кроме None
, процесс должен быть создан с аргументами stdout=asyncio.subprocess.PIPE
и/или stderr=asyncio.subprocess.PIPE
.
Обратите внимание, что считанные данные буферизуются в памяти, поэтому не используйте метод Process.communicate()
, если размер данных большой или неограниченный!
Process.send_signal(signal)
:Метод Process.send_signal()
посылает сигнал signal
дочернему процессу.
Примечание. В Windows SIGTERM
является псевдонимом для метода Process.terminate()
. CTRL_C_EVENT
и CTRL_BREAK_EVENT
могут быть отправлены процессам, запущенным с параметром creationflags
, который включает CREATE_NEW_PROCESS_GROUP
.
Process.terminate()
:Метод Process.terminate()
останавливает дочерний процесс.
В системах POSIX этот метод посылает signal.SIGTERM
для дочернего процесса.
В Windows для остановки дочернего процесса вызывается функция Win32 API TerminateProcess()
.
Process.kill()
:Метод Process.kill()
убивает дочерний процесс.
В системах POSIX этот метод отправляет signal.SIGKILL
дочернему процессу.
В Windows Этот метод является псевдонимом для метода Process.terminate()
.
Process.stdin
:Атрибут Process.stdin
это стандартный входной поток (asyncio.StreamWriter
) или None
, если процесс был создан с помощью stdin=None
.
Process.stdout
:Атрибут Process.stdout
это стандартный выходной поток (asyncio.StreamReader
) или None
, если процесс был создан с помощью stdout=None
.
Process.stderr
:Атрибут Process.stderr
это стандартный поток ошибок (asyncio.StreamReader
) или None
, если процесс был создан с помощью stderr=None
.
Предупреждение. Используйте метод Process.communicate()
, а не Process.stdin.write()
, await Process.stdout.read()
или await Process.stderr.read
. Это позволяет избежать взаимоблокировок из-за того, что потоки приостанавливают чтение или запись и блокируют дочерний процесс.
Process.pid
:Идентификационный номер процесса (PID).
Обратите внимание, что для процессов, созданных функцией asyncio.create_subprocess_shell()
, этот атрибут является номером процесса PID созданной оболочки.
Process.returncode
:Атрибут Process.returncode
возвращает код возврата процесса при выходе.
Значение None
указывает на то, что процесс еще не завершен.
Отрицательное значение -N
указывает на то, что дочерний элемент был прерван сигналом N
(только POSIX).
Пример использования класса asyncio.subprocess.Process
для управления субпроцессом и класса asyncio.StreamReader
для чтения из его стандартного вывода.
Подпроцесс создается функцией asyncio.create_subprocess_exec()
:
import asyncio import sys async def get_date(): # строка с кодом, которую будем выполнять, # ее можно заменить любой командой code = 'import datetime; print(datetime.datetime.now())' # Создаем подпроцесс и перенаправляем # стандартный вывод в канал `PIPE`. proc = await asyncio.create_subprocess_exec( sys.executable, '-c', code, stdout=asyncio.subprocess.PIPE) # Читаем вывод запущенной команды. data = await proc.stdout.readline() line = data.decode('ascii').rstrip() # Ждем когда субпроцесс завершиться. await proc.wait() # возвращаем прочитанную строку return line if __name__ == '__main__': date = asyncio.run(get_date()) # выводим результат работы print(f"Current date: {date}") # Current date: 2021-01-19 09:57:24.047066