O módulo termios expõe o controle POSIX de terminais TTY em sistemas Unix. Com ele, uma aplicação pode ativar ou desativar echo, alternar entre modo canônico e leitura caractere a caractere, configurar velocidades, controlar filas de entrada e saída e consultar o tamanho da janela.
Essas operações alteram o estado global do terminal associado ao file descriptor. Se o programa encerrar sem restaurar os atributos, o shell pode ficar sem echo, com teclas especiais desativadas ou em modo aparentemente travado. A regra principal é salvar o estado original e restaurá-lo em finally.
Disponibilidade
termios existe apenas em sistemas Unix que oferecem controle POSIX de TTY. Windows usa APIs diferentes. Também é necessário que o file descriptor esteja ligado a um terminal real.
import os
import sys
if not os.isatty(sys.stdin.fileno()):
raise RuntimeError("stdin não está conectado a um TTY")
Entrada redirecionada por pipe ou arquivo não possui os mesmos atributos. Detecte isso antes de modificar o terminal.
Estrutura retornada por tcgetattr()
tcgetattr(fd) retorna sete elementos:
[iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
iflag controla entrada, oflag saída, cflag parâmetros de hardware, lflag comportamento local, ispeed e ospeed velocidades e cc caracteres de controle.
Use sempre constantes simbólicas do módulo. Não codifique números encontrados em outra plataforma.
Salvar e restaurar
import sys
import termios
fd = sys.stdin.fileno()
original = termios.tcgetattr(fd)
novo = termios.tcgetattr(fd)
try:
novo[3] &= ~termios.ECHO
termios.tcsetattr(fd, termios.TCSADRAIN, novo)
segredo = input("Senha: ")
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, original)
print()
Faça duas chamadas ou copie profundamente a lista, porque cc também é mutável. Uma cópia superficial pode compartilhar o subarray.
TCSANOW, TCSADRAIN e TCSAFLUSH
TCSANOW aplica imediatamente. TCSADRAIN espera a saída pendente ser transmitida. TCSAFLUSH também descarta entrada ainda não lida.
Para desligar echo em um prompt, TCSADRAIN evita cortar texto já enviado. Use TCSAFLUSH apenas quando descartar teclas pendentes for intencional.
Echo
O bit ECHO determina se caracteres digitados aparecem na tela.
atributos[3] &= ~termios.ECHO
Desligar echo não torna a captura de senha automaticamente segura. Processos privilegiados, keyloggers e o próprio código ainda podem acessar o valor. Para senhas comuns, prefira getpass.getpass().
Modo canônico
Com ICANON ativo, a linha é entregue após Enter e o driver processa edição básica. Ao removê-lo, leituras podem retornar antes do fim da linha.
atributos[3] &= ~termios.ICANON
Modo não canônico é útil para jogos, atalhos e interfaces em tela cheia, mas exige tratar cada byte, sinais e sequências de escape.
VMIN e VTIME
No array cc, VMIN e VTIME controlam leituras não canônicas.
atributos[6][termios.VMIN] = 1
atributos[6][termios.VTIME] = 0
Com VMIN 1 e VTIME 0, a leitura aguarda ao menos um byte. Com VMIN 0 e VTIME positivo, existe timeout em décimos de segundo. As combinações possuem semântica específica POSIX; teste no sistema alvo.
Leitura de uma tecla
import os
import sys
import termios
fd = sys.stdin.fileno()
original = termios.tcgetattr(fd)
novo = termios.tcgetattr(fd)
novo[3] &= ~(termios.ICANON | termios.ECHO)
novo[6][termios.VMIN] = 1
novo[6][termios.VTIME] = 0
try:
termios.tcsetattr(fd, termios.TCSADRAIN, novo)
tecla = os.read(fd, 1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, original)
print(tecla)
Uma tecla visual pode gerar vários bytes. Setas e teclas de função normalmente produzem sequências de escape.
Unicode
os.read() retorna bytes. Um caractere UTF-8 pode ocupar vários bytes, portanto ler apenas um byte não produz necessariamente um caractere completo.
Use decoder incremental de codecs ou leia sequências conforme o protocolo do terminal. Não faça byte.decode() isolado presumindo ASCII.
Teclas especiais e sinais
O flag ISIG permite que caracteres como Ctrl+C e Ctrl+Z gerem sinais. Removê-lo transforma esses bytes em entrada normal.
Desativar ISIG pode impedir o usuário de interromper a aplicação. Ofereça uma tecla de saída e restaure o terminal mesmo diante de exceções. Veja signal no Python.
Mapeamento de entrada
Flags como ICRNL, INLCR e IGNCR controlam conversão entre carriage return e newline. Alterá-las afeta Enter e protocolos seriais.
Modifique apenas os bits necessários, preservando o restante.
Processamento de saída
OPOST habilita processamento de saída. Em modo raw, ele pode ser desativado para evitar conversões. Isso também pode fazer newline não retornar o cursor à coluna inicial.
Aplicações interativas geralmente devem usar as funções de conveniência do módulo tty ou uma biblioteca como curses em vez de reconstruir todos os flags.
Parâmetros de hardware
cflag inclui tamanho de caractere, paridade, stop bits e controle do receptor. Essas opções são relevantes para portas seriais.
Constantes e suporte variam por driver. Configuração errada pode produzir dados ilegíveis, bloquear comunicação ou ignorar controle de fluxo.
Baud rate
ispeed e ospeed representam velocidades de entrada e saída usando constantes como B9600 e B115200.
atributos[4] = termios.B115200
atributos[5] = termios.B115200
Nem toda velocidade está disponível. Para comunicação serial de produção, uma biblioteca como pyserial oferece API mais portátil.
tcdrain()
tcdrain(fd) espera toda saída enfileirada ser transmitida. É útil antes de trocar parâmetros de linha ou fechar uma porta serial.
A chamada pode bloquear por tempo significativo se o dispositivo estiver lento ou desconectado. Use arquitetura que permita cancelamento.
tcflush()
tcflush() descarta filas.
termios.tcflush(fd, termios.TCIFLUSH)
TCIFLUSH descarta entrada, TCOFLUSH saída e TCIOFLUSH ambas. Descartar saída pode perder comandos já aceitos pela aplicação.
tcflow()
tcflow() suspende ou retoma entrada e saída por software com ações como TCOOFF e TCOON.
Não confunda com controle de fluxo de hardware. A disponibilidade e interação com drivers precisam de testes.
Enviar break
tcsendbreak(fd, duration) envia uma condição break em linhas seriais. Com duração zero, POSIX define aproximadamente 0,25 a 0,5 segundo; valores diferentes possuem significado dependente do sistema.
Tamanho da janela
Desde Python 3.11, tcgetwinsize() retorna linhas e colunas.
linhas, colunas = termios.tcgetwinsize(sys.stdout.fileno())
print(linhas, colunas)
tcsetwinsize() altera o tamanho associado ao TTY quando suportado. Em pseudo-terminais, isso ajuda a simular resize.
SIGWINCH
Quando a janela muda, processos Unix normalmente recebem SIGWINCH. Recalcule layout fora do handler ou marque uma flag para o loop principal.
O módulo curses, abordado no último conjunto deste lote, gerencia boa parte do redimensionamento de interfaces em tela cheia.
Context manager para estado
from contextlib import contextmanager
import termios
@contextmanager
def atributos_temporarios(fd, alterar):
original = termios.tcgetattr(fd)
novo = termios.tcgetattr(fd)
alterar(novo)
try:
termios.tcsetattr(fd, termios.TCSADRAIN, novo)
yield
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, original)
Centralizar a restauração reduz o risco de esquecer um caminho de erro.
Fork e subprocessos
Processos filhos podem herdar o mesmo terminal e observar atributos alterados. Evite iniciar subprocessos enquanto o terminal está em estado temporário, a menos que isso faça parte do protocolo.
Um filho que muda o TTY pode afetar o pai. Use pseudo-terminal quando precisar isolar uma aplicação interativa.
Threads
O estado pertence ao terminal, não a uma thread. Duas threads alterando flags podem restaurar valores fora de ordem. Concentre o controle de terminal em uma única thread e use locks de aplicação apenas fora de signal handlers.
Recuperação manual
Se um programa falhar e deixar o terminal sem echo, o comando Unix stty sane costuma restaurar configurações razoáveis. Documente isso para ferramentas experimentais.
Testes
Teste terminal real e entrada redirecionada, Ctrl+C, exceção durante leitura, Unicode, setas, resize, suspensão, subprocessos, porta serial, VMIN/VTIME e restauração após crash.
Use pty para testes automatizados de TTY, mas ainda faça testes manuais em terminais suportados.
Erros comuns
Os erros mais frequentes são não usar finally, modificar a lista original, fazer cópia superficial de cc, presumir que uma tecla equivale a um byte, desligar ISIG sem saída alternativa, usar stdin redirecionado, alterar flags demais e não testar liberação após exceção.
Conclusão
termios oferece controle preciso de terminais POSIX e portas seriais. Use-o quando realmente precisar de flags de baixo nível; para operações comuns, prefira tty, getpass, curses ou bibliotecas especializadas.
Salve e restaure o estado, altere somente os bits necessários e teste no Unix alvo. Consulte a documentação oficial de termios e o manual termios(3).







