O módulo tty fornece funções de conveniência para colocar um terminal Unix em modo raw ou cbreak. Ele usa termios por baixo, mas evita que a aplicação precise ajustar manualmente dezenas de flags para tarefas comuns como ler teclas sem esperar Enter.
Raw e cbreak são úteis em jogos de terminal, menus interativos, atalhos, ferramentas de administração e interfaces em tela cheia. Também são perigosos quando o estado não é restaurado: o shell pode ficar sem echo, sem processamento de sinais ou com teclas aparentemente quebradas.
Disponibilidade
tty está disponível apenas em Unix porque depende de termios. Windows requer outra API ou uma biblioteca multiplataforma.
try:
import tty
except ImportError:
tty = None
O file descriptor também precisa representar um terminal real. Use os.isatty() antes de alterar stdin ou stdout.
Raw ou cbreak?
No modo cbreak, caracteres são entregues imediatamente e o echo é desativado, mas parte do processamento do terminal continua ativa. Ctrl+C normalmente ainda gera SIGINT e Enter preserva comportamento compatível com o sistema.
No modo raw, a aplicação recebe bytes quase sem transformação. Sinais, conversões de newline, flow control e processamento de saída podem ser desativados. Isso oferece controle máximo, mas exige implementar mais lógica.
setcbreak()
import os
import sys
import termios
import tty
fd = sys.stdin.fileno()
original = tty.setcbreak(fd)
try:
tecla = os.read(fd, 1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, original)
print(tecla)
Desde Python 3.12, setcbreak() retorna os atributos originais. Em versões anteriores, o retorno era None, então bibliotecas que suportam Python antigo devem chamar tcgetattr() antes.
setraw()
original = tty.setraw(fd)
try:
dados = os.read(fd, 32)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, original)
Raw mode remove mais processamento do driver. Use quando a aplicação precisa interpretar cada byte, inclusive Ctrl+C e sequências de controle.
Sempre restaure em finally
A restauração deve ocorrer mesmo após KeyboardInterrupt, erro de parsing, EOF ou exceção inesperada.
def ler_tecla():
fd = sys.stdin.fileno()
original = tty.setcbreak(fd)
try:
return os.read(fd, 1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, original)
Não dependa apenas de atexit. Ele pode não executar após SIGKILL, crash nativo ou encerramento abrupto.
O argumento when
setraw(fd, when=...) e setcbreak() repassam when para tcsetattr(). O padrão é TCSAFLUSH, que aplica depois da saída pendente e descarta entrada não processada.
Se descartar teclas anteriores não for desejado, avalie TCSADRAIN. A escolha depende do protocolo interativo.
cfmakeraw()
Python 3.12 adicionou cfmakeraw(mode), que modifica uma lista de atributos para raw mode sem aplicá-la imediatamente.
atributos = termios.tcgetattr(fd)
novo = termios.tcgetattr(fd)
tty.cfmakeraw(novo)
novo[6][termios.VMIN] = 0
novo[6][termios.VTIME] = 5
termios.tcsetattr(fd, termios.TCSADRAIN, novo)
Isso é útil quando você quer partir de uma configuração raw e personalizar VMIN, VTIME ou flags específicas antes de aplicar.
cfmakecbreak()
cfmakecbreak(mode) limpa ECHO e ICANON, configura VMIN para 1 e VTIME para zero.
novo = termios.tcgetattr(fd)
tty.cfmakecbreak(novo)
termios.tcsetattr(fd, termios.TCSADRAIN, novo)
Desde Python 3.12.2, a função não limpa ICRNL. Isso mantém o comportamento histórico e corresponde ao cbreak descrito por Linux, macOS e BSD.
Compatibilidade de versões
if hasattr(tty, "cfmakecbreak"):
tty.cfmakecbreak(atributos)
else:
atributos[3] &= ~(termios.ECHO | termios.ICANON)
atributos[6][termios.VMIN] = 1
atributos[6][termios.VTIME] = 0
Ao manter fallback, preserve ICRNL para reproduzir o comportamento atual de cbreak.
Leitura de setas
Uma seta geralmente envia uma sequência como ESC, colchete e uma letra. Ler um byte não identifica a tecla completa.
primeiro = os.read(fd, 1)
if primeiro == b"\x1b":
restante = os.read(fd, 2)
sequencia = primeiro + restante
Sequências variam por terminal e modificadores. Para aplicações robustas, use curses ou uma biblioteca que conheça a base terminfo.
Timeout para distinguir Escape
A tecla Escape isolada começa com o mesmo byte de muitas sequências. Configure VMIN/VTIME ou use select para esperar brevemente por bytes adicionais.
Timeout muito curto falha em conexões lentas; timeout longo torna Escape atrasado. Teste local, SSH e ambientes remotos.
Unicode
Raw e cbreak entregam bytes. Caracteres Unicode podem ocupar vários bytes em UTF-8. Use decoder incremental em vez de decodificar cada leitura isolada.
import codecs
decoder = codecs.getincrementaldecoder("utf-8")()
texto = decoder.decode(bloco)
Sequências de controle e texto Unicode compartilham o fluxo, portanto o parser deve distinguir ambos.
Ctrl+C
Em cbreak, ISIG normalmente continua ativo e Ctrl+C gera KeyboardInterrupt. Em raw, o byte \x03 pode chegar como entrada comum.
Se raw mode desativa sinais, implemente uma tecla de saída confiável. Não prenda o usuário em uma interface sem escape.
Ctrl+Z e suspensão
No cbreak, Ctrl+Z pode suspender o processo. Ao ser retomado, a aplicação deve confirmar tamanho da janela e redesenhar a interface.
Em raw mode, Ctrl+Z pode ser apenas um byte. Escolha conscientemente o comportamento.
Leitura com select()
import select
prontos, _, _ = select.select([fd], [], [], 0.5)
if prontos:
bloco = os.read(fd, 1024)
else:
executar_tarefa_periodica()
O guia de select no Python aborda readiness e I/O não bloqueante.
Context manager
from contextlib import contextmanager
@contextmanager
def modo_cbreak(fd):
original = tty.setcbreak(fd)
try:
yield
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, original)
Para suportar Python anterior a 3.12, capture os atributos antes de chamar setcbreak().
stdin, stdout ou /dev/tty?
stdin pode estar redirecionado enquanto a aplicação ainda tem um terminal controlador. Em Unix, abrir /dev/tty permite interagir com ele diretamente.
with open("/dev/tty", "rb+", buffering=0) as terminal:
fd = terminal.fileno()
Essa abertura pode falhar em serviços, containers sem TTY e jobs em background. Trate o erro.
Subprocessos
Um subprocesso iniciado enquanto o terminal está raw herda esse estado. Programas comuns podem se comportar de forma inesperada.
Restaure temporariamente antes de executar um comando externo ou use pty para fornecer um terminal isolado.
Threads
O estado pertence ao terminal inteiro. Duas threads alternando raw e cbreak podem restaurar valores fora de ordem. Concentre entrada interativa em uma única thread.
Saída e cursor
Raw mode pode remover processamento de saída. Um \n pode não retornar à coluna inicial. Use \r\n quando apropriado ou preserve flags de saída se não precisa de raw completo.
Resize
Use termios.tcgetwinsize() para obter linhas e colunas. Após SIGWINCH, marque a necessidade de redesenho e processe no loop principal.
Teste com pty
O módulo pty cria um pseudo-terminal para executar programas interativos em testes. Ele permite verificar se o código entra e sai de raw/cbreak sem deixar o TTY real alterado.
Recuperação
Quando um bug deixa o shell sem echo, digite stty sane e pressione Enter, mesmo sem ver os caracteres. Esse comando restaura configurações comuns.
Segurança
Não trate raw mode como ocultação segura de senha. Bytes podem ser registrados pelo próprio programa. Não execute sequências de escape vindas de fonte não confiável, pois algumas alteram terminal, clipboard ou título da janela.
Testes recomendados
Teste cbreak e raw, Python 3.11 e 3.12+, Ctrl+C, Ctrl+Z, Escape, setas, Unicode, stdin redirecionado, SSH lento, resize, exceções, subprocessos e ausência de TTY.
Erros comuns
Os erros mais frequentes são não restaurar o estado, presumir que setraw() sempre retorna atributos em versões antigas, confundir raw com cbreak, ler setas como um byte, esquecer Unicode, descartar entrada com TCSAFLUSH sem intenção e iniciar subprocessos no modo errado.
Conclusão
tty simplifica a configuração raw e cbreak em Unix. Use cbreak quando quiser entrada imediata preservando sinais; escolha raw apenas quando precisar controlar cada byte e transformação.
Restaure sempre em finally, trate versões do Python e teste em TTY real e pseudo-terminal. Consulte a documentação oficial de tty e o artigo sobre termios no Python.







