> **Источник:** https://python-all.ru/3.4/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.4/library/asyncio-eventloops.html#asyncio.SelectorEventLoop), который не поддерживает подпроцессы. Вместо него следует использовать [`ProactorEventLoop`](https://python-all.ru/3.4/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.4/library/asyncio-eventloops.html#asyncio-event-loops) и [*Поддержка платформ*](https://python-all.ru/3.4/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.4/library/asyncio-stream.html#asyncio.StreamReader). Другие параметры описаны в [`BaseEventLoop.subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.BaseEventLoop.subprocess_exec).

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

Эта функция – [*корутина*](https://python-all.ru/3.4/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.4/library/asyncio-stream.html#asyncio.StreamReader). Другие параметры описаны в [`BaseEventLoop.subprocess_shell()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.BaseEventLoop.subprocess_shell).

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

Приложение должно самостоятельно гарантировать, что все пробелы и метасимволы экранированы надлежащим образом, чтобы избежать уязвимостей [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) позволяет корректно экранировать пробелы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Приложение должно самостоятельно гарантировать, что все пробелы и метасимволы экранированы надлежащим образом, чтобы избежать уязвимостей [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) позволяет корректно экранировать пробелы и метасимволы оболочки в строках, которые будут использоваться для построения команд оболочки.

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

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

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

#### `asyncio.subprocess.PIPE`

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

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

#### `asyncio.subprocess.DEVNULL`

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

## 18.5.6.5. Процесс

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

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

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

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

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

#### `coroutine wait()`

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

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

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

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

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

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

Если при записи *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.

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

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

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

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

#### `send_signal(signal)`

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

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

#### `terminate()`

Останавливает дочерний процесс. В ОС Posix метод отправляет `signal.SIGTERM` дочернему процессу. В Windows для остановки вызывается функция Win32 API `TerminateProcess()`.

#### `kill()`

Завершает дочерний процесс. В ОС 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).

#### `stdin`

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

#### `stdout`

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

#### `stderr`

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

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

#### `pid`

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

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

#### `returncode`

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

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

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

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

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

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

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

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

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

Пример протокола подпроцесса для получения вывода подпроцесса и ожидания его завершения. Подпроцесс создаётся методом [`BaseEventLoop.subprocess_exec()`](https://python-all.ru/3.4/library/asyncio-subprocess.html#asyncio.BaseEventLoop.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.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):

```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()
```
