> **Источник:** https://python-all.ru/3.7/library/pty.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# [`pty`](https://python-all.ru/3.7/library/pty.html#module-pty) – Утилиты псевдотерминалов

**Исходный код:** [Lib/pty.py](https://python-all.ru/src/3.7/Lib/pty.py)

---

Модуль [`pty`](https://python-all.ru/3.7/library/pty.html#module-pty) определяет операции для работы с концепцией псевдотерминала: запуск другого процесса и возможность программно записывать и читать из его управляющего терминала.

Поскольку работа с псевдотерминалами сильно зависит от платформы, код для этого реализован только для Linux. (Предполагается, что код для Linux будет работать и на других платформах, но это ещё не проверено.)

Модуль [`pty`](https://python-all.ru/3.7/library/pty.html#module-pty) определяет следующие функции:

#### `pty.fork()`

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

#### `pty.openpty()`

Открывает новую пару псевдотерминалов, используя [`os.openpty()`](https://python-all.ru/3.7/library/os.html#os.openpty), если возможно, или код эмуляции для стандартных Unix-систем. Возвращает пару файловых дескрипторов `(master, slave)`, для ведущего и ведомого конца соответственно.

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

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

Функциям *master\_read* и *stdin\_read* передаётся файловый дескриптор, из которого они должны читать, и они всегда должны возвращать байтовую строку. Чтобы заставить spawn вернуть управление до завершения дочернего процесса, должно быть вызвано исключение [`OSError`](https://python-all.ru/3.7/library/exceptions.html#OSError).

Реализация по умолчанию для обеих функций читает и возвращает до 1024 байт при каждом вызове. Колбэку *master\_read* передаётся ведущий файловый дескриптор псевдотерминала для чтения вывода дочернего процесса, а *stdin\_read* передаётся файловый дескриптор 0 для чтения из стандартного ввода родительского процесса.

Возврат пустой байтовой строки из любого колбэка интерпретируется как условие конца файла (EOF), и после этого колбэк вызываться не будет. Если *stdin\_read* сигнализирует EOF, управляющий терминал больше не может взаимодействовать ни с родительским процессом, ни с дочерним. Если дочерний процесс не завершится без ввода, *spawn* будет циклически ожидать вечно. Если *master\_read* сигнализирует EOF, поведение будет таким же (по крайней мере, в Linux).

Если оба колбэка сигнализируют о конце файла (EOF), то *spawn*, вероятно, никогда не вернёт управление, если только *select* не вызовет ошибку на вашей платформе при передаче трёх пустых списков. Это ошибка, задокументированная в [issue 26228](https://python-all.ru/3.7/library/pty.html).

Изменено в версии 3.4: [`spawn()`](https://python-all.ru/3.7/library/pty.html#pty.spawn) теперь возвращает значение статуса от [`os.waitpid()`](https://python-all.ru/3.7/library/os.html#os.waitpid) для дочернего процесса.

## Пример

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

```python
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)
```
