# Підпроцеси

Вихідний код: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py

У цьому розділі описано високорівневі API async/await asyncio для створення та керування підпроцесами.

Ось приклад того, як 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

Оскільки всі функції підпроцесів asyncio є асинхронними, а asyncio надає багато інструментів для роботи з такими функціями, легко виконувати та контролювати декілька підпроцесів паралельно. Дійсно тривіально змінити наведений вище приклад для одночасного запуску кількох команд:

    async def main():
        await asyncio.gather(
            run('ls /zzz'),
            run('sleep 1; echo "hello"'))

    asyncio.run(main())

Дивіться також підрозділ Examples.

## Створення підпроцесів

async asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

  Створіть підпроцес.

  The limit argument sets the buffer limit for `StreamReader` wrappers for `stdout` and `stderr` (if `subprocess.PIPE` is passed to stdout and stderr arguments).

  Повертає екземпляр `Process`.

  Перегляньте документацію `loop.subprocess_exec()` для інших параметрів.

  Змінено в версії 3.10: Видалено параметр loop.

async asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

  Виконайте команду оболонки cmd.

  The limit argument sets the buffer limit for `StreamReader` wrappers for `stdout` and `stderr` (if `subprocess.PIPE` is passed to stdout and stderr arguments).

  Повертає екземпляр `Process`.

  Перегляньте документацію `loop.subprocess_shell()` для інших параметрів.

  Важливо

  Програма несе відповідальність за те, щоб усі пробіли та спеціальні символи були взяті в лапки належним чином, щоб уникнути вразливості впровадження оболонки. Функцію `shlex.quote()` можна використати для правильного екранування пробілів і спеціальних символів оболонки в рядках, які використовуватимуться для створення команд оболонки.

  Змінено в версії 3.10: Видалено параметр loop.

Примітка

Підпроцеси доступні для Windows, якщо використовується `ProactorEventLoop`. Дивіться Підтримку підпроцесів у Windows, щоб дізнатися більше.

Дивись також

asyncio також має такі низькопівневі API для роботи з підпроцесами: `loop.subprocess_exec()`, `loop.subprocess_shell()`, `loop.connect_read_pipe()`, `loop.connect_write_pipe()`, а також Транспорти підпроцесів і Протоколи підпроцесів.

## Константи

asyncio.subprocess.PIPE

  Можна передати в параметри stdin, stdout або stderr.

  If PIPE is passed to stdin argument, the `Process.stdin` attribute will point to a `StreamWriter` instance.

  If PIPE is passed to stdout or stderr arguments, the `Process.stdout` and `Process.stderr` attributes will point to `StreamReader` instances.

asyncio.subprocess.STDOUT

  Спеціальне значення, яке можна використовувати як аргумент stderr і вказує на те, що стандартну помилку слід перенаправляти в стандартний вивід.

asyncio.subprocess.DEVNULL

  Спеціальне значення, яке можна використовувати як аргумент stdin, stdout або stderr для функцій створення процесу. Це вказує, що спеціальний файл `os.devnull` буде використано для відповідного потоку підпроцесу.

## Взаємодія з підпроцесами

Обидві функції `create_subprocess_exec()` і `create_subprocess_shell()` повертають екземпляри класу Process. Process — це високорівнева оболонка, яка дозволяє спілкуватися з підпроцесами та спостерігати за їх завершенням.

class asyncio.subprocess.Process

  An object that wraps OS processes created by the `create_subprocess_exec()` and `create_subprocess_shell()` functions.

  Цей клас розроблено таким чином, щоб мати API, подібний до класу `subprocess.Popen`, але є деякі помітні відмінності:

  - на відміну від Popen, екземпляри Process не мають еквівалента методу `poll()`;

  - the `communicate()` and `wait()` methods don’t have a timeout parameter: use the `wait_for()` function;

  - метод `Process.wait()` є асинхронним, тоді як метод `subprocess.Popen.wait()` реалізовано як блокуючий цикл зайнятості;

  - параметр universal_newlines не підтримується.

  Цей клас не потоково безпечний.

  Дивіться також розділ Підпроцеси та потоки.

  async wait()

    Дочекайтеся завершення дочірнього процесу.

    Установіть і поверніть атрибут `returncode`.

    Примітка

    Цей метод може призвести до взаємоблокування під час використання `stdout=PIPE` або `stderr=PIPE`, і дочірній процес генерує стільки вихідних даних, що він блокує очікування, поки буфер каналу ОС прийме більше даних. Використовуйте метод `communicate()` під час використання каналів, щоб уникнути цієї ситуації.

  async communicate(input=None)

    Взаємодія з процесом:

    1. надсилати дані на stdin (якщо input не `None`);

    2. closes stdin;

    3. читати дані з stdout і stderr, доки не буде досягнуто EOF;

    4. дочекайтеся завершення процесу.

    Додатковий вхідний аргумент — це дані (об’єкт `bytes`), які будуть надіслані дочірньому процесу.

    Повертає кортеж `(stdout_data, stderr_data)`.

    Якщо під час запису input у stdin виникає виняткова ситуація `BrokenPipeError` або `ConnectionResetError`, виняток ігнорується. Ця умова виникає, коли процес завершується до того, як усі дані будуть записані в stdin.

    Якщо потрібно надіслати дані процесу stdin, процес потрібно створити за допомогою `stdin=PIPE`. Подібним чином, щоб отримати в кортежі результатів щось інше, окрім `None`, процес має бути створений з аргументами `stdout=PIPE` та/або `stderr=PIPE`.

    Зауважте, що зчитані дані буферизуються в пам’яті, тому не використовуйте цей метод, якщо розмір даних великий або необмежений.

    Змінено в версії 3.12: stdin gets closed when `input=None` too.

  send_signal(signal)

    Надсилає сигнал signal дочірньому процесу.

    Примітка

    On Windows, `SIGTERM` is an alias for `terminate()`. `CTRL_C_EVENT` and `CTRL_BREAK_EVENT` can be sent to processes started with a creationflags parameter which includes `CREATE_NEW_PROCESS_GROUP`.

  terminate()

    Зупиніть дочірній процес.

    On POSIX systems this method sends `SIGTERM` to the child process.

    On Windows the Win32 API function `TerminateProcess()` is called to stop the child process.

  kill()

    Закрийте дочірній процес.

    On POSIX systems this method sends `SIGKILL` to the child process.

    У Windows цей метод є псевдонімом для `terminate()`.

  stdin

    Standard input stream (`StreamWriter`) or `None` if the process was created with `stdin=None`.

  stdout

    Standard output stream (`StreamReader`) or `None` if the process was created with `stdout=None`.

  stderr

    Standard error stream (`StreamReader`) or `None` if the process was created with `stderr=None`.

  Попередження

  Використовуйте метод `communicate()` замість `process.stdin.write()`, `await process.stdout.read()` або `await process.stderr.read ()`. Це дозволяє уникнути взаємоблокувань через те, що потоки призупиняють читання або запис і блокують дочірній процес.

  pid

    Ідентифікаційний номер процесу (PID).

    Note that for processes created by the `create_subprocess_shell()` function, this attribute is the PID of the spawned shell.

  returncode

    Код повернення процесу під час його завершення.

    Значення `None` вказує на те, що процес ще не завершено.

    Від’ємне значення `-N` вказує на те, що дочірній елемент було завершено сигналом `N` (лише POSIX).

### Підпроцеси та потоки

Стандартний асинхронний цикл подій за замовчуванням підтримує виконання підпроцесів з різних потоків.

У Windows підпроцеси надаються лише `ProactorEventLoop` (за замовчуванням), `SelectorEventLoop` не підтримує підпроцеси.

Зауважте, що альтернативні реалізації циклу подій можуть мати власні обмеження; зверніться до їх документації.

Дивись також

Розділ Параллельність і багатопотоковість в asyncio.

### Приклади

Приклад використання класу `Process` для керування підпроцесом і класу `StreamReader` для читання зі стандартного виводу.

Підпроцес створюється функцією `create_subprocess_exec()`:

    import asyncio
    import sys

    async def get_date():
        code = 'import datetime; print(datetime.datetime.now())'

        # Create the subprocess; redirect the standard output
        # into a pipe.
        proc = await asyncio.create_subprocess_exec(
            sys.executable, '-c', code,
            stdout=asyncio.subprocess.PIPE)

        # Read one line of output.
        data = await proc.stdout.readline()
        line = data.decode('ascii').rstrip()

        # Wait for the subprocess exit.
        await proc.wait()
        return line

    date = asyncio.run(get_date())
    print(f"Current date: {date}")

Дивіться також той же приклад, написаний з використанням API низького рівня.