Підпроцеси¶
Вихідний код: 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.
Створення підпроцесів¶
- coroutine 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 forProcess.stdout
andProcess.stderr
(ifsubprocess.PIPE
is passed to stdout and stderr arguments).Повертає екземпляр
Process
.Перегляньте документацію
loop.subprocess_exec()
для інших параметрів.Змінено в версії 3.10: Видалено параметр loop.
- coroutine 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 forProcess.stdout
andProcess.stderr
(ifsubprocess.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.
Якщо PIPE передано аргументу stdin, атрибут
Process.stdin
вказуватиме на примірникStreamWriter
.Якщо PIPE передано аргументам stdout або stderr, атрибути
Process.stdout
іProcess.stderr
вказуватимуть на екземпляриStreamReader
.
- asyncio.subprocess.STDOUT¶
Спеціальне значення, яке можна використовувати як аргумент stderr і вказує на те, що стандартну помилку слід перенаправляти в стандартний вивід.
- asyncio.subprocess.DEVNULL¶
Спеціальне значення, яке можна використовувати як аргумент stdin, stdout або stderr для функцій створення процесу. Це вказує, що спеціальний файл
os.devnull
буде використано для відповідного потоку підпроцесу.
Взаємодія з підпроцесами¶
Обидві функції create_subprocess_exec()
і create_subprocess_shell()
повертають екземпляри класу Process. Process — це високорівнева оболонка, яка дозволяє спілкуватися з підпроцесами та спостерігати за їх завершенням.
- class asyncio.subprocess.Process¶
Об’єкт, який обгортає процеси ОС, створені функціями
create_subprocess_exec()
іcreate_subprocess_shell()
.Цей клас розроблено таким чином, щоб мати API, подібний до класу
subprocess.Popen
, але є деякі помітні відмінності:на відміну від Popen, екземпляри Process не мають еквівалента методу
poll()
;the
communicate()
andwait()
methods don’t have a timeout parameter: use thewait_for()
function;метод
Process.wait()
є асинхронним, тоді як методsubprocess.Popen.wait()
реалізовано як блокуючий цикл зайнятості;параметр universal_newlines не підтримується.
Цей клас не потоково безпечний.
Дивіться також розділ Підпроцеси та потоки.
- coroutine wait()¶
Дочекайтеся завершення дочірнього процесу.
Установіть і поверніть атрибут
returncode
.Примітка
Цей метод може призвести до взаємоблокування під час використання
stdout=PIPE
абоstderr=PIPE
, і дочірній процес генерує стільки вихідних даних, що він блокує очікування, поки буфер каналу ОС прийме більше даних. Використовуйте методcommunicate()
під час використання каналів, щоб уникнути цієї ситуації.
- coroutine communicate(input=None)¶
Взаємодія з процесом:
надсилати дані на stdin (якщо input не
None
);читати дані з stdout і stderr, доки не буде досягнуто EOF;
дочекайтеся завершення процесу.
Додатковий вхідний аргумент — це дані (об’єкт
bytes
), які будуть надіслані дочірньому процесу.Повертає кортеж
(stdout_data, stderr_data)
.Якщо під час запису input у stdin виникає виняткова ситуація
BrokenPipeError
абоConnectionResetError
, виняток ігнорується. Ця умова виникає, коли процес завершується до того, як усі дані будуть записані в stdin.Якщо потрібно надіслати дані процесу stdin, процес потрібно створити за допомогою
stdin=PIPE
. Подібним чином, щоб отримати в кортежі результатів щось інше, окрімNone
, процес має бути створений з аргументамиstdout=PIPE
та/абоstderr=PIPE
.Зауважте, що зчитані дані буферизуються в пам’яті, тому не використовуйте цей метод, якщо розмір даних великий або необмежений.
- send_signal(signal)¶
Надсилає сигнал signal дочірньому процесу.
Примітка
On Windows,
SIGTERM
is an alias forterminate()
.CTRL_C_EVENT
andCTRL_BREAK_EVENT
can be sent to processes started with a creationflags parameter which includesCREATE_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()¶
Закрийте дочірній процес.
У системах POSIX цей метод надсилає
SIGKILL
дочірньому процесу.У Windows цей метод є псевдонімом для
terminate()
.
- stdin¶
Стандартний вхідний потік (
StreamWriter
) абоNone
, якщо процес було створено за допомогоюstdin=None
.
- stdout¶
Стандартний вихідний потік (
StreamReader
) абоNone
, якщо процес було створено за допомогоюstdout=None
.
- stderr¶
Стандартний потік помилок (
StreamReader
) абоNone
, якщо процес було створено за допомогоюstderr=None
.
Попередження
Використовуйте метод
communicate()
замістьprocess.stdin.write()
,await process.stdout.read()
абоawait process.stderr.read ()
. Це дозволяє уникнути взаємоблокувань через те, що потоки призупиняють читання або запис і блокують дочірній процес.- pid¶
Ідентифікаційний номер процесу (PID).
Зауважте, що для процесів, створених функцією
create_subprocess_shell()
, цей атрибут є PID створеної оболонки.
- returncode¶
Код повернення процесу під час його завершення.
Значення
None
вказує на те, що процес ще не завершено.Від’ємне значення
-N
вказує на те, що дочірній елемент було завершено сигналомN
(лише POSIX).
Підпроцеси та потоки¶
Стандартний асинхронний цикл подій за замовчуванням підтримує виконання підпроцесів з різних потоків.
У Windows підпроцеси надаються лише ProactorEventLoop
(за замовчуванням), SelectorEventLoop
не підтримує підпроцеси.
В UNIX child watchers використовуються для очікування завершення підпроцесу, див. Спостерігачі процесів для отримання додаткової інформації.
Змінено в версії 3.8: UNIX перейшла на використання ThreadedChildWatcher
для створення підпроцесів з різних потоків без будь-яких обмежень.
Створення підпроцесу з неактивним поточним дочірнім спостерігачем викликає RuntimeError
.
Зауважте, що альтернативні реалізації циклу подій можуть мати власні обмеження; зверніться до їх документації.
Дивись також
Приклади¶
Приклад використання класу 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 низького рівня.