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

    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
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python en pantalla que representa inspección de módulos y paquetes
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    inspect.ispackage: identifica paquetes Python

    Aprende inspect.ispackage en Python para identificar paquetes, explorar módulos y crear herramientas de introspección seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    05/10/2026