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

    Código Python en una pantalla
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    T-strings y templatelib: interpolación estructurada

    Aprende t-strings y string.templatelib en Python para interpolación estructurada, renderizado seguro, logs y procesamiento personalizado.

    Ler mais

    Tempo de leitura: 6 minutos
    22/10/2026
    Base SQLite y código Python para exportación selectiva con iterdump
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    SQLite iterdump: crea dumps selectivos en Python

    Aprende sqlite3.Connection.iterdump con filtro para crear dumps SQLite selectivos, validar dependencias y restaurar con seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    21/10/2026
    Código y archivos que representan compresión de datos con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: Zstandard rápido en Python

    Aprende compression.zstd en Python para comprimir archivos y datos con velocidad, streaming, integridad y seguridad.

    Ler mais

    Tempo de leitura: 7 minutos
    21/10/2026
    Archivo de configuración y estructura de código para ConfigParser en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ConfigParser: lee INI sin sección en Python

    Aprende ConfigParser sin sección en Python con validación, tipos, compatibilidad, capas de configuración y escritura segura.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026
    Datos binarios y memoria de ordenador representando memoryview en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview count e index: busca sin copiar

    Aprende memoryview count e index en Python para buscar valores en buffers sin copias y validar datos binarios con eficiencia.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026
    Desarrolladora trabajando con código Python y validación de datos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    map strict: detecta iterables desalineados

    Aprende map strict en Python para validar iterables, evitar truncamientos silenciosos y detectar datos desalineados.

    Ler mais

    Tempo de leitura: 4 minutos
    19/10/2026