code en Python: crea un REPL personalizado

Publicado el: 12/08/2026
Tempo de leitura: 6 minutos
Terminal interactivo que representa un REPL personalizado creado con el módulo code en Python

El módulo code en Python proporciona las clases básicas para crear bucles de lectura, evaluación y presentación, conocidos como REPL. Con InteractiveInterpreter e InteractiveConsole, una aplicación puede incorporar un prompt Python, mantener un namespace, capturar errores y decidir cómo almacenar bloques incompletos.

Es útil en herramientas educativas, depuradores, notebooks sencillos, consolas administrativas y entornos locales de diagnóstico. Sin embargo, ejecuta código Python real. Un REPL integrado no es un sandbox para usuarios no confiables: imports, archivos, red, procesos, introspección y objetos presentes en el namespace pueden quedar accesibles.

InteractiveInterpreter e InteractiveConsole

InteractiveInterpreter gestiona compilación, estado y ejecución, pero no incluye prompts ni buffering multilínea. InteractiveConsole hereda esa base y añade una experiencia parecida al intérprete estándar, con prompt principal y secundario.

from code import InteractiveConsole

consola = InteractiveConsole()
consola.interact(
    banner='Consola de diagnóstico',
    exitmsg='Consola cerrada',
)

El REPL se ejecuta dentro del proceso actual. Cada orden dispone de los mismos permisos del sistema operativo que la aplicación.

Definir un namespace personalizado

El argumento locals recibe un mapping utilizado como namespace. Permite exponer objetos concretos y conservar variables entre órdenes.

from code import InteractiveConsole

contexto = {
    'estado': lambda: {'cola': 4, 'salud': 'ok'},
    'version': '2.1.0',
}
consola = InteractiveConsole(locals=contexto)

Un mapping reducido mejora la ergonomía, pero no crea una frontera de seguridad. La introspección y los objetos alcanzables pueden proporcionar rutas indirectas a capacidades sensibles.

Compilar y ejecutar con runsource()

runsource() compila y ejecuta una cadena. Su retorno indica si hace falta más entrada: True significa bloque incompleto; False significa ejecutado o rechazado.

from code import InteractiveInterpreter

interprete = InteractiveInterpreter()
necesita_mas = interprete.runsource('for i in range(3):')
print(necesita_mas)

Este protocolo permite que una interfaz cambie entre el prompt principal y el de continuación.

Acumular bloques con push()

InteractiveConsole.push() añade una línea a un buffer interno e intenta compilar el contenido completo.

from code import InteractiveConsole

consola = InteractiveConsole()
print(consola.push('def doble(x):'))
print(consola.push('    return x * 2'))
print(consola.push(''))
print(consola.push('doble(5)'))

Cuando el bloque termina o es inválido, el buffer se reinicia. resetbuffer() descarta manualmente una entrada pendiente tras una cancelación.

Detectar entrada incompleta

compile_command() intenta tomar la misma decisión que el intérprete real. Devuelve un objeto de código para una entrada completa y válida, None para una entrada incompleta y lanza SyntaxError para una entrada completa pero incorrecta.

from code import compile_command

resultado = compile_command('if activo:', symbol='single')
assert resultado is None

La guía de codeop en Python explica el estado del compilador y las flags de __future__.

Personalizar la entrada con raw_input()

Una subclase puede sobrescribir raw_input() para leer desde un editor gráfico, socket local, navegador o cola interna.

class ConsolaCola(InteractiveConsole):
    def __init__(self, cola, **kwargs):
        super().__init__(**kwargs)
        self.cola = cola

    def raw_input(self, prompt=''):
        mostrar_prompt(prompt)
        return self.cola.get()

No conectes este método directamente a Internet. Una consola remota necesita autenticación fuerte, red restringida, proceso dedicado sin privilegios, límites de recursos, timeout y auditoría.

Capturar errores con write()

Los errores de sintaxis y tracebacks se escriben en sys.stderr por defecto. Sobrescribe write() para almacenarlos o enviarlos a otra interfaz.

class ConsolaCaptura(InteractiveConsole):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.errores = []

    def write(self, data):
        self.errores.append(data)

No devuelvas tracebacks completos a usuarios externos: pueden revelar rutas, módulos, configuración y datos. Guarda el detalle en logs protegidos.

Errores de sintaxis y runtime

showsyntaxerror() formatea problemas de compilación; showtraceback() trata excepciones de ejecución y elimina el frame interno del intérprete. Las excepciones encadenadas se muestran completas desde Python 3.5.

Puedes sobrescribir estos métodos para generar JSON o mensajes localizados, conservando la excepción original para diagnóstico.

Ejecutar objetos compilados con runcode()

runcode() ejecuta un objeto de código. Las excepciones ordinarias se muestran, pero SystemExit puede propagarse. KeyboardInterrupt también puede aparecer fuera del punto previsto.

compilado = compile('respuesta = 6 * 7', '<console>', 'exec')
interprete = InteractiveInterpreter()
interprete.runcode(compilado)
print(interprete.locals['respuesta'])

La aplicación anfitriona necesita una política clara para cierre, cancelación y limpieza.

local_exit desde Python 3.13

InteractiveConsole e interact() aceptan local_exit=True. Entonces exit() y quit() regresan a la aplicación en lugar de propagar SystemExit por todo el proceso.

consola = InteractiveConsole(local_exit=True)
consola.interact()

Esto facilita la integración, pero el código aún puede llamar os._exit(), alterar threads, cerrar recursos o dañar el proceso.

Usar code.interact()

code.interact() crea una consola temporal. Acepta banner, función de lectura, namespace, mensaje de salida y configuración de cierre local.

import code

estado = {'pedido_id': 123, 'modo': 'debug'}
code.interact(
    banner='Sesión de soporte local',
    local=estado,
    exitmsg='',
    local_exit=True,
)

Puede activarse mediante una flag de desarrollo. Comprueba que permanezca deshabilitada en producción.

REPL para depuración local

Es posible iniciar una sesión confiable en un punto concreto y exponer un objeto seleccionado.

def diagnosticar(objeto):
    code.interact(
        local={'objeto': objeto, 'resumen': objeto.resumen},
        local_exit=True,
    )

No incluyas contraseñas, tokens ni objetos con métodos destructivos. Incluso un terminal local puede compartirse o grabarse.

Persistencia y memoria

Variables, funciones y clases permanecen en el mapping de locales mientras viva la instancia. Esto favorece la exploración, pero puede retener objetos grandes.

Ofrece un reset, elimina nombres temporales o recrea la consola periódicamente. El artículo sobre weakref en Python explica retención, aunque la política de ciclo de vida debe ser explícita.

Limitaciones de pickle

Las funciones y clases definidas pertenecen al namespace indicado. Solo suelen ser serializables con pickle si ese namespace corresponde a un módulo importable existente. No esperes persistir una sesión arbitraria.

Guarda datos en formatos definidos y reconstruye comportamiento desde código versionado. Nunca cargues pickle no confiable.

El aislamiento real requiere otro proceso

Eliminar __builtins__ o bloquear algunos nombres no crea un sandbox. La introspección y los tipos alcanzables hacen que estos filtros sean insuficientes.

Para reducir impacto, ejecuta el REPL en un proceso o container desechable, con usuario sin privilegios, sistema de archivos limitado, red deshabilitada, límites de CPU y memoria y timeout estricto. Aun así, trátalo como ejecución de código.

Probar una consola personalizada

Prueba los retornos de push(), cambios del namespace, salida capturada, errores, bloques incompletos, EOF y cierre local.

def test_consola():
    consola = ConsolaCaptura(locals={})
    assert consola.push('x = 10') is False
    assert consola.locals['x'] == 10
    assert consola.push('for i in range(2):') is True
    consola.resetbuffer()

Inyecta servicios falsos y no conectes pruebas unitarias a recursos de producción.

Errores frecuentes

  • Tratar un namespace reducido como sandbox.
  • Exponer el REPL en un puerto público.
  • Dejar consolas de debug activas en producción.
  • Mostrar tracebacks sensibles.
  • Ignorar SystemExit y KeyboardInterrupt.
  • Retener objetos grandes indefinidamente.
  • Intentar guardar funciones de sesión con pickle.

Buenas prácticas

  • Usa el módulo solo para operadores confiables.
  • Prefiere procesos desechables para aislamiento.
  • Controla namespace y duración.
  • Captura salida mediante write().
  • Limita CPU, memoria, red y tiempo.
  • Desactiva la función por defecto en producción.
  • Audita sin registrar secretos.

Conclusión

El módulo code en Python facilita REPLs personalizados con namespaces persistentes, buffering, prompts, errores y cierre local. Es una base poderosa para herramientas de desarrollo y diagnóstico.

Su significado de seguridad debe ser claro: la entrada es código ejecutable. Para usuarios no confiables, utiliza aislamiento del sistema operativo, no filtros del namespace. Consulta la documentación oficial del módulo code y la documentación de codeop.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código de aplicación web que representa WSGI con wsgiref en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    wsgiref en Python: aplicaciones WSGI

    Aprende wsgiref en Python para crear y validar aplicaciones WSGI, probar environ y headers, enrutar solicitudes y ejecutar un servidor

    Ler mais

    Tempo de leitura: 4 minutos
    12/08/2026
    Protocolo seguro de Internet que representa preparación Unicode con stringprep en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    stringprep en Python: prepara Unicode

    Aprende stringprep en Python para aplicar tablas RFC 3454, mapear Unicode, rechazar caracteres prohibidos y validar reglas bidireccionales.

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Red de conexiones que representa I/O no bloqueante con selectors en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    selectors en Python: I/O no bloqueante

    Aprende selectors en Python para monitorizar muchos sockets, eventos de lectura y escritura, timeouts y conexiones no bloqueantes con seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    11/08/2026
    Flujo de datos en red que representa contexto asíncrono con contextvars en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    contextvars en Python: contexto asíncrono

    Aprende contextvars en Python para guardar estado por tarea, evitar fugas en asyncio, copiar contextos y restaurar valores con tokens.

    Ler mais

    Tempo de leitura: 5 minutos
    11/08/2026
    Código de programación que representa operaciones como funciones con operator en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    operator en Python: operaciones como funciones

    Aprende operator en Python para usar operaciones como funciones, ordenar campos, acceder a elementos, llamar métodos y crear pipelines claros.

    Ler mais

    Tempo de leitura: 4 minutos
    11/08/2026
    Alfabeto tridimensional que representa normalización Unicode con unicodedata en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    unicodedata en Python: normaliza Unicode

    Aprende unicodedata en Python para normalizar Unicode, consultar nombres, categorías, números, marcas combinantes y ancho de visualización.

    Ler mais

    Tempo de leitura: 5 minutos
    11/08/2026