codeop en Python: compila entrada interactiva

Publicado el: 27/08/2026
Tempo de leitura: 7 minutos
Vivid close-up of code on a computer screen showcasing programming details.

El módulo codeop ayuda a compilar código Python recibido de forma interactiva. Su capacidad principal es distinguir tres estados: código completo y válido, código definitivamente inválido y código incompleto que puede volverse válido cuando el usuario escriba más líneas. Esta decisión es esencial en REPLs, consolas embebidas, notebooks, editores educativos, shells administrativas y herramientas que ejecutan comandos multilínea.

Una llamada normal a compile() no está diseñada para decidir si un bloque solo necesita más entrada. codeop encapsula heurísticas compatibles con el comportamiento interactivo de Python y también puede recordar efectos de from __future__ entre comandos mediante CommandCompiler.

El problema del código incompleto

Considera una función escrita línea por línea:

def doble(valor):
    return valor * 2

Después de la primera línea, la consola no debería rechazar el código; debería mostrar un prompt de continuación. Después del cuerpo y del terminador adecuado, puede compilar y ejecutar.

compile_command

compile_command(source, filename="<input>", symbol="single") determina el estado actual.

import codeop

resultado = codeop.compile_command("1 + 2")
print(resultado)

El código completo devuelve un code object. El código aparentemente incompleto devuelve None. El código definitivamente inválido lanza una excepción de sintaxis.

Maneja los tres resultados

def analizar(fuente):
    try:
        codigo = codeop.compile_command(fuente)
    except (SyntaxError, OverflowError, ValueError) as error:
        return "invalido", error

    if codigo is None:
        return "incompleto", None
    return "completo", codigo

No trates None como error. Indica que la interfaz debe seguir recogiendo líneas.

El parámetro symbol

symbol controla el tipo de entrada esperado. Los valores comunes son single, exec y eval.

  • single representa entrada interactiva y puede activar la visualización de expresiones.
  • exec representa un conjunto de instrucciones.
  • eval acepta una expresión.
expresion = codeop.compile_command(
    "10 * 4",
    filename="<calculadora>",
    symbol="eval",
)

Elige el modo conscientemente

Una consola tradicional suele usar single. Un editor de celdas puede usar exec. Una calculadora puede usar eval, aunque evaluar expresiones no confiables sigue siendo peligroso.

El modo define gramática y comportamiento del code object; no aporta seguridad.

Acumula líneas

Un REPL mínimo conserva un buffer hasta que la entrada queda completa.

import codeop

lineas = []
while True:
    prompt = "... " if lineas else ">>> "
    linea = input(prompt)
    lineas.append(linea)
    fuente = "\n".join(lineas)

    try:
        codigo = codeop.compile_command(fuente)
    except SyntaxError as error:
        print(f"error: {error}")
        lineas.clear()
        continue

    if codigo is None:
        continue

    exec(codigo, namespace)
    lineas.clear()

Una consola real también necesita políticas para EOF, KeyboardInterrupt, historial, encoding, salida y excepciones de ejecución.

Líneas en blanco

Las consolas interactivas usan líneas en blanco para finalizar ciertos bloques compuestos. Conserva esa semántica en lugar de borrar toda entrada vacía.

No apliques strip() al buffer completo porque puede alterar indentación y finalización.

Indentación

El whitespace inicial forma parte de la sintaxis. Conserva exactamente el texto introducido.

Un editor puede sugerir indentación, pero reescribir silenciosamente la fuente antes de compilar puede producir resultados inesperados.

SyntaxError

El código definitivamente inválido genera SyntaxError.

try:
    codeop.compile_command("if :")
except SyntaxError as error:
    print(error.msg, error.lineno, error.offset)

Muestra filename, línea, columna y un fragmento corto sin exponer buffers de otros usuarios.

Otros fallos de compilación

Entradas extremas pueden generar OverflowError, ValueError o fallos de recursos. Limita bytes, líneas, nesting y tiempo antes de aceptar fuente pública.

No aumentes automáticamente el límite de recursión ante código hostil o mal formado.

Filename simbólico

El argumento filename aparece en tracebacks y diagnósticos.

codigo = codeop.compile_command(
    fuente,
    filename="<consola-admin>",
)

Usa un nombre que identifique sesión o celda sin revelar datos personales. Un ID estable ayuda a recuperar el fuente correspondiente.

Integración con linecache

El código generado dinámicamente no tiene archivo normal. Para mostrar líneas correctas en tracebacks, conserva la fuente bajo su filename simbólico e intégrala cuidadosamente con el cache.

Consulta linecache en Python.

CommandCompiler

CommandCompiler es una alternativa con estado a llamar directamente a compile_command().

import codeop

compilador = codeop.CommandCompiler()
codigo = compilador("x = 10")

Está diseñado para comandos sucesivos pertenecientes a una misma sesión interactiva.

Declaraciones future

En un módulo, from __future__ import ... afecta el código posterior. En una consola, debería afectar comandos posteriores de la misma sesión.

CommandCompiler recuerda las flags observadas en compilaciones anteriores.

compilador = codeop.CommandCompiler()
compilador("from __future__ import annotations")
siguiente = compilador("def f(x: Tipo) -> Otro: pass")

Un compilador por sesión

No compartas un CommandCompiler entre usuarios independientes. Flags futuras y estado podrían filtrarse entre contextos.

Crea uno por consola, kernel, conexión o tenant.

Compilación frente a ejecución

Compilar valida sintaxis y crea bytecode; no ejecuta el cuerpo. La ejecución comienza con exec() o eval().

La compilación consume recursos, pero la ejecución introduce los riesgos mayores: filesystem, red, imports, subprocesses, introspección y código nativo.

No es un sandbox

codeop no restringe Python. Usar modo eval, reducir builtins o rechazar algunos nodos AST no crea un lenguaje seguro.

Para código no confiable, usa aislamiento fuerte: proceso separado, usuario sin privilegios, filesystem restringido, red controlada, límites de CPU y memoria y deadline duro.

Namespaces persistentes

Una consola suele conservar un diccionario de globals entre comandos.

namespace = {"__name__": "__console__"}
exec(codigo, namespace, namespace)

Esto permite persistir variables y funciones, pero también acumula memoria, archivos y referencias.

Separa usuarios

Cada usuario necesita su propio namespace. Compartir globals permite leer o modificar datos de otra sesión.

Destruye el proceso worker al terminar si necesitas cleanup fuerte. Threads y recursos nativos pueden sobrevivir al borrado del diccionario.

Visualización de expresiones

El modo single coopera con el mecanismo de display del intérprete. Una consola personalizada puede ajustar sys.displayhook.

Usa reprlib para limitar valores gigantes o recursivos. Consulta reprlib en Python.

Resultados grandes

Una expresión puede producir una representación enorme. Limita bytes, líneas y tiempo de render.

El método __repr__ de un objeto hostil puede ejecutar lógica arbitraria. No renderices objetos no confiables en un proceso privilegiado.

Captura de stdout y stderr

Las consolas web suelen capturar salida. contextlib.redirect_stdout() cambia estado global y no es seguro para múltiples sesiones concurrentes en un intérprete.

Un proceso worker por sesión ofrece ownership claro de streams, señales, límites y terminación.

Excepciones de ejecución

Después de compilar, captura errores en el boundary correcto.

try:
    exec(codigo, namespace, namespace)
except SystemExit:
    cerrar_sesion()
except Exception:
    traceback.print_exc()

Define comportamiento explícito para KeyboardInterrupt, SystemExit y cancelación. Evita un handler que impida el shutdown de la aplicación.

Timeout de ejecución

Una thread no puede detener de forma segura cualquier código Python o nativo. Usa procesos descartables y termina el worker al vencer el plazo.

En sesiones internas confiables, un dump de faulthandler antes de terminar puede conservar contexto.

Async y top-level await

Una consola asíncrona puede querer await en nivel superior. Esto requiere flags de compilación y soporte del event loop más allá del comportamiento básico de codeop.

Usa APIs de la versión o framework interactivo objetivo.

Notebooks

Los kernels de notebook gestionan sesiones, historial, protocolos de display, IDs de celda, ejecución async, almacenamiento de fuente y salidas ricas. codeop resuelve únicamente completitud sintáctica y flags futuras.

Un loop de input() no es un kernel completo.

Consolas administrativas

Una consola embebida es una superficie de gestión de alto riesgo. Exige autenticación fuerte, autorización, auditoría, red privada y acceso temporal.

Prefiere comandos administrativos explícitos a ejecución arbitraria de Python.

Historial

Si almacenas comandos, cifra y define retención. Los usuarios pueden introducir tokens, datos personales y secretos accidentalmente.

Ofrece borrado y evita registrar sesiones sensibles por defecto.

Límites de entrada

Limita bytes, líneas, nesting y cuánto tiempo puede permanecer un buffer incompleto. Un cliente puede ocupar memoria y conexión indefinidamente.

Después del deadline, descarta el buffer o cierra la sesión.

Cancela el buffer actual

Ofrece un comando para limpiar solo el bloque sin terminar.

if linea == ":cancelar":
    lineas.clear()
    continue

Elige comandos fuera de la sintaxis habitual y documéntalos.

Autocomplete

La detección de completitud no ofrece autocomplete. Las sugerencias requieren tokens, AST parcial, symbol tables o un language server.

Evita evaluar descriptors y properties solo para descubrir atributos, porque pueden producir side effects.

Compatibilidad de versión

La gramática y heurísticas evolucionan. Prueba la consola en cada versión soportada.

Usa el codeop incluido con el intérprete en lugar de copiar lógica interna antigua.

Pruebas

Cubre expresiones, funciones y clases multilínea, decorators, paréntesis abiertos, strings triples, comprehensions, try/except, pattern matching, async, errores definitivos, EOF, interrupción y future imports.

Verifica también que sesiones independientes no compartan namespaces ni flags.

Errores comunes

Los fallos frecuentes son tratar None como error, eliminar indentación, elegir el symbol incorrecto, compartir CommandCompiler, ejecutar código hostil en el host, capturar streams globales, aceptar buffers ilimitados y confundir compilación con sandbox.

Conclusión

codeop aporta la lógica necesaria para decidir si una entrada interactiva está completa, incompleta o inválida. Usa compile_command() para comprobaciones sin estado y CommandCompiler para sesiones que conservan flags de __future__.

Separa namespaces, conserva fuente para tracebacks, impone límites y aísla la ejecución. Consulta la documentación oficial de codeop y symtable en Python para analizar nombres de una sesión.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    compileall en Python: genera bytecode .pyc

    Aprende compileall en Python para generar .pyc, validar sintaxis, compilar en paralelo y controlar optimización, paths y builds reproducibles.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    linecache en Python: lee líneas del código

    Aprende linecache en Python para recuperar líneas de código, actualizar cache, soportar tracebacks y loaders, conservar indentación y proteger rutas.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    faulthandler en Python: diagnostica fallos

    Aprende faulthandler en Python para diagnosticar crashes, deadlocks, señales fatales, timeouts y bloqueos con dumps de todas las threads.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    A detailed image of a reticulated python showcasing its patterned scales and intricate skin texture.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    symtable en Python: analiza scopes

    Aprende symtable en Python para analizar scopes, locals, globals, parámetros, imports, nonlocals, closures y namespaces del compilador.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    dis en Python: entiende el bytecode

    Aprende dis en Python para inspeccionar bytecode, jumps, stack effects, caches adaptativos y optimizaciones sin depender de internals inestables.

    Ler mais

    Tempo de leitura: 5 minutos
    27/08/2026
    Gold Bitcoin coins displayed on a sparkling gold texture, representing digital currency and finance.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tokenize en Python: lee tokens del código

    Aprende tokenize en Python para leer tokens, comentarios, encoding, indentación y posiciones, además de transformar y reconstruir código con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026