pty --- ابزارهای شبه‌پایانه

کد منبع: Lib/pty.py


ماژول pty عملیاتی را برای مدیریت مفهوم شبه‌پایانه (pseudo-terminal) تعریف می‌کند: آغاز فرایندی دیگر و امکان نوشتن و خواندن از پایانه‌ی کنترل‌کننده‌ی آن به‌صورت برنامه‌ای.

مدیریت شبه‌پایانه (pseudo-terminal) به‌شدت وابسته به پلتفرم است. این کد بیشتر روی Linux، FreeBSD و macOS آزمایش شده است (انتظار می‌رود روی سایر پلتفرم‌های POSIX کار کند، اما به‌طور کامل آزمایش نشده است).

ماژول pty توابع زیر را تعریف می‌کند:

pty.fork()

فورک . پایانه‌ی کنترل‌کننده‌ی فرزند را به یک شبه‌پایانه متصل می‌کند. مقدار بازگشتی (pid, fd) است. توجه داشته باشید که فرزند pid برابر ۰ دریافت می‌کند و fd نامعتبر است. مقدار بازگشتی والد، pid فرزند است و fd توصیف‌گر پرونده‌ای است که به پایانه‌ی کنترل‌کننده‌ی فرزند (و همچنین به ورودی و خروجی استاندارد فرزند) متصل است.

هشدار

در macOS، استفاده از این تابع در صورت ترکیب با استفاده از APIهای سیستمی سطح بالاتر ناایمن است، و این شامل استفاده از urllib.request نیز می‌شود.

pty.openpty()

یک جفت شبه‌پایانه (pseudo-terminal) جدید را، در صورت امکان با استفاده از os.openpty() یا با کد شبیه‌سازی برای سیستم‌های یونیکسی عام، باز می‌کند. یک جفت توصیف‌گر پرونده (master, slave) را به‌ترتیب برای سمت master و slave برمی‌گرداند.

pty.spawn(argv[, master_read[, stdin_read]])

فرایندی را ایجاد می‌کند و پایانه کنترل‌کننده آن را به ورودی/خروجی استاندارد فرایند جاری متصل می‌کند. این کار اغلب برای سردرگم کردن برنامه‌هایی استفاده می‌شود که اصرار دارند از پایانه کنترل‌کننده بخوانند. انتظار می‌رود فرایند ایجادشده در پشت pty در نهایت خاتمه یابد، و هنگامی که این اتفاق بیفتد spawn بازمی‌گردد.

یک حلقه، STDIN فرایند جاری را به فرزند و داده‌های دریافت‌شده از فرزند را به STDOUT فرایند جاری کپی می‌کند. اگر STDIN فرایند جاری بسته شود، سیگنالی به فرزند ارسال نمی‌شود.

توابع master_read و stdin_read یک توصیف‌گر پرونده دریافت می‌کنند که باید از آن بخوانند، و باید همیشه یک رشته بایتی برگردانند. برای وادار کردن spawn به بازگشت پیش از خارج شدن فرایند فرزند، باید یک آرایه بایتی خالی برگردانده شود تا پایان پرونده علامت‌دهی شود.

پیاده‌سازی پیش‌فرض برای هر دو تابع، هر بار که تابع فراخوانی می‌شود، تا ۱۰۲۴ بایت را می‌خواند و برمی‌گرداند. کال‌بک master_read توصیف‌گر پرونده master شبه‌پایانه را دریافت می‌کند تا خروجی فرآیند فرزند را بخواند، و stdin_read توصیف‌گر پرونده ۰ را دریافت می‌کند تا از ورودی استاندارد فرآیند والد بخواند.

برگرداندن یک رشته بایتی خالی از هر یک از کال‌بک‌ها به‌عنوان پایان پرونده تفسیر می‌شود، و آن کال‌بک پس از آن فراخوانی نخواهد شد. اگر stdin_read EOF را اعلام کند، پایانه کنترل‌کننده دیگر نمی‌تواند با فرایند والد یا فرایند فرزند ارتباط برقرار کند. اگر فرایند فرزند بدون هیچ ورودی خارج نشود، در این صورت spawn برای همیشه حلقه خواهد زد. اگر master_read EOF را اعلام کند، همان رفتار حاصل می‌شود (حداقل در لینوکس).

مقدار وضعیت خروج را از os.waitpid() برای فرآیند فرزند برمی‌گرداند.

می‌توان از os.waitstatus_to_exitcode() برای تبدیل وضعیت خروج به کد خروج استفاده کرد.

یک رویداد حسابرسی pty.spawn را با آرگومان argv پرتاب می‌کند.

تغییر یافته در نسخه‌ی 3.4: spawn() اکنون مقدار وضعیتِ os.waitpid() را برای فرایند فرزند برمی‌گرداند.

مثال

برنامه زیر مانند فرمان یونیکسی script(1) عمل می‌کند و با استفاده از یک شبه‌پایانه (pseudo-terminal) تمام ورودی و خروجی یک نشست پایانه را در یک "typescript" ضبط می‌کند.

import argparse
import os
import pty
import sys
import time

parser = argparse.ArgumentParser()
parser.add_argument('-a', dest='append', action='store_true')
parser.add_argument('-p', dest='use_python', action='store_true')
parser.add_argument('filename', nargs='?', default='typescript')
options = parser.parse_args()

shell = sys.executable if options.use_python else os.environ.get('SHELL', 'sh')
filename = options.filename
mode = 'ab' if options.append else 'wb'

with open(filename, mode) as script:
    def read(fd):
        data = os.read(fd, 1024)
        script.write(data)
        return data

    print('Script started, file is', filename)
    script.write(('Script started on %s\n' % time.asctime()).encode())

    pty.spawn(shell, read)

    script.write(('Script done on %s\n' % time.asctime()).encode())
    print('Script done, file is', filename)