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 NoneLa 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
SystemExityKeyboardInterrupt. - 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.







