pty — Псевдотерминальные утилиты

Источник: Lib/pty.py


Модуль pty определяет операции для работы с концепцией псевдотерминала: запуск другого процесса с возможностью программной записи на управляющий терминал и чтения с него.

Availability: Unix.

Работа с псевдотерминалами сильно зависит от платформы. Этот код в основном протестирован на Linux, FreeBSD и macOS (предполагается, что он будет работать и на других POSIX-платформах, но это не было тщательно проверено).

Модуль pty определяет следующие функции:

pty.fork()

Вилка. Подключить управляющий терминал дочернего устройства к псевдотерминалу. Возвращаемое значение - (pid, fd). Обратите внимание, что ребенок получает pid 0, а fd - invalid. Возвращаемое значение родителя - это pid ребенка, а fd - дескриптор файла, подключенный к управляющему терминалу ребенка (а также к его стандартному вводу и выводу).

Предупреждение

В macOS использование этой функции небезопасно в сочетании с использованием системных API более высокого уровня, включая использование urllib.request.

pty.openpty()

Откройте новую пару псевдотерминалов, используя os.openpty(), если это возможно, или код эмуляции для общих Unix-систем. Верните пару файловых дескрипторов (master, slave), для ведущего и ведомого концов соответственно.

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

Порождает процесс и соединяет его управляющий терминал со стандартным io текущего процесса. Это часто используется для того, чтобы сбить с толку программы, которые настаивают на чтении с управляющего терминала. Ожидается, что процесс, порожденный за pty, в конце концов завершится, и когда это произойдет, spawn вернется.

Цикл копирует STDIN текущего процесса в дочерний и данные, полученные от дочернего, в STDOUT текущего процесса. Сигнал о закрытии STDIN текущего процесса дочернему процессу не передается.

Функциям master_read и stdin_read передается дескриптор файла, из которого они должны читать, и они всегда должны возвращать байтовую строку. Чтобы заставить spawn вернуться до выхода дочернего процесса, следует возвращать пустой массив байтов, сигнализирующий о конце файла.

Реализация по умолчанию для обеих функций будет считывать и возвращать до 1024 байт при каждом вызове функции. Обратному вызову master_read передается дескриптор главного файла псевдотерминала для чтения вывода из дочернего процесса, а stdin_read передается дескриптор файла 0 для чтения из стандартного ввода родительского процесса.

Возврат пустой байтовой строки из любого обратного вызова интерпретируется как условие конца файла (EOF), после чего этот обратный вызов вызываться не будет. Если stdin_read сигнализирует EOF, управляющий терминал больше не может взаимодействовать с родительским процессом ИЛИ дочерним процессом. Если только дочерний процесс не выйдет без каких-либо входных данных, то spawn будет циклиться вечно. Если master_read подаст сигнал EOF, то поведение будет таким же (по крайней мере, в linux).

Возвращает значение статуса выхода из os.waitpid() для дочернего процесса.

os.waitstatus_to_exitcode() можно использовать для преобразования статуса выхода в код выхода.

Поднимает auditing event pty.spawn с аргументом argv.

Изменено в версии 3.4: spawn() теперь возвращает значение статуса из os.waitpid() дочернего процесса.

Пример

Следующая программа действует подобно команде Unix script(1), используя псевдотерминал для записи всех входных и выходных данных терминального сеанса в «типовой сценарий».

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)