Підпроцеси¶
Вихідний код: 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, loop=None, limit=None, **kwds)¶ Створіть підпроцес.
The limit argument sets the buffer limit for
StreamReaderwrappers forProcess.stdoutandProcess.stderr(ifsubprocess.PIPEis passed to stdout and stderr arguments).Повертає екземпляр
Process.Перегляньте документацію
loop.subprocess_exec()для інших параметрів.Deprecated since version 3.8, will be removed in version 3.10: The loop parameter.
-
coroutine
asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)¶ Виконайте команду оболонки cmd.
The limit argument sets the buffer limit for
StreamReaderwrappers forProcess.stdoutandProcess.stderr(ifsubprocess.PIPEis passed to stdout and stderr arguments).Повертає екземпляр
Process.Перегляньте документацію
loop.subprocess_shell()для інших параметрів.Важливо
Програма несе відповідальність за те, щоб усі пробіли та спеціальні символи були взяті в лапки належним чином, щоб уникнути вразливості впровадження оболонки. Функцію
shlex.quote()можна використати для правильного екранування пробілів і спеціальних символів оболонки в рядках, які використовуватимуться для створення команд оболонки.Deprecated since version 3.8, will be removed in version 3.10: The loop parameter.
Примітка
Підпроцеси доступні для 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,
SIGTERMis an alias forterminate().CTRL_C_EVENTandCTRL_BREAK_EVENTcan be sent to processes started with a creationflags parameter which includesCREATE_NEW_PROCESS_GROUP.
-
terminate()¶ Зупиніть дочірній процес.
On POSIX systems this method sends
signal.SIGTERMto 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.
Попередження
Use the
communicate()method rather thanprocess.stdin.write(),await process.stdout.read()orawait process.stderr.read. This avoids deadlocks due to streams pausing reading or writing and blocking the child process.-
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 низького рівня.