readline en Python: historial y autocomplete

Publicado el: 25/08/2026
Tempo de leitura: 5 minutos
Close-up view of a computer screen displaying code in a software development environment.

El módulo readline añade edición de línea, historial de comandos y autocompletado a programas interactivos ejecutados en terminal. Puede afectar el prompt tradicional de Python y llamadas normales a input(), permitiendo navegar con flechas, reutilizar entradas anteriores y completar palabras con Tab.

A pesar del nombre, el módulo puede usar GNU Readline o la implementación compatible libedit. Esa diferencia afecta archivos de configuración, atajos, formatos de historial y algunos detalles de la API. En Python moderno también existe otra distinción: el nuevo REPL introducido en Python 3.13 no usa readline por defecto.

Disponibilidad

readline es opcional y normalmente está disponible en Unix. No funciona en Android, iOS ni WASI. Una distribución puede compilar CPython sin el módulo, por lo que software portable debería manejar ImportError.

try:
    import readline
except ImportError:
    readline = None

En Windows existen alternativas de terceros, pero no forman parte del módulo estándar. Las mejoras de terminal no deberían impedir que la aplicación principal funcione.

Detectar el backend

Desde Python 3.13, readline.backend informa readline o editline.

import readline

print(readline.backend)

macOS suele usar libedit. GNU Readline normalmente lee ~/.inputrc, mientras libedit suele leer ~/.editrc. La sintaxis y el formato del historial pueden ser distintos.

Activar autocompletado con Tab

La configuración más simple usa rlcompleter, que completa nombres Python del namespace actual.

import readline
import rlcompleter

if readline.backend == "editline":
    readline.parse_and_bind("bind ^I rl_complete")
else:
    readline.parse_and_bind("tab: complete")

La guía de rlcompleter en Python explica el completado de identificadores. Una aplicación con comandos propios normalmente necesita un completer personalizado.

Crear un completer

La función recibe text y state. Devuelve una coincidencia por llamada y finalmente None.

import readline

COMANDOS = ["abrir", "ayuda", "configurar", "ejecutar", "listar", "salir"]

def completar(texto, estado):
    opciones = [item for item in COMANDOS if item.startswith(texto)]
    if estado < len(opciones):
        return opciones[estado]
    return None

readline.set_completer(completar)
readline.set_completer_delims(" \t\n")
readline.parse_and_bind("tab: complete")

Mantén el completer rápido. Consultar red, base de datos o un directorio enorme en cada Tab genera una interfaz lenta.

Delimitadores de completado

set_completer_delims() define qué caracteres separan la palabra actual. Rutas, nombres con punto y valores con dos puntos pueden requerir eliminar caracteres del conjunto predeterminado.

delimitadores = readline.get_completer_delims()
readline.set_completer_delims(delimitadores.replace("/", ""))

get_begidx() y get_endidx() muestran el rango completado. GNU Readline y libedit pueden devolver índices diferentes, así que prueba ambos.

Historial en memoria

La lista global de historial puede inspeccionarse y modificarse.

import readline

readline.add_history("estado")
readline.add_history("listar proyectos")

cantidad = readline.get_current_history_length()
for indice in range(1, cantidad + 1):
    print(indice, readline.get_history_item(indice))

get_history_item() usa índices desde 1. remove_history_item() y replace_history_item() usan posiciones desde cero. Confundir esas bases es un error frecuente.

No guardes secretos

Tokens, contraseñas, claves privadas, códigos de recuperación y datos personales no deben llegar al historial. Usa getpass y desactiva el historial automático alrededor de prompts sensibles.

import getpass
import readline

readline.set_auto_history(False)
try:
    secreto = getpass.getpass("Token: ")
finally:
    readline.set_auto_history(True)

El estado es global al proceso. Restáuralo siempre y nunca registres el valor.

Leer y guardar historial

Un archivo persistente permite reutilizar comandos entre sesiones.

import atexit
import os
import readline

ruta = os.path.expanduser("~/.mi_app_history")

try:
    readline.read_history_file(ruta)
except FileNotFoundError:
    pass

readline.set_history_length(1000)
atexit.register(readline.write_history_file, ruta)

Python 3.14 añade eventos de auditoría a la lectura de configuración e historial. Hooks de seguridad pueden registrar o bloquear el acceso.

Proteger el archivo

El historial puede revelar rutas, hosts y argumentos. Guárdalo en un directorio privado y usa permisos restrictivos cuando la plataforma los soporte.

from pathlib import Path
import os

ruta = Path.home() / ".mi_app_history"
if not ruta.exists():
    fd = os.open(ruta, os.O_CREAT | os.O_WRONLY, 0o600)
    os.close(fd)

Los mode bits de Unix no representan el modelo completo de seguridad en Windows.

Sesiones concurrentes

write_history_file() sobrescribe. Dos sesiones abiertas pueden perder comandos. Cuando existe, append_history_file() agrega solo entradas nuevas.

import atexit
import readline

inicio = readline.get_current_history_length()

def guardar_incremental(ruta):
    actual = readline.get_current_history_length()
    nuevos = max(0, actual - inicio)
    readline.set_history_length(1000)
    if nuevos:
        readline.append_history_file(nuevos, ruta)

atexit.register(guardar_incremental, ruta)

El append tampoco garantiza coordinación perfecta entre escritores simultáneos. Usa file locking o historial por sesión si importa la consistencia.

Limitar crecimiento

set_history_length() controla cuántas líneas se guardan. Un valor negativo significa ilimitado y puede crear un archivo cada vez mayor.

Define un límite práctico y considera rotación por bytes para comandos muy largos.

Modificar la línea actual

get_line_buffer() devuelve el texto actual. insert_text() inserta en el cursor y redisplay() actualiza la pantalla.

import readline


def insertar_default():
    if not readline.get_line_buffer():
        readline.insert_text("listar ")
        readline.redisplay()

readline.set_startup_hook(insertar_default)

El startup hook se ejecuta antes del prompt. El pre-input hook, cuando está disponible, se ejecuta después del prompt y antes de leer caracteres.

No insertes contenido no confiable

Un texto insertado puede parecer un comando listo para ejecutar. No construyas automáticamente órdenes con contenido remoto, nombres de archivo maliciosos o datos pegados sin validar. El usuario debe conservar el control antes de pulsar Enter.

Archivos de configuración

read_init_file() carga un archivo y parse_and_bind() aplica una línea.

if readline.backend == "readline":
    readline.parse_and_bind("set editing-mode vi")
else:
    readline.parse_and_bind("bind -v")

No cargues configuración desde ubicaciones no confiables. Puede definir macros y cambiar atajos importantes.

Integración con input()

Después de importar y configurar el módulo, llamadas normales a input() obtienen edición e historial.

while True:
    try:
        comando = input("app> ").strip()
    except EOFError:
        break
    except KeyboardInterrupt:
        print()
        continue

    if comando == "salir":
        break
    ejecutar(comando)

Maneja EOF y Ctrl+C sin mostrar un traceback. La guía de signal en Python explica interrupciones y cierre cooperativo.

El nuevo REPL

La documentación actual indica que el nuevo REPL de Python 3.13 no usa readline. La variable PYTHON_BASIC_REPL activa el prompt básico compatible.

La limitación afecta al nuevo prompt del intérprete. Aplicaciones propias que usan input() aún pueden configurar readline.

Pruebas

Prueba GNU Readline y libedit, ausencia del módulo, archivo inexistente o sin permiso, sesiones concurrentes, Unicode, cero y varias sugerencias, Ctrl+C, EOF y terminal no interactivo.

Los pseudo-terminales ayudan a automatizar estas pruebas. Un artículo posterior de este lote cubre pty en Unix.

Errores comunes

Los fallos frecuentes son asumir GNU Readline en macOS, compartir configuración incompatible, guardar secretos, dejar historial ilimitado, sobrescribir sesiones, ejecutar I/O lento en el completer, confundir índices y exigir el módulo en plataformas no soportadas.

Conclusión

readline convierte prompts básicos en interfaces productivas con edición, historial y autocompletado. Su uso fiable requiere detectar backend, proteger el historial, limitar almacenamiento y ofrecer fallback.

Mantén completadores rápidos y prueba GNU Readline y libedit. Consulta la documentación oficial de readline y el manual GNU Readline.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Rack de servidores que representa el balanceo de conexiones con SO_REUSEPORT_LB en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: reparte conexiones entre workers

    Aprende SO_REUSEPORT_LB en Python para distribuir conexiones entre workers con pruebas, portabilidad y cierre ordenado.

    Ler mais

    Tempo de leitura: 5 minutos
    11/10/2026
    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