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

    Código Python asíncrono en un portátil para inspect.markcoroutinefunction
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: detecta wrappers async

    Aprende inspect.markcoroutinefunction en Python para identificar wrappers asíncronos, integrar frameworks y evitar detecciones incorrectas.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Código Python para recorrer carpetas y archivos con Path.walk
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: recorre directorios con seguridad

    Aprende Path.walk en Python para recorrer directorios, filtrar archivos, tratar errores y controlar la travesía con seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Depuración de un proceso Python en terminal con código
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depura procesos Python en ejecución

    Aprende a conectar pdb a un proceso Python en ejecución, inspeccionar la pila y diagnosticar bloqueos de forma segura.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Código Python para representar fracciones exactas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: convierte números en fracciones

    Aprende fractions.from_number en Python para convertir números en fracciones exactas, controlar precisión, validar entradas y evitar redondeos inesperados.

    Ler mais

    Tempo de leitura: 4 minutos
    09/10/2026
    Desarrollador configurando un servidor HTTPS y certificado TLS con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crea un servidor HTTPS local en Python

    Aprende HTTPSServer en Python para crear servicios HTTPS locales, configurar certificados, usar hilos y comprender sus límites.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026