Документация Python неофициальный перевод

asyncio-subprocess.md

297 строк · 27.6 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.5/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.5/library/asyncio-eventloops.html#asyncio.SelectorEventLoop), который не поддерживает подпроцессы. Вместо него следует использовать [`ProactorEventLoop`](https://python-all.ru/3.5/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.5/library/asyncio-eventloops.html#asyncio-event-loops) и [Поддержка платформ](https://python-all.ru/3.5/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.5/library/asyncio-stream.html#asyncio.StreamReader). Дополнительные параметры см. в [`AbstractEventLoop.subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.AbstractEventLoop.subprocess_exec).3233Возвращает экземпляр [`Process`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process).3435Эта функция – [корутина](https://python-all.ru/3.5/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.5/library/asyncio-stream.html#asyncio.StreamReader). Дополнительные параметры см. в [`AbstractEventLoop.subprocess_shell()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.AbstractEventLoop.subprocess_shell).4243Возвращает экземпляр [`Process`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process).4445Приложение должно гарантировать, что все пробельные символы и метасимволы правильно экранированы, чтобы избежать уязвимостей типа [внедрение команд оболочки](https://python-all.ru/3.5/library/asyncio-subprocess.html). Функция [`shlex.quote()`](https://python-all.ru/3.5/library/shlex.html#shlex.quote) позволяет правильно экранировать пробельные символы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.4647Эта функция – [корутина](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).4849Используйте методы [`AbstractEventLoop.connect_read_pipe()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.connect_read_pipe) и [`AbstractEventLoop.connect_write_pipe()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.connect_write_pipe) для подключения каналов.5051## 18.5.6.3. Создание подпроцесса: низкоуровневый API с использованием subprocess.Popen5253Асинхронный запуск подпроцессов с помощью модуля [`subprocess`](https://python-all.ru/3.5/library/subprocess.html#module-subprocess).5455#### `coroutine AbstractEventLoop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)`5657Создание подпроцесса из одного или нескольких строковых аргументов (строки символов или байтовые строки, закодированные в [кодировку файловой системы](https://python-all.ru/3.5/library/os.html#filesystem-encoding)), где первая строка определяет выполняемую программу, а остальные строки – её аргументы. (Таким образом, вместе строковые аргументы образуют значение `sys.argv` программы, если предположить, что это скрипт Python.) Это похоже на класс [`subprocess.Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen) из стандартной библиотеки, вызываемый с shell=False и списком строк, передаваемым в качестве первого аргумента; однако [`Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen) принимает один аргумент – список строк, а [`subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.AbstractEventLoop.subprocess_exec) принимает несколько строковых аргументов.5859Параметр *protocol\_factory* должен создавать экземпляр подкласса класса [`asyncio.SubprocessProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.SubprocessProtocol).6061Другие параметры:6263- *stdin*: либо файлоподобный объект, представляющий канал, который будет подключён к стандартному потоку ввода подпроцесса с помощью [`connect_write_pipe()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.connect_write_pipe), либо константа [`subprocess.PIPE`](https://python-all.ru/3.5/library/subprocess.html#subprocess.PIPE) (по умолчанию). По умолчанию создаётся и подключается новый канал.64- *stdout*: либо файлоподобный объект, представляющий канал, который будет подключён к стандартному потоку вывода подпроцесса с помощью [`connect_read_pipe()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.connect_read_pipe), либо константа [`subprocess.PIPE`](https://python-all.ru/3.5/library/subprocess.html#subprocess.PIPE) (по умолчанию). По умолчанию создаётся и подключается новый канал.65- *stderr*: либо файлоподобный объект, представляющий канал, который будет подключён к стандартному потоку ошибок подпроцесса с помощью [`connect_read_pipe()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.connect_read_pipe), либо одна из констант [`subprocess.PIPE`](https://python-all.ru/3.5/library/subprocess.html#subprocess.PIPE) (по умолчанию) или [`subprocess.STDOUT`](https://python-all.ru/3.5/library/subprocess.html#subprocess.STDOUT). По умолчанию создаётся и подключается новый канал. Если указано [`subprocess.STDOUT`](https://python-all.ru/3.5/library/subprocess.html#subprocess.STDOUT), стандартный поток ошибок подпроцесса будет подключён к тому же каналу, что и стандартный поток вывода.66- Все остальные именованные аргументы передаются в [`subprocess.Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen) без интерпретации, за исключением *bufsize*, *universal\_newlines* и *shell*, которые не следует указывать вовсе.6768Возвращает пару `(transport, protocol)`, где *транспорт* – экземпляр [`BaseSubprocessTransport`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport).6970Этот метод является [корутиной](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).7172Параметры см. в конструкторе класса [`subprocess.Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen).7374#### `coroutine AbstractEventLoop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)`7576Создание подпроцесса из *cmd* – строки символов или байтовой строки, закодированной в [кодировку файловой системы](https://python-all.ru/3.5/library/os.html#filesystem-encoding), с использованием синтаксиса оболочки платформы. Это похоже на класс [`subprocess.Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen) из стандартной библиотеки, вызываемый с `shell=True`.7778Параметр *protocol\_factory* должен создавать экземпляр подкласса класса [`asyncio.SubprocessProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.SubprocessProtocol).7980Подробнее об остальных аргументах см. в [`subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.AbstractEventLoop.subprocess_exec).8182Возвращает пару `(transport, protocol)`, где *транспорт* – экземпляр [`BaseSubprocessTransport`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport).8384Приложение должно гарантировать, что все пробельные символы и метасимволы правильно экранированы, чтобы избежать уязвимостей типа [внедрение команд оболочки](https://python-all.ru/3.5/library/asyncio-subprocess.html). Функция [`shlex.quote()`](https://python-all.ru/3.5/library/shlex.html#shlex.quote) позволяет правильно экранировать пробельные символы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.8586Этот метод является [корутиной](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).8788> **См. также**89>90> Методы [`AbstractEventLoop.connect_read_pipe()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.connect_read_pipe) и [`AbstractEventLoop.connect_write_pipe()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.connect_write_pipe).9192## 18.5.6.4. Константы9394#### `asyncio.subprocess.PIPE`9596Специальное значение, которое можно использовать в качестве аргумента *stdin*, *stdout* или *stderr*\\nдля [`create_subprocess_shell()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_shell) и [`create_subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_exec), и\\nуказывающее, что должен быть открыт канал к стандартному потоку.9798#### `asyncio.subprocess.STDOUT`99100Специальное значение, которое можно использовать в качестве аргумента *stderr* для\\n[`create_subprocess_shell()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_shell) и [`create_subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_exec), и\\nуказывающее, что стандартный поток ошибок должен направляться в тот же дескриптор, что и стандартный вывод.101102#### `asyncio.subprocess.DEVNULL`103104Специальное значение, которое можно использовать в качестве аргумента *stdin*, *stdout* или *stderr*\\nдля [`create_subprocess_shell()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_shell) и [`create_subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_exec), и\\nуказывающее, что будет использоваться специальный файл [`os.devnull`](https://python-all.ru/3.5/library/os.html#os.devnull).105106## 18.5.6.5. Процесс107108#### `class asyncio.subprocess.Process`109110Подпроцесс, созданный функцией [`create_subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_exec) или\\n[`create_subprocess_shell()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_shell).111112API класса [`Process`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) разработан так, чтобы быть близким к API класса [`subprocess.Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen), но есть некоторые различия:113114- Нет явного метода [`poll()`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen.poll)115- Методы [`communicate()`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen.communicate) и\\n[`wait()`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen.wait) не принимают параметр *timeout*:\\nиспользуйте функцию [`wait_for()`](https://python-all.ru/3.5/library/asyncio-task.html#asyncio.wait_for)116- Параметр *universal\_newlines* не поддерживается (поддерживаются только строки байтов)117- Метод [`wait()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.wait) класса\\n[`Process`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) является асинхронным, тогда как\\nметод [`wait()`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen.wait) класса [`Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen)\\nреализован как активный цикл ожидания.118119Этот класс [не потокобезопасен](https://python-all.ru/3.5/library/asyncio-dev.html#asyncio-multithreading). См. также раздел\\n[Подпроцессы и потоки](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio-subprocess-threads).120121#### `coroutine wait()`122123Ожидать завершения дочернего процесса. Установить и вернуть атрибут [`returncode`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.returncode).124125Этот метод является [корутиной](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).126127> **Примечание**128>129> Это приведёт к взаимоблокировке при использовании `stdout=PIPE` или `stderr=PIPE`, если\\nдочерний процесс генерирует достаточно выходных данных в канал, так что он\\nблокируется в ожидании, когда буфер канала ОС сможет принять больше данных. Используйте\\nметод [`communicate()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.communicate) при работе с каналами, чтобы избежать этого.130131#### `coroutine communicate(input=None)`132133Взаимодействие с процессом: отправить данные в стандартный ввод. Читать данные из стандартного вывода и стандартного вывода ошибок до достижения конца файла. Ожидать завершения процесса. Необязательный аргумент *input* должен содержать данные для отправки дочернему процессу, или `None`, если данные не нужно отправлять. Тип *input* должен быть байтовым.134135[`communicate()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.communicate) возвращает кортеж `(stdout_data, stderr_data)`.136137Если исключение [`BrokenPipeError`](https://python-all.ru/3.5/library/exceptions.html#BrokenPipeError) или [`ConnectionResetError`](https://python-all.ru/3.5/library/exceptions.html#ConnectionResetError) возникает\\nпри записи *input* в stdin, это исключение игнорируется. Оно\\nвозникает, когда процесс завершается до того, как все данные будут записаны в stdin.138139Обратите внимание: если требуется отправить данные в stdin процесса, необходимо создать объект процесса с `stdin=PIPE`. Аналогично, чтобы получить в результирующем кортеже что-то кроме `None`, нужно также указать `stdout=PIPE` и/или `stderr=PIPE`.140141Этот метод является [корутиной](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).142143> **Примечание**144>145> Прочитанные данные буферизируются в памяти, поэтому не следует использовать этот метод, если\\nразмер данных велик или неограничен.146147Изменено в версии 3.4.2: Метод теперь игнорирует [`BrokenPipeError`](https://python-all.ru/3.5/library/exceptions.html#BrokenPipeError) и\\n[`ConnectionResetError`](https://python-all.ru/3.5/library/exceptions.html#ConnectionResetError).148149#### `send_signal(signal)`150151Отправляет сигнал *signal* дочернему процессу.152153> **Примечание**154>155> В Windows `SIGTERM` является псевдонимом для [`terminate()`](https://python-all.ru/3.5/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`\\nдочернему процессу. В Windows для остановки дочернего процесса вызывается функция Win32 API\\n`TerminateProcess()`.160161#### `kill()`162163Убить дочерний процесс. В POSIX-системах функция отправляет `SIGKILL`\\nдочернему процессу. В Windows [`kill()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.kill) является псевдонимом для [`terminate()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.terminate).164165#### `stdin`166167Поток стандартного ввода ([`StreamWriter`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.StreamWriter)), `None` если процесс\\nбыл создан с `stdin=None`.168169#### `stdout`170171Поток стандартного вывода ([`StreamReader`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.StreamReader)), `None` если процесс\\nбыл создан с `stdout=None`.172173#### `stderr`174175Поток стандартного вывода ошибок ([`StreamReader`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.StreamReader)), `None` если процесс\\nбыл создан с `stderr=None`.176177> **Предупреждение**178>179> Используйте метод [`communicate()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.communicate) вместо [`.stdin.write`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.stdin), [`.stdout.read`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.stdout) или [`.stderr.read`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.stderr),\\nчтобы избежать взаимоблокировок из-за приостановки чтения или записи потоков и блокировки\\nдочернего процесса.180181#### `pid`182183Идентификатор процесса.184185Обратите внимание: для процессов, созданных функцией [`create_subprocess_shell()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_shell),\\nэтот атрибут является идентификатором процесса запущенной оболочки.186187#### `returncode`188189Код возврата процесса при его завершении. Значение `None` указывает,\\nчто процесс ещё не завершён.190191Отрицательное значение `-N` указывает, что дочерний процесс был завершён сигналом\\n`N` (только Unix).192193## 18.5.6.6. Подпроцессы и потоки194195asyncio поддерживает запуск подпроцессов из разных потоков, но есть ограничения:196197- Цикл событий должен выполняться в главном потоке.198- Дочерний наблюдатель должен быть создан в главном потоке до запуска подпроцессов из других потоков. Вызовите функцию `get_child_watcher()` в главном потоке для создания дочернего наблюдателя.199200Класс [`asyncio.subprocess.Process`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) не является потокобезопасным.201202> **См. также**203>204> Раздел [Конкурентность и многопоточность в asyncio](https://python-all.ru/3.5/library/asyncio-dev.html#asyncio-multithreading).205206## 18.5.6.7. Примеры подпроцессов207208### 18.5.6.7.1. Подпроцесс с использованием транспорта и протокола209210Пример протокола подпроцесса, используемого для получения вывода подпроцесса и ожидания его завершения. Подпроцесс создаётся методом [`AbstractEventLoop.subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.AbstractEventLoop.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.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) для управления подпроцессом и класса [`StreamReader`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.StreamReader) для чтения из стандартного вывода. Подпроцесс создаётся функцией [`create_subprocess_exec()`](https://python-all.ru/3.5/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