زیرفرایندها¶
کد منبع: 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=65536, **kwds)¶
ایجاد یک زیرفرایند.
آرگومان limit محدودیت بافر را برای دربرگیرندههای
StreamReaderمربوط بهstdoutوstderrتنظیم میکند (اگرsubprocess.PIPEبه آرگومانهای stdout و stderr ارسال شود).یک نمونه از
Processبرمیگرداند.برای سایر پارامترها، مستندات
loop.subprocess_exec()را ببینید.اگر شیء فرایند در حالی که فرایند هنوز در حال اجرا است زبالهروبی شود، فرایند فرزند خاتمه داده خواهد شد.
تغییر یافته در نسخهی 3.10: پارامتر loop حذف شد.
- async asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, limit=65536, **kwds)¶
دستور پوستهی cmd را اجرا کنید.
آرگومان limit محدودیت بافر را برای دربرگیرندههای
StreamReaderمربوط بهstdoutوstderrتنظیم میکند (اگرsubprocess.PIPEبه آرگومانهای stdout و stderr ارسال شود).یک نمونه از
Processبرمیگرداند.برای سایر پارامترها، مستندات
loop.subprocess_shell()را ببینید.اگر شیء فرایند در حالی که فرایند هنوز در حال اجرا است زبالهروبی شود، فرایند فرزند خاتمه داده خواهد شد.
مهم
این مسئولیت برنامه است که اطمینان حاصل کند همهی نویسههای فضای خالی و نویسههای خاص بهدرستی درون علامت نقلقول قرار گیرند تا از آسیبپذیریهای تزریق پوسته جلوگیری شود. برای خنثی کردن صحیح نویسههای فضای خالی و نویسههای خاص پوسته در رشتههایی که قرار است برای ساخت دستورات پوسته استفاده شوند، میتوان از تابع
shlex.quote()استفاده کرد.تغییر یافته در نسخهی 3.10: پارامتر loop حذف شد.
توجه
زیرفرایندها برای ویندوز در دسترس هستند، اگر از ProactorEventLoop استفاده شود. برای جزئیات، پشتیبانی از زیرفرایند در ویندوز را ببینید.
همچنین ملاحظه نمائید
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()ندارند؛متدهای
communicate()وwait()پارامتر timeout ندارند: از تابعwait_for()استفاده کنید؛متد
Process.wait()ناهمگام است، در حالی که متدsubprocess.Popen.wait()بهصورت یک حلقه مشغول مسدودکننده (blocking busy loop) پیادهسازی شده است؛پارامتر universal_newlines پشتیبانی نمیشود.
این کلاس نخایمن نیست.
همچنین بخش زیرفرایند و نخها را ببینید.
- async wait()¶
منتظر بمانید تا فرایند فرزند خاتمه یابد.
ویژگی
returncodeرا تنظیم میکند و برمیگرداند.توجه
این متد ممکن است هنگام استفاده از
stdout=PIPEیاstderr=PIPEو در صورتی که فرایند فرزند آنقدر خروجی تولید کند که در انتظار پذیرش دادههای بیشتر توسط بافر پایپ سیستمعامل مسدود شود، دچار بنبست شود. برای جلوگیری از این وضعیت، هنگام استفاده از پایپها از متدcommunicate()استفاده کنید.
- async communicate(input=None)¶
تعامل با فرایند:
ارسال داده به stdin (اگر input
Noneنباشد)؛stdin را میبندد؛
دادهها را از stdout و stderr، تا رسیدن به EOF بخوانید؛
صبر کنید تا فرایند خاتمه یابد.
آرگومان اختیاری input، دادهای است (شیء
bytes) که به فرایند فرزند ارسال خواهد شد.یک تاپل
(stdout_data, stderr_data)برمیگرداند.اگر هنگام نوشتن input به stdin، استثنای
BrokenPipeErrorیاConnectionResetErrorپرتاب شود، آن استثنا نادیده گرفته میشود. این حالت زمانی رخ میدهد که فرآیند پیش از آنکه همه دادهها به stdin نوشته شوند، خارج شود.اگر بخواهید دادهای را به stdin فرایند بفرستید، فرایند باید با
stdin=PIPEایجاد شود. بهطور مشابه، برای دریافت هر چیزی غیر ازNoneدر تاپل نتیجه، فرایند باید با آرگومانهایstdout=PIPEو/یاstderr=PIPEایجاد شود.توجه داشته باشید که دادههای خواندهشده در حافظه بافر میشوند، بنابراین اگر اندازهی دادهها بزرگ یا نامحدود است، از این متد استفاده نکنید.
تغییر یافته در نسخهی 3.12: stdin همچنین وقتی
input=Noneباشد بسته میشود.
- send_signal(signal)¶
سیگنال signal را به فرایند فرزند ارسال میکند.
توجه
در ویندوز،
SIGTERMنام مستعاری برایterminate()است. میتوانCTRL_C_EVENTوCTRL_BREAK_EVENTرا به فرآیندهایی که با پارامتر creationflags حاویCREATE_NEW_PROCESS_GROUPآغاز شدهاند، ارسال کرد.
- terminate()¶
فرایند فرزند را متوقف کنید.
در سیستمهای POSIX، این متد
SIGTERMرا به فرایند فرزند ارسال میکند.در ویندوز، تابع Win32 API با نام
TerminateProcess()برای توقف فرایند فرزند فراخوانی میشود.
- kill()¶
فرایند فرزند را خاتمه دهید.
در سیستمهای POSIX، این متد
SIGKILLرا به فرایند فرزند ارسال میکند.در ویندوز، این متد نام مستعاری برای
terminate()است.
- stdin¶
جریان ورودی استاندارد (
StreamWriter) یاNoneاگر فرایند باstdin=Noneایجاد شده باشد.
- stdout¶
جریان خروجی استاندارد (
StreamReader) یاNoneاگر فرآیند باstdout=Noneایجاد شده باشد.
- stderr¶
جریان خطای استاندارد (
StreamReader) یاNoneاگر فرآیند باstderr=Noneایجاد شده باشد.
هشدار
بهجای
process.stdin.write()،await process.stdout.read()یاawait process.stderr.read()از متدcommunicate()استفاده کنید. این کار از بنبستهای ناشی از توقف خواندن یا نوشتن جریانها و مسدود شدن فرایند فرزند جلوگیری میکند.- pid¶
شمارهی شناسایی فرایند (PID).
توجه داشته باشید که برای فرایندهای ایجادشده توسط تابع
create_subprocess_shell()، این ویژگی، PID پوسته راهاندازیشده است.
- returncode¶
کد بازگشتی فرایند هنگام خروج.
مقدار
Noneنشان میدهد که فرآیند هنوز به پایان نرسیده است.برای فرایندهایی که با
create_subprocess_exec()ایجاد میشوند، مقدار منفی-Nنشان میدهد که فرایند فرزند با سیگنالNخاتمه یافته است (فقط POSIX).برای فرایندهایی که با
create_subprocess_shell()ایجاد میشوند، کد بازگشتی وضعیت خروج خود پوسته (برای مثال/bin/sh) را منعکس میکند، که ممکن است سیگنالها را به کدهایی مانند128+Nنگاشت کند. برای جزئیات، مستندات پوسته (برای مثال، بخش Exit Status دفترچه راهنمای Bash) را ببینید.
زیرفرایند و نخها¶
حلقه رویداد استاندارد asyncio بهصورت پیشفرض از اجرای زیرفرایندها از نخهای مختلف پشتیبانی میکند.
در ویندوز، زیرفرایندها فقط توسط ProactorEventLoop (پیشفرض) ارائه میشوند؛ SelectorEventLoop از زیرفرایندها پشتیبانی نمیکند.
توجه داشته باشید که پیادهسازیهای جایگزین حلقه رویداد ممکن است محدودیتهای خاص خود را داشته باشند؛ لطفاً به مستندات آنها مراجعه کنید.
همچنین ملاحظه نمائید
مثالها¶
مثالی که از کلاس Process برای کنترل یک زیرفرایند و از کلاس StreamReader برای خواندن از خروجی استاندارد آن استفاده میکند.
زیرفرایند توسط تابع create_subprocess_exec() ایجاد میشود:
import asyncio
import sys
async def get_date():
code = 'import datetime as dt; print(dt.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های سطح پایین نوشتهشده است، ببینید.