tty en Python: modos raw y cbreak

Publicado el: 25/08/2026
Tempo de leitura: 5 minutos
Detailed view of programming code in a dark theme on a computer screen.

El módulo tty ofrece funciones de conveniencia para colocar un terminal Unix en modo raw o cbreak. Se apoya en termios y evita que la aplicación tenga que modificar manualmente muchas flags para tareas como leer una tecla sin esperar Enter.

Raw y cbreak son útiles en juegos de terminal, menús interactivos, hotkeys, herramientas de administración e interfaces de pantalla completa. También son peligrosos si el estado no se restaura: el shell puede quedar sin echo, sin señales o con teclas aparentemente rotas.

Disponibilidad

tty solo está disponible en Unix porque depende de termios. Windows requiere otra API o una biblioteca multiplataforma.

try:
    import tty
except ImportError:
    tty = None

El descriptor también debe representar un terminal real. Comprueba os.isatty() antes de cambiar stdin o stdout.

Raw frente a cbreak

En cbreak, los caracteres se entregan inmediatamente y el echo se desactiva, pero parte del procesamiento del terminal continúa. Ctrl+C normalmente genera SIGINT y Enter mantiene el mapeo habitual.

Raw entrega bytes con muy poca transformación. Señales, conversiones de newline, flow control y procesamiento de salida pueden quedar desactivados. Ofrece máximo control, pero obliga a implementar más 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() devuelve los atributos originales. Versiones anteriores devolvían None, así que código compatible debe llamar tcgetattr() antes.

setraw()

original = tty.setraw(fd)
try:
    datos = os.read(fd, 32)
finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, original)

Raw desactiva más comportamiento del driver. Úsalo solo cuando la aplicación deba interpretar cada byte, incluso Ctrl+C y secuencias de control.

Restaurar siempre en finally

La restauración debe ejecutarse después de KeyboardInterrupt, errores de parsing, EOF y excepciones inesperadas.

def leer_tecla():
    fd = sys.stdin.fileno()
    original = tty.setcbreak(fd)
    try:
        return os.read(fd, 1)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, original)

No dependas solo de atexit. No se ejecuta después de SIGKILL o algunos crashes nativos.

El argumento when

setraw() y setcbreak() pasan when a tcsetattr(). El default es TCSAFLUSH, que espera la salida y descarta entrada pendiente.

Usa TCSADRAIN cuando no quieras eliminar teclas previas.

cfmakeraw()

Python 3.12 añade cfmakeraw(mode), que modifica una lista de atributos sin aplicarla.

nuevo = termios.tcgetattr(fd)
tty.cfmakeraw(nuevo)
nuevo[6][termios.VMIN] = 0
nuevo[6][termios.VTIME] = 5
termios.tcsetattr(fd, termios.TCSADRAIN, nuevo)

Permite partir de raw y personalizar timeouts o flags.

cfmakecbreak()

cfmakecbreak(mode) limpia ECHO e ICANON y configura VMIN en uno con VTIME cero.

nuevo = termios.tcgetattr(fd)
tty.cfmakecbreak(nuevo)
termios.tcsetattr(fd, termios.TCSADRAIN, nuevo)

Desde Python 3.12.2 ya no limpia ICRNL. Esto coincide con el comportamiento histórico y con Linux, macOS y BSD.

Compatibilidad de versiones

if hasattr(tty, "cfmakecbreak"):
    tty.cfmakecbreak(atributos)
else:
    atributos[3] &= ~(termios.ECHO | termios.ICANON)
    atributos[6][termios.VMIN] = 1
    atributos[6][termios.VTIME] = 0

Conserva ICRNL en el fallback para reproducir el cbreak actual.

Flechas

Una flecha normalmente envía una secuencia que comienza con ESC. Leer un byte no identifica la tecla completa.

primero = os.read(fd, 1)
if primero == b"\x1b":
    restante = os.read(fd, 2)
    secuencia = primero + restante

Las secuencias cambian por terminal y modificadores. Para una solución robusta, usa curses o terminfo.

Distinguir Escape

Escape aislado comparte el primer byte de muchas secuencias. Usa VMIN/VTIME o select para esperar brevemente bytes adicionales.

Un timeout corto falla por SSH lento; uno largo retrasa Escape.

Unicode

Raw y cbreak entregan bytes. Un carácter Unicode puede necesitar varios bytes UTF-8. Usa decoder incremental.

import codecs

decoder = codecs.getincrementaldecoder("utf-8")()
texto = decoder.decode(bloque)

El parser debe distinguir texto y secuencias de control.

Ctrl+C

En cbreak, ISIG normalmente sigue activo y Ctrl+C genera KeyboardInterrupt. En raw, \x03 puede llegar como entrada común.

Si raw desactiva señales, ofrece una tecla de salida confiable.

Ctrl+Z

En cbreak, Ctrl+Z puede suspender el proceso. Al volver, confirma el tamaño y redibuja. En raw puede ser solo un byte.

Esperar con select()

import select

listos, _, _ = select.select([fd], [], [], 0.5)
if listos:
    bloque = os.read(fd, 1024)
else:
    ejecutar_tarea_periodica()

select en Python explica readiness e I/O no 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 Python anterior a 3.12, guarda los atributos antes.

stdin o /dev/tty

stdin puede estar redirigido aunque exista un terminal controlador. En Unix, /dev/tty abre ese terminal.

with open("/dev/tty", "rb+", buffering=0) as terminal:
    fd = terminal.fileno()

Puede fallar en servicios, jobs en background o containers sin TTY.

Subprocesses

Un hijo iniciado mientras el terminal está raw hereda ese estado. Restaura modo normal antes de ejecutar comandos externos o usa pty.

Threads

El estado pertenece al terminal, no al thread. Dos threads cambiando modos pueden restaurar valores fuera de orden. Centraliza la entrada interactiva.

Salida y cursor

Raw puede desactivar procesamiento de salida. Un \n quizá no vuelva a la primera columna. Usa \r\n o conserva flags de salida si no necesitas raw completo.

Resize

Usa termios.tcgetwinsize() para filas y columnas. Después de SIGWINCH, marca el layout y redibuja en el loop.

Tests con pty

pty crea un pseudo-terminal para pruebas de interfaces interactivas sin alterar el TTY real.

Recuperación

Si un bug deja el shell sin echo, escribe stty sane y pulsa Enter aunque no veas caracteres.

Seguridad

Raw no es ocultación segura de contraseñas. Además, no imprimas secuencias de escape no confiables, porque pueden manipular terminal, título o clipboard.

Pruebas recomendadas

Prueba raw y cbreak, Python 3.11 y 3.12+, Ctrl+C, Ctrl+Z, Escape, flechas, Unicode, stdin redirigido, SSH lento, resize, excepciones, subprocesses y ausencia de TTY.

Errores comunes

Los fallos frecuentes son no restaurar, asumir el retorno nuevo en Python antiguo, confundir raw y cbreak, leer flechas como un byte, ignorar Unicode, descartar entrada sin intención e iniciar subprocesses en el modo equivocado.

Conclusión

tty simplifica raw y cbreak en Unix. Elige cbreak para entrada inmediata conservando señales; usa raw solo cuando necesites controlar cada byte.

Restaura en finally, maneja diferencias de versión y prueba en terminal real y pseudo-terminal. Consulta la documentación oficial de tty y termios en Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Laptop displaying code with reflection, perfect for tech and programming themes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    termios en Python: control seguro del terminal

    Aprende termios en Python para modo canónico, echo, lectura de teclas, baud rate, colas, ventana y restauración segura del TTY.

    Ler mais

    Tempo de leitura: 5 minutos
    25/08/2026
    Multiple padlocks securing a green chain link fence, symbolizing safety and protection.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    fcntl en Python: locks y control de archivos

    Aprende fcntl en Python para locks, flags de descriptors, ioctl, pipes y control Unix, evitando buffers inválidos y corrupción de

    Ler mais

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

    readline en Python: historial y autocomplete

    Aprende readline en Python para historial, autocompletado, edición de línea, GNU Readline, libedit y prompts seguros en terminal.

    Ler mais

    Tempo de leitura: 5 minutos
    25/08/2026
    Vibrant green tree python elegantly coiled on branch, showcasing its natural beauty.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    io en Python: domina streams y buffers

    Aprende io en Python para streams de texto y bytes, buffering, encoding, StringIO, BytesIO, I/O bruto e interfaces file-like.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    Vibrant green tree python elegantly coiled on branch, showcasing its natural beauty.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    select en Python: monitorea varios I/O

    Aprende select en Python para monitorear sockets y pipes, tratar I/O parcial, backpressure, poll, epoll, señales y diferencias de plataforma.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    Detailed view of programming code in a dark theme on a computer screen.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    signal en Python: cierre correcto

    Aprende signal en Python para manejar SIGTERM y SIGINT, detener servicios, usar timers, wakeup FD y evitar deadlocks en handlers.

    Ler mais

    Tempo de leitura: 7 minutos
    24/08/2026