asyncio-subprocess.md
1> **Источник:** https://python-all.ru/3.4/library/asyncio-subprocess.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 18.5.6. Подпроцесс89## 18.5.6.1. Цикл событий Windows1011В Windows цикл событий по умолчанию – [`SelectorEventLoop`](https://python-all.ru/3.4/library/asyncio-eventloops.html#asyncio.SelectorEventLoop), который не поддерживает подпроцессы. Вместо него следует использовать [`ProactorEventLoop`](https://python-all.ru/3.4/library/asyncio-eventloops.html#asyncio.ProactorEventLoop). Пример использования в Windows:1213```python14import asyncio, sys1516if sys.platform == 'win32':17 loop = asyncio.ProactorEventLoop()18 asyncio.set_event_loop(loop)19```2021> **См. также**22>23> [*Доступные циклы событий*](https://python-all.ru/3.4/library/asyncio-eventloops.html#asyncio-event-loops) и [*Поддержка платформ*](https://python-all.ru/3.4/library/asyncio-eventloops.html#asyncio-platform-support).2425## 18.5.6.2. Создание подпроцесса: высокоуровневый API с использованием процесса2627#### `coroutine asyncio.create_subprocess_exec(*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)`2829Создание подпроцесса.3031Параметр *limit* задаёт лимит буфера, передаваемый [`StreamReader`](https://python-all.ru/3.4/library/asyncio-stream.html#asyncio.StreamReader). Другие параметры описаны в [`BaseEventLoop.subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.BaseEventLoop.subprocess_exec).3233Возвращает экземпляр [`процесс`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process).3435Эта функция – [*корутина*](https://python-all.ru/3.4/library/asyncio-task.html#coroutine).3637#### `coroutine asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)`3839Выполняет команду оболочки *cmd*.4041Параметр *limit* задаёт лимит буфера, передаваемый [`StreamReader`](https://python-all.ru/3.4/library/asyncio-stream.html#asyncio.StreamReader). Другие параметры описаны в [`BaseEventLoop.subprocess_shell()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.BaseEventLoop.subprocess_shell).4243Возвращает экземпляр [`процесс`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process).4445Приложение должно самостоятельно гарантировать, что все пробелы и метасимволы экранированы надлежащим образом, чтобы избежать уязвимостей [shell injection](https://python-all.ru/3.4/library/asyncio-subprocess.html). Функция [`shlex.quote()`](https://python-all.ru/3.4/library/shlex.html#shlex.quote) позволяет корректно экранировать пробелы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.4647Эта функция – [*корутина*](https://python-all.ru/3.4/library/asyncio-task.html#coroutine).4849Используйте методы [`BaseEventLoop.connect_read_pipe()`](https://python-all.ru/3.4/library/asyncio-eventloop.html#asyncio.BaseEventLoop.connect_read_pipe) и [`BaseEventLoop.connect_write_pipe()`](https://python-all.ru/3.4/library/asyncio-eventloop.html#asyncio.BaseEventLoop.connect_write_pipe) для подключения каналов.5051## 18.5.6.3. Создание подпроцесса: низкоуровневый API с использованием subprocess.Popen5253Запуск подпроцессов асинхронно с использованием модуля [`подпроцесс`](https://python-all.ru/3.4/library/subprocess.html#module-subprocess).5455#### `coroutine BaseEventLoop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)`5657Создаёт подпроцесс из одного или нескольких строковых аргументов (строк символов или байтовых строк, закодированных в [*кодировку файловой системы*](https://python-all.ru/3.4/library/os.html#filesystem-encoding)), где первая строка задаёт программу для выполнения, а остальные – её аргументы. (Таким образом, вместе строковые аргументы образуют значение `sys.argv` программы, если предположить, что это скрипт Python.) Это похоже на класс стандартной библиотеки [`subprocess.Popen`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen), вызываемый с shell=False и списком строк, передаваемым в качестве первого аргумента; однако [`Popen`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen) принимает единственный аргумент – список строк, а [`subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.BaseEventLoop.subprocess_exec) принимает несколько строковых аргументов.5859Параметр *protocol\_factory* должен создавать экземпляр подкласса класса [`asyncio.SubprocessProtocol`](https://python-all.ru/3.4/library/asyncio-protocol.html#asyncio.SubprocessProtocol).6061Другие параметры:6263- *stdin*: Либо файлоподобный объект, представляющий канал, который будет подключён к стандартному потоку ввода подпроцесса с помощью [`connect_write_pipe()`](https://python-all.ru/3.4/library/asyncio-eventloop.html#asyncio.BaseEventLoop.connect_write_pipe), либо константа [`subprocess.PIPE`](https://python-all.ru/3.4/library/subprocess.html#subprocess.PIPE) (по умолчанию). По умолчанию создаётся и подключается новый канал.64- *stdout*: Либо файлоподобный объект, представляющий канал, который будет подключён к стандартному потоку вывода подпроцесса с помощью [`connect_read_pipe()`](https://python-all.ru/3.4/library/asyncio-eventloop.html#asyncio.BaseEventLoop.connect_read_pipe), либо константа [`subprocess.PIPE`](https://python-all.ru/3.4/library/subprocess.html#subprocess.PIPE) (по умолчанию). По умолчанию создаётся и подключается новый канал.65- *stderr*: Либо файлоподобный объект, представляющий канал, который будет подключён к стандартному потоку ошибок подпроцесса с помощью [`connect_read_pipe()`](https://python-all.ru/3.4/library/asyncio-eventloop.html#asyncio.BaseEventLoop.connect_read_pipe), либо одна из констант [`subprocess.PIPE`](https://python-all.ru/3.4/library/subprocess.html#subprocess.PIPE) (по умолчанию) или [`subprocess.STDOUT`](https://python-all.ru/3.4/library/subprocess.html#subprocess.STDOUT). По умолчанию создаётся и подключается новый канал. Когда указана константа [`subprocess.STDOUT`](https://python-all.ru/3.4/library/subprocess.html#subprocess.STDOUT), стандартный поток ошибок подпроцесса подключается к тому же каналу, что и стандартный поток вывода.66- Все остальные именованные аргументы передаются в [`subprocess.Popen`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen) без интерпретации, за исключением *bufsize*, *universal\_newlines* и *shell*, которые не следует указывать вовсе.6768Возвращает пару `(транспорт, протокол)`, где *транспорт* – экземпляр [`BaseSubprocessTransport`](https://python-all.ru/3.4/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport).6970Этот метод является [*корутиной*](https://python-all.ru/3.4/library/asyncio-task.html#coroutine).7172Описание параметров см. в конструкторе класса [`subprocess.Popen`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen).7374#### `coroutine BaseEventLoop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)`7576Создаёт подпроцесс из *cmd*, который является строкой символов или байтовой строкой, закодированной в [*кодировку файловой системы*](https://python-all.ru/3.4/library/os.html#filesystem-encoding), с использованием синтаксиса «shell» платформы. Это похоже на класс стандартной библиотеки [`subprocess.Popen`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen), вызываемый с `shell=True`.7778Параметр *protocol\_factory* должен создавать экземпляр подкласса класса [`asyncio.SubprocessProtocol`](https://python-all.ru/3.4/library/asyncio-protocol.html#asyncio.SubprocessProtocol).7980Подробнее об остальных аргументах см. в [`subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.BaseEventLoop.subprocess_exec).8182Возвращает пару `(транспорт, протокол)`, где *транспорт* – экземпляр [`BaseSubprocessTransport`](https://python-all.ru/3.4/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport).8384Приложение должно самостоятельно гарантировать, что все пробелы и метасимволы экранированы надлежащим образом, чтобы избежать уязвимостей [shell injection](https://python-all.ru/3.4/library/asyncio-subprocess.html). Функция [`shlex.quote()`](https://python-all.ru/3.4/library/shlex.html#shlex.quote) позволяет корректно экранировать пробелы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.8586Этот метод является [*корутиной*](https://python-all.ru/3.4/library/asyncio-task.html#coroutine).8788> **См. также**89>90> Методы [`BaseEventLoop.connect_read_pipe()`](https://python-all.ru/3.4/library/asyncio-eventloop.html#asyncio.BaseEventLoop.connect_read_pipe) и [`BaseEventLoop.connect_write_pipe()`](https://python-all.ru/3.4/library/asyncio-eventloop.html#asyncio.BaseEventLoop.connect_write_pipe).9192## 18.5.6.4. Константы9394#### `asyncio.subprocess.PIPE`9596Специальное значение, которое может использоваться в качестве аргумента *stdin*, *stdout* или *stderr* для [`create_subprocess_shell()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_shell) и [`create_subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_exec) и указывает, что должен быть открыт канал к стандартному потоку.9798#### `asyncio.subprocess.STDOUT`99100Специальное значение, которое может использоваться в качестве аргумента *stderr* для [`create_subprocess_shell()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_shell) и [`create_subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_exec) и указывает, что стандартный поток ошибок должен направляться в тот же дескриптор, что и стандартный вывод.101102#### `asyncio.subprocess.DEVNULL`103104Специальное значение, которое можно использовать в качестве аргумента *stdin*, *stdout* или *stderr* для [`create_subprocess_shell()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_shell) и [`create_subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_exec), и указывает, что будет использоваться специальный файл [`os.devnull`](https://python-all.ru/3.4/library/os.html#os.devnull).105106## 18.5.6.5. Процесс107108#### `class asyncio.subprocess.Process`109110Подпроцесс, созданный функцией [`create_subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_exec) или [`create_subprocess_shell()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_shell).111112API класса [`Process`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) спроектирован близким к API класса [`subprocess.Popen`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen), но есть некоторые отличия:113114- Отсутствует явный метод [`poll()`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen.poll)115- Методы [`communicate()`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen.communicate) и [`wait()`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen.wait) не принимают параметр *timeout*: используйте функцию [`wait_for()`](https://python-all.ru/3.4/library/asyncio-task.html#asyncio.wait_for)116- Параметр *universal\_newlines* не поддерживается (поддерживаются только строки байтов)117- Метод [`wait()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.wait) класса [`Process`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) является асинхронным, тогда как метод [`wait()`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen.wait) класса [`Popen`](https://python-all.ru/3.4/library/subprocess.html#subprocess.Popen) реализован как цикл активного ожидания.118119Этот класс [*не потокобезопасен*](https://python-all.ru/3.4/library/asyncio-dev.html#asyncio-multithreading). См. также раздел\\n[*Подпроцессы и потоки*](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio-subprocess-threads).120121#### `coroutine wait()`122123Ожидает завершения дочернего процесса. Устанавливает и возвращает атрибут [`returncode`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.returncode).124125Этот метод является [*корутиной*](https://python-all.ru/3.4/library/asyncio-task.html#coroutine).126127> **Примечание**128>129> Это приведёт к взаимоблокировке при использовании `stdout=PIPE` или `stderr=PIPE` и если дочерний процесс генерирует достаточно выходных данных в канал, так что он блокируется в ожидании, пока буфер канала ОС примет больше данных. Используйте метод [`communicate()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.communicate) при работе с каналами, чтобы избежать этого.130131#### `coroutine communicate(input=None)`132133Взаимодействие с процессом: отправка данных в stdin. Чтение данных из stdout и stderr до достижения конца файла. Ожидание завершения процесса. Необязательный аргумент *input* должен содержать данные для отправки дочернему процессу, или `None`, если данные не должны отправляться. Тип *input* должен быть bytes.134135[`communicate()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.communicate) возвращает кортеж `(stdout_data, stderr_data)`.136137Если при записи *input* в stdin возникает исключение [`BrokenPipeError`](https://python-all.ru/3.4/library/exceptions.html#BrokenPipeError) или [`ConnectionResetError`](https://python-all.ru/3.4/library/exceptions.html#ConnectionResetError), оно игнорируется. Это происходит, когда процесс завершается до того, как все данные будут записаны в stdin.138139Обратите внимание: чтобы отправить данные в stdin процесса, необходимо создать объект Process с `stdin=PIPE`. Аналогично, чтобы получить в результирующем кортеже что-либо, кроме `None`, нужно также указать `stdout=PIPE` и/или `stderr=PIPE`.140141Этот метод является [*корутиной*](https://python-all.ru/3.4/library/asyncio-task.html#coroutine).142143> **Примечание**144>145> Прочитанные данные буферизируются в памяти, поэтому не следует использовать этот метод, если\\nразмер данных велик или неограничен.146147Изменено в версии 3.4.2: Метод теперь игнорирует [`BrokenPipeError`](https://python-all.ru/3.4/library/exceptions.html#BrokenPipeError) и [`ConnectionResetError`](https://python-all.ru/3.4/library/exceptions.html#ConnectionResetError).148149#### `send_signal(signal)`150151Отправляет сигнал *signal* дочернему процессу.152153> **Примечание**154>155> В Windows `SIGTERM` является псевдонимом для [`terminate()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.terminate). `CTRL_C_EVENT` и `CTRL_BREAK_EVENT` могут быть отправлены процессам, запущенным с параметром *creationflags*, который включает `CREATE_NEW_PROCESS_GROUP`.156157#### `terminate()`158159Останавливает дочерний процесс. В ОС Posix метод отправляет `signal.SIGTERM` дочернему процессу. В Windows для остановки вызывается функция Win32 API `TerminateProcess()`.160161#### `kill()`162163Завершает дочерний процесс. В ОС Posix функция отправляет `SIGKILL` дочернему процессу. В Windows [`kill()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.kill) является псевдонимом для [`terminate()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.terminate).164165#### `stdin`166167Поток стандартного ввода ([`StreamWriter`](https://python-all.ru/3.4/library/asyncio-stream.html#asyncio.StreamWriter)), `None`, если процесс был создан с `stdin=None`.168169#### `stdout`170171Поток стандартного вывода ([`StreamReader`](https://python-all.ru/3.4/library/asyncio-stream.html#asyncio.StreamReader)), `None`, если процесс был создан с `stdout=None`.172173#### `stderr`174175Поток стандартного вывода ошибок ([`StreamReader`](https://python-all.ru/3.4/library/asyncio-stream.html#asyncio.StreamReader)), `None`, если процесс был создан с `stderr=None`.176177> **Предупреждение**178>179> Используйте метод [`communicate()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.communicate) вместо [`.stdin.write`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.stdin), [`.stdout.read`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.stdout) или [`.stderr.read`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.stderr), чтобы избежать взаимоблокировок из-за приостановки чтения или записи потоков и блокировки дочернего процесса.180181#### `pid`182183Идентификатор процесса.184185Обратите внимание: для процессов, созданных функцией [`create_subprocess_shell()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_shell), этот атрибут является идентификатором процесса запущенной оболочки.186187#### `returncode`188189Код возврата процесса при его завершении. Значение `None` означает, что процесс ещё не завершён.190191Отрицательное значение `-N` означает, что дочерний процесс был завершён сигналом `N` (только в Unix).192193## 18.5.6.6. Подпроцессы и потоки194195asyncio поддерживает запуск подпроцессов из разных потоков, но есть ограничения:196197- Цикл событий должен выполняться в главном потоке.198- Наблюдатель дочерних процессов должен быть создан в главном потоке до выполнения подпроцессов из других потоков. Вызовите функцию `get_child_watcher()` в главном потоке для создания экземпляра наблюдателя.199200Класс [`asyncio.subprocess.Process`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) не является потокобезопасным.201202> **См. также**203>204> Раздел [*Конкурентность и многопоточность в asyncio*](https://python-all.ru/3.4/library/asyncio-dev.html#asyncio-multithreading).205206## 18.5.6.7. Примеры подпроцессов207208### 18.5.6.7.1. Подпроцесс с использованием транспорта и протокола209210Пример протокола подпроцесса для получения вывода подпроцесса и ожидания его завершения. Подпроцесс создаётся методом [`BaseEventLoop.subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.BaseEventLoop.subprocess_exec):211212```python213import asyncio214import sys215216class DateProtocol(asyncio.SubprocessProtocol):217 def __init__(self, exit_future):218 self.exit_future = exit_future219 self.output = bytearray()220221 def pipe_data_received(self, fd, data):222 self.output.extend(data)223224 def process_exited(self):225 self.exit_future.set_result(True)226227@asyncio.coroutine228def get_date(loop):229 code = 'import datetime; print(datetime.datetime.now())'230 exit_future = asyncio.Future(loop=loop)231232 # Создать подпроцесс, управляемый протоколом DateProtocol,233 # перенаправить стандартный вывод в канал234 create = loop.subprocess_exec(lambda: DateProtocol(exit_future),235 sys.executable, '-c', code,236 stdin=None, stderr=None)237 transport, protocol = yield from create238239 # Ожидать завершения подпроцесса с помощью метода process_exited()240 # протокола241 yield from exit_future242243 # Закрыть канал stdout244 transport.close()245246 # Прочитать вывод, собранный методом pipe_data_received()247 # протокола248 data = bytes(protocol.output)249 return data.decode('ascii').rstrip()250251if sys.platform == "win32":252 loop = asyncio.ProactorEventLoop()253 asyncio.set_event_loop(loop)254else:255 loop = asyncio.get_event_loop()256257date = loop.run_until_complete(get_date(loop))258print("Current date: %s" % date)259loop.close()260```261262### 18.5.6.7.2. Подпроцесс с использованием потоков данных263264Пример использования класса [`Process`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) для управления подпроцессом и класса [`StreamReader`](https://python-all.ru/3.4/library/asyncio-stream.html#asyncio.StreamReader) для чтения стандартного вывода. Подпроцесс создаётся функцией [`create_subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.create_subprocess_exec):265266```python267import asyncio.subprocess268import sys269270@asyncio.coroutine271def get_date():272 code = 'import datetime; print(datetime.datetime.now())'273274 # Создать подпроцесс, перенаправив стандартный вывод в канал275 create = asyncio.create_subprocess_exec(sys.executable, '-c', code,276 stdout=asyncio.subprocess.PIPE)277 proc = yield from create278279 # Прочитать одну строку вывода280 data = yield from proc.stdout.readline()281 line = data.decode('ascii').rstrip()282283 # Ожидать завершения подпроцесса284 yield from proc.wait()285 return line286287if sys.platform == "win32":288 loop = asyncio.ProactorEventLoop()289 asyncio.set_event_loop(loop)290else:291 loop = asyncio.get_event_loop()292293date = loop.run_until_complete(get_date())294print("Current date: %s" % date)295loop.close()296```297