tty no Python: modos raw e cbreak

Publicado em: 25/08/2026
Tempo de leitura: 6 minutos
A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.

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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Multiple padlocks securing a green chain link fence, symbolizing safety and protection.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    termios no Python: controle seguro do terminal

    Aprenda termios no Python para modo canônico, echo, leitura por tecla, baud rate, filas e restauração segura do terminal POSIX.

    Ler mais

    Tempo de leitura: 6 minutos
    25/08/2026
    Detailed close-up of a combination lock with numbers in focus, highlighting security and privacy.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    fcntl no Python: locks e controle de arquivos

    Aprenda fcntl no Python para locks, flags de descritores, ioctl, pipes e controle Unix, evitando buffers incorretos e corrupção de

    Ler mais

    Tempo de leitura: 6 minutos
    25/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    readline no Python: histórico e autocomplete

    Aprenda readline no Python para histórico, autocomplete, edição de linha, GNU Readline, libedit e prompts seguros no terminal.

    Ler mais

    Tempo de leitura: 6 minutos
    25/08/2026
    Serene stream flowing through Bavarian mountains, capturing winter beauty and natural tranquility.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    io no Python: domine streams e buffers

    Aprenda io no Python para trabalhar com streams de texto e bytes, buffering, encoding, StringIO, BytesIO e I/O bruto com

    Ler mais

    Tempo de leitura: 7 minutos
    24/08/2026
    Business professional analyzing financial data on multiple computer monitors at his workspace.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    select no Python: monitore vários I/Os

    Aprenda select no Python para monitorar sockets e pipes, tratar leituras parciais, backpressure, epoll, poll e sinais sem busy loop.

    Ler mais

    Tempo de leitura: 7 minutos
    24/08/2026
    View of multiple railway tracks with signals and buildings in an urban setting during daytime.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    signal no Python: encerre processos bem

    Aprenda signal no Python para tratar SIGTERM e SIGINT, encerrar serviços, usar timers, wakeup FD e coordenar shutdown sem deadlocks.

    Ler mais

    Tempo de leitura: 7 minutos
    24/08/2026