pty — Utilitários de pseudoterminal

Código-fonte: Lib/pty.py


O módulo pty define operações para lidar com o conceito de pseudoterminal: iniciar outro processo e poder gravar e ler de seu terminal de controle programaticamente.

Disponibilidade

O tratamento do pseudoterminal é altamente dependente da plataforma. Esse código foi testado principalmente no Linux, no FreeBSD e no macOS (supõe-se que funcione em outras plataformas POSIX, mas não foi testado exaustivamente).

O módulo pty define as seguintes funções:

pty.fork()

Faz um fork. Conecta o terminal de controle do filho a um pseudoterminal. O valor de retorno é (pid, fd). Observe que a criança recebe pid 0 e o fd é inválido. O valor de retorno do pai é o pid do filho, e o fd é um descritor de arquivo conectado ao terminal de controle do filho (e também à entrada e à saída padrão do filho).

Aviso

No macOS, o uso desta função é inseguro quando combinado com o uso de APIs de sistema de nível mais alto, e isso inclui o uso de urllib.request.

pty.openpty()

Abre um novo par de pseudoterminais, usando os.openpty(), se possível, ou código de emulação para sistemas genérico Unix . Retorna um par de descritores de arquivo (master, slave), para a extremidade mestre e escrava, respectivamente.

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

Gera um processo e conecta seu terminal de controle com o E/S padrão do processo atual. Isso é frequentemente usado para confundir programas que insistem em ler no terminal de controle. Espera-se que o processo gerado por trás do pty acabe sendo encerrado e, quando isso acontecer, o spawn é retornado.

Um laço copia o STDIN do processo atual para o filho e os dados recebidos do filho para o STDOUT do site processar atual. Não é sinalizado para o filho se o STDIN do processar atual fechar.

As funções master_read e o stdin_read recebem um descritor de arquivo do qual devem ler e devem sempre retorna uma string de bytes. Para forçar spawn retornar antes que o processo filho saia, um byte vazio vetor deve ser retornado para sinalizar o fim do arquivo.

A implementação padrão para ambos os funções vai ler e retornar até 1024 bytes cada vez que a função for chamada. A função de retorno master_read é passada para o descritor de arquivo mestre do pseudoterminal para ler a saída do processo filho, e ao stdin_read é passado o descritor de arquivo 0, para ler a entrada padrão do processo pai.

Retornar uma string de bytes vazia de qualquer uma das funções de retorno é interpretado como uma condição de fim de vida (EOF), e que a função de retorno não será chamada depois disso. Se stdin_read sinalizar EOF, o terminal de controle não poderá mais se comunicar com o processo pai OU o processo filho. A menos que o filho processar seja encerrado sem nenhuma entrada, spawn vai então fazer o laço para sempre. Se master_read sinalizar EOF, os mesmos comportamento resultados (pelo menos no Linux).

Retorna o valor de status de saída de os.waitpid() no processo filho.

os.waitstatus_to_exitcode() pode ser usado para converter o status de saída em um código de saída.

Levanta um evento de auditoria pty.spawn com o argumento argv.

Alterado na versão 3.4: spawn() agora retorna o valor de status de os.waitpid() no processo filho.

Exemplo

O programa a seguir funciona como o comando Unix script(1) , usando um pseudoterminal para registrar todas as entradas e saídas de uma sessão de terminal em um “script”.

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)