> **Источник:** https://python-all.ru/3.5/library/asyncio-subprocess.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# 18.5.6. Подпроцесс

## 18.5.6.1. Цикл событий Windows

В 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:

```python
import asyncio, sys

if sys.platform == 'win32':
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
```

> **См. также**
>
> [Доступные циклы событий](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).

## 18.5.6.2. Создание подпроцесса: высокоуровневый API с использованием процесса

#### `coroutine asyncio.create_subprocess_exec(*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)`

Создание подпроцесса.

Параметр *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).

Возвращает экземпляр [`Process`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process).

Эта функция – [корутина](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).

#### `coroutine asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)`

Выполняет команду оболочки *cmd*.

Параметр *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).

Возвращает экземпляр [`Process`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process).

Приложение должно гарантировать, что все пробельные символы и метасимволы правильно экранированы, чтобы избежать уязвимостей типа [внедрение команд оболочки](https://python-all.ru/3.5/library/asyncio-subprocess.html). Функция [`shlex.quote()`](https://python-all.ru/3.5/library/shlex.html#shlex.quote) позволяет правильно экранировать пробельные символы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.

Эта функция – [корутина](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).

Используйте методы [`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) для подключения каналов.

## 18.5.6.3. Создание подпроцесса: низкоуровневый API с использованием subprocess.Popen

Асинхронный запуск подпроцессов с помощью модуля [`subprocess`](https://python-all.ru/3.5/library/subprocess.html#module-subprocess).

#### `coroutine AbstractEventLoop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)`

Создание подпроцесса из одного или нескольких строковых аргументов (строки символов или байтовые строки, закодированные в [кодировку файловой системы](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) принимает несколько строковых аргументов.

Параметр *protocol\_factory* должен создавать экземпляр подкласса класса [`asyncio.SubprocessProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.SubprocessProtocol).

Другие параметры:

- *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) (по умолчанию). По умолчанию создаётся и подключается новый канал.
- *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) (по умолчанию). По умолчанию создаётся и подключается новый канал.
- *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), стандартный поток ошибок подпроцесса будет подключён к тому же каналу, что и стандартный поток вывода.
- Все остальные именованные аргументы передаются в [`subprocess.Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen) без интерпретации, за исключением *bufsize*, *universal\_newlines* и *shell*, которые не следует указывать вовсе.

Возвращает пару `(transport, protocol)`, где *транспорт* – экземпляр [`BaseSubprocessTransport`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport).

Этот метод является [корутиной](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).

Параметры см. в конструкторе класса [`subprocess.Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen).

#### `coroutine AbstractEventLoop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)`

Создание подпроцесса из *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`.

Параметр *protocol\_factory* должен создавать экземпляр подкласса класса [`asyncio.SubprocessProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.SubprocessProtocol).

Подробнее об остальных аргументах см. в [`subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.AbstractEventLoop.subprocess_exec).

Возвращает пару `(transport, protocol)`, где *транспорт* – экземпляр [`BaseSubprocessTransport`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport).

Приложение должно гарантировать, что все пробельные символы и метасимволы правильно экранированы, чтобы избежать уязвимостей типа [внедрение команд оболочки](https://python-all.ru/3.5/library/asyncio-subprocess.html). Функция [`shlex.quote()`](https://python-all.ru/3.5/library/shlex.html#shlex.quote) позволяет правильно экранировать пробельные символы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.

Этот метод является [корутиной](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).

> **См. также**
>
> Методы [`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).

## 18.5.6.4. Константы

#### `asyncio.subprocess.PIPE`

Специальное значение, которое можно использовать в качестве аргумента *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указывающее, что должен быть открыт канал к стандартному потоку.

#### `asyncio.subprocess.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указывающее, что стандартный поток ошибок должен направляться в тот же дескриптор, что и стандартный вывод.

#### `asyncio.subprocess.DEVNULL`

Специальное значение, которое можно использовать в качестве аргумента *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).

## 18.5.6.5. Процесс

#### `class asyncio.subprocess.Process`

Подпроцесс, созданный функцией [`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).

API класса [`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), но есть некоторые различия:

- Нет явного метода [`poll()`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen.poll)
- Методы [`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)
- Параметр *universal\_newlines* не поддерживается (поддерживаются только строки байтов)
- Метод [`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реализован как активный цикл ожидания.

Этот класс [не потокобезопасен](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).

#### `coroutine wait()`

Ожидать завершения дочернего процесса. Установить и вернуть атрибут [`returncode`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.returncode).

Этот метод является [корутиной](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).

> **Примечание**
>
> Это приведёт к взаимоблокировке при использовании `stdout=PIPE` или `stderr=PIPE`, если\\nдочерний процесс генерирует достаточно выходных данных в канал, так что он\\nблокируется в ожидании, когда буфер канала ОС сможет принять больше данных. Используйте\\nметод [`communicate()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.communicate) при работе с каналами, чтобы избежать этого.

#### `coroutine communicate(input=None)`

Взаимодействие с процессом: отправить данные в стандартный ввод. Читать данные из стандартного вывода и стандартного вывода ошибок до достижения конца файла. Ожидать завершения процесса. Необязательный аргумент *input* должен содержать данные для отправки дочернему процессу, или `None`, если данные не нужно отправлять. Тип *input* должен быть байтовым.

[`communicate()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.communicate) возвращает кортеж `(stdout_data, stderr_data)`.

Если исключение [`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.

Обратите внимание: если требуется отправить данные в stdin процесса, необходимо создать объект процесса с `stdin=PIPE`. Аналогично, чтобы получить в результирующем кортеже что-то кроме `None`, нужно также указать `stdout=PIPE` и/или `stderr=PIPE`.

Этот метод является [корутиной](https://python-all.ru/3.5/library/asyncio-task.html#coroutine).

> **Примечание**
>
> Прочитанные данные буферизируются в памяти, поэтому не следует использовать этот метод, если\\nразмер данных велик или неограничен.

Изменено в версии 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).

#### `send_signal(signal)`

Отправляет сигнал *signal* дочернему процессу.

> **Примечание**
>
> В 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`.

#### `terminate()`

Остановить дочерний процесс. В POSIX-системах метод отправляет `signal.SIGTERM`\\nдочернему процессу. В Windows для остановки дочернего процесса вызывается функция Win32 API\\n`TerminateProcess()`.

#### `kill()`

Убить дочерний процесс. В 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).

#### `stdin`

Поток стандартного ввода ([`StreamWriter`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.StreamWriter)), `None` если процесс\\nбыл создан с `stdin=None`.

#### `stdout`

Поток стандартного вывода ([`StreamReader`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.StreamReader)), `None` если процесс\\nбыл создан с `stdout=None`.

#### `stderr`

Поток стандартного вывода ошибок ([`StreamReader`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.StreamReader)), `None` если процесс\\nбыл создан с `stderr=None`.

> **Предупреждение**
>
> Используйте метод [`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дочернего процесса.

#### `pid`

Идентификатор процесса.

Обратите внимание: для процессов, созданных функцией [`create_subprocess_shell()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.create_subprocess_shell),\\nэтот атрибут является идентификатором процесса запущенной оболочки.

#### `returncode`

Код возврата процесса при его завершении. Значение `None` указывает,\\nчто процесс ещё не завершён.

Отрицательное значение `-N` указывает, что дочерний процесс был завершён сигналом\\n`N` (только Unix).

## 18.5.6.6. Подпроцессы и потоки

asyncio поддерживает запуск подпроцессов из разных потоков, но есть ограничения:

- Цикл событий должен выполняться в главном потоке.
- Дочерний наблюдатель должен быть создан в главном потоке до запуска подпроцессов из других потоков. Вызовите функцию `get_child_watcher()` в главном потоке для создания дочернего наблюдателя.

Класс [`asyncio.subprocess.Process`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) не является потокобезопасным.

> **См. также**
>
> Раздел [Конкурентность и многопоточность в asyncio](https://python-all.ru/3.5/library/asyncio-dev.html#asyncio-multithreading).

## 18.5.6.7. Примеры подпроцессов

### 18.5.6.7.1. Подпроцесс с использованием транспорта и протокола

Пример протокола подпроцесса, используемого для получения вывода подпроцесса и ожидания его завершения. Подпроцесс создаётся методом [`AbstractEventLoop.subprocess_exec()`](https://python-all.ru/3.5/library/asyncio-subprocess.html#asyncio.AbstractEventLoop.subprocess_exec):

```python
import asyncio
import sys

class DateProtocol(asyncio.SubprocessProtocol):
    def __init__(self, exit_future):
        self.exit_future = exit_future
        self.output = bytearray()

    def pipe_data_received(self, fd, data):
        self.output.extend(data)

    def process_exited(self):
        self.exit_future.set_result(True)

@asyncio.coroutine
def get_date(loop):
    code = 'import datetime; print(datetime.datetime.now())'
    exit_future = asyncio.Future(loop=loop)

    # Создать подпроцесс, управляемый протоколом DateProtocol,
    # перенаправить стандартный вывод в канал
    create = loop.subprocess_exec(lambda: DateProtocol(exit_future),
                                  sys.executable, '-c', code,
                                  stdin=None, stderr=None)
    transport, protocol = yield from create

    # Ожидать завершения подпроцесса с помощью метода process_exited()
    # протокола
    yield from exit_future

    # Закрыть канал stdout
    transport.close()

    # Прочитать вывод, собранный методом pipe_data_received()
    # протокола
    data = bytes(protocol.output)
    return data.decode('ascii').rstrip()

if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

date = loop.run_until_complete(get_date(loop))
print("Current date: %s" % date)
loop.close()
```

### 18.5.6.7.2. Подпроцесс с использованием потоков данных

Пример использования класса [`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):

```python
import asyncio.subprocess
import sys

@asyncio.coroutine
def get_date():
    code = 'import datetime; print(datetime.datetime.now())'

    # Создать подпроцесс, перенаправив стандартный вывод в канал
    create = asyncio.create_subprocess_exec(sys.executable, '-c', code,
                                            stdout=asyncio.subprocess.PIPE)
    proc = yield from create

    # Прочитать одну строку вывода
    data = yield from proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Ожидать завершения подпроцесса
    yield from proc.wait()
    return line

if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

date = loop.run_until_complete(get_date())
print("Current date: %s" % date)
loop.close()
```
