codeop no Python: compile entradas interativas

Publicado em: 04/08/2026
Tempo de leitura: 6 minutos
Terminal de programação representando compilação de entradas interativas com codeop no Python

Um console interativo precisa decidir se o texto digitado já forma uma instrução Python completa. Depois de if condicao:, por exemplo, ele deve mostrar o prompt de continuação em vez de tentar executar imediatamente. O módulo codeop no Python fornece as funções usadas para compilar entradas de REPL, distinguir código completo de código incompleto e preservar instruções __future__ entre comandos.

Neste guia, você aprenderá a usar compile_command(), Compile e CommandCompiler, tratar erros e montar um loop interativo controlado. O conteúdo complementa nossos artigos sobre bytecode com dis, escopos com symtable, traceback, inspect e Python IDLE.

O problema de um REPL

O ciclo read-eval-print lê texto, compila, executa e mostra o resultado. A dificuldade é que uma linha pode ser válida, inválida ou apenas incompleta.

if usuario_ativo:
    liberar_acesso()

Depois da primeira linha, o parser espera um bloco indentado. Um console precisa responder com ..., acumular mais texto e tentar novamente.

compile_command()

codeop.compile_command() tenta compilar uma string como entrada interativa.

import codeop

resultado = codeop.compile_command("x = 10")
print(resultado)

Quando o texto está completo e correto, a função retorna um objeto de código. Quando é um prefixo válido, mas incompleto, retorna None. Sintaxe inválida gera uma exceção.

Distinguir incompleto de inválido

import codeop

entradas = [
    "x = 10",
    "if x > 0:",
    "if :",
]

for texto in entradas:
    try:
        codigo = codeop.compile_command(texto)
    except SyntaxError as erro:
        print("Inválido:", erro)
    else:
        if codigo is None:
            print("Incompleto")
        else:
            print("Completo")

Essa diferença é a principal razão para usar codeop em vez de chamar apenas compile().

O parâmetro filename

O nome do arquivo aparece em tracebacks e mensagens de sintaxe.

codigo = codeop.compile_command(
    "resultado = 10 / 0",
    filename="<console-admin>",
)

Escolha um identificador que indique a origem, como <console>, <regra-42> ou o caminho real de um script. Não inclua segredos ou dados pessoais no filename.

O parâmetro symbol

symbol controla o modo de compilação:

  • single: uma instrução interativa, valor padrão;
  • exec: sequência de instruções;
  • eval: uma expressão.
expressao = codeop.compile_command(
    "10 * 2",
    symbol="eval",
)

print(eval(expressao, {}))

Qualquer outro valor gera ValueError. Escolha o modo de acordo com a interface e não permita que o usuário o controle livremente sem validação.

single e exibição de resultados

O modo single foi projetado para comportamento interativo. Expressões podem ser enviadas ao display hook do interpretador, semelhante ao console padrão.

codigo = codeop.compile_command("2 + 3", symbol="single")
exec(codigo)

Em uma aplicação web, provavelmente será melhor capturar a saída e definir uma representação própria.

Construir um acumulador de linhas

Um loop simples mantém um buffer até o código ficar completo.

import codeop

buffer = []

while True:
    prompt = "... " if buffer else ">>> "
    linha = input(prompt)
    buffer.append(linha)
    fonte = "\n".join(buffer)

    try:
        codigo = codeop.compile_command(fonte)
    except (SyntaxError, OverflowError, ValueError) as erro:
        print(f"Erro: {erro}")
        buffer.clear()
        continue

    if codigo is None:
        continue

    exec(codigo, globals(), globals())
    buffer.clear()

Esse exemplo demonstra a mecânica, mas executar entrada arbitrária com exec() é perigoso. Um console real precisa de isolamento e autorização.

Linhas em branco

No console tradicional, uma linha vazia encerra um bloco. Seu loop deve preservar o newline e permitir que compile_command() faça a decisão.

if buffer and linha == "":
    fonte = "\n".join(buffer) + "\n"

Teste funções, classes, try, with, decorators, strings multilinha e parênteses abertos.

Erros possíveis

A função pode gerar SyntaxError para sintaxe inválida, OverflowError ou ValueError para determinados literais e parâmetros.

try:
    codigo = codeop.compile_command(fonte)
except SyntaxError as erro:
    mostrar_syntax_error(erro)
except (OverflowError, ValueError) as erro:
    mostrar_erro_compilacao(erro)

A documentação oficial de codeop também alerta para casos raros em que o parser pode aceitar uma parte inicial e ignorar símbolos posteriores. Não use a função como validador de segurança.

CommandCompiler

CommandCompiler é um objeto chamável com interface semelhante a compile_command().

compilador = codeop.CommandCompiler()

codigo = compilador(
    "x = 1",
    filename="<sessao>",
    symbol="single",
)

A diferença importante é que a instância lembra instruções __future__ compiladas anteriormente.

Preservar __future__

Um REPL deve manter opções de compilação habilitadas pelo usuário para comandos seguintes.

compilador = codeop.CommandCompiler()

primeiro = compilador(
    "from __future__ import annotations",
    "<sessao>",
    "single",
)
exec(primeiro, ambiente)

segundo = compilador(
    "def processar(valor: TipoAindaNaoDefinido): pass",
    "<sessao>",
    "single",
)
exec(segundo, ambiente)

Usar uma nova instância em cada entrada perderia esse estado. Mantenha um compilador por sessão.

A classe Compile

Compile se comporta de forma semelhante ao built-in compile() e também memoriza flags futuras.

compilador = codeop.Compile()

codigo = compilador(
    "resultado = 2 + 2",
    "<entrada>",
    "exec",
)

Compile é útil quando a aplicação já sabe que o código está completo. CommandCompiler acrescenta a detecção de entrada incompleta.

codeop e o módulo code

O módulo code oferece classes prontas para consoles e interpretadores interativos. A documentação oficial de code descreve InteractiveInterpreter e InteractiveConsole.

Use codeop diretamente quando precisa controlar protocolo, interface, armazenamento de sessão ou transporte. Para um console convencional embutido, as classes de code reduzem trabalho.

Capturar stdout e stderr

Uma interface gráfica ou remota precisa capturar saídas.

from contextlib import redirect_stdout, redirect_stderr
from io import StringIO

saida = StringIO()

with redirect_stdout(saida), redirect_stderr(saida):
    exec(codigo, ambiente, ambiente)

print(saida.getvalue())

Redirecionamento global não é seguro entre várias threads. Em serviços concorrentes, execute cada sessão em processo separado.

Ambiente de execução

Passar o mesmo dicionário como globals e locals preserva variáveis entre comandos.

ambiente = {
    "__name__": "__console__",
}

exec(codigo, ambiente, ambiente)

Esse dicionário não é uma sandbox. Mesmo removendo __builtins__, objetos disponíveis podem oferecer caminhos para filesystem, rede ou introspecção.

Não existe sandbox segura apenas com exec

Código Python arbitrário deve ser considerado equivalente a acesso ao processo. Ele pode ler arquivos, consumir CPU e memória, criar threads, abrir sockets e encerrar o programa.

Para entrada não confiável, use um processo ou container isolado, usuário sem privilégios, filesystem restrito, rede bloqueada, limites de CPU e memória, timeout e descarte completo após a execução.

Timeouts

Uma thread não consegue interromper de maneira segura qualquer código Python ou nativo. Execute a avaliação em subprocesso e finalize o processo ao exceder o prazo.

from subprocess import run, TimeoutExpired

try:
    run(
        ["python", "runner_isolado.py"],
        input=fonte,
        text=True,
        timeout=3,
        check=True,
    )
except TimeoutExpired:
    print("Tempo excedido")

O runner ainda precisa de limites do sistema operacional.

Sessões concorrentes

Cada sessão deve possuir buffer, CommandCompiler e namespace próprios. Não compartilhe o dicionário de globals entre usuários.

class Sessao:
    def __init__(self):
        self.buffer = []
        self.compilador = codeop.CommandCompiler()
        self.ambiente = {"__name__": "__console__"}

Em aplicações distribuídas, armazene apenas código e resultados necessários. Objetos Python arbitrários não são serializáveis de forma segura.

Formatar SyntaxError

SyntaxError inclui filename, linha, offset, texto e mensagem.

except SyntaxError as erro:
    print(erro.filename, erro.lineno, erro.offset)
    print(erro.text)
    print(erro.msg)

O módulo traceback pode produzir uma representação consistente. Não exponha caminhos internos em interfaces públicas.

Auditoria

Um console administrativo deve registrar usuário, horário, origem, duração, status e um hash do código. Evite gravar segredos digitados acidentalmente e defina retenção.

Use autenticação forte, autorização explícita e aprovação adicional para ambientes críticos. A conveniência de um REPL remoto aumenta a superfície de ataque.

Testar o detector de completude

casos_incompletos = [
    "if True:",
    "def funcao(x):",
    "(",
    "'''texto",
]

for fonte in casos_incompletos:
    assert codeop.compile_command(fonte) is None

Adicione casos completos, inválidos, Unicode, decorators, comprehensions, match, async e sintaxe da versão suportada.

Compatibilidade entre versões

codeop usa o parser do interpretador atual. Uma entrada válida no Python 3.14 pode ser inválida no 3.11. O estado de __future__ também depende das features daquela versão.

Registre a versão da sessão e execute o código no mesmo runtime usado para validar.

Erros frequentes

  • Confundir None com erro de sintaxe.
  • Criar novo CommandCompiler para cada linha.
  • Perder newlines ao acumular o buffer.
  • Executar código de usuário no processo principal.
  • Considerar remoção de built-ins uma sandbox.
  • Compartilhar namespace entre sessões.
  • Não impor timeout e limites de recursos.
  • Expor tracebacks e caminhos internos.

Boas práticas

  • Use um compilador por sessão.
  • Trate completo, incompleto e inválido separadamente.
  • Preserve linhas e filename coerentes.
  • Prefira o módulo code para consoles convencionais.
  • Isole execução não confiável em processo ou container.
  • Aplique limites de CPU, memória, rede e tempo.
  • Audite consoles administrativos.
  • Teste toda a sintaxe suportada.

Conclusão

O módulo codeop no Python resolve a parte delicada de um REPL: descobrir se a entrada já forma código completo e manter opções de compilação futuras entre comandos. compile_command() atende verificações pontuais, enquanto CommandCompiler preserva estado por sessão.

A compilação é apenas uma etapa. Executar o objeto resultante continua tão poderoso quanto executar um script. Ao separar análise de execução, isolar sessões, impor recursos e proteger a interface, você pode construir consoles, notebooks e ferramentas educacionais sem transformar conveniência em acesso irrestrito ao servidor.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor analisando estrutura de código e tabelas de símbolos com symtable no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    symtable no Python: escopos e símbolos

    Aprenda symtable no Python para analisar escopos, símbolos, globals, nonlocals, closures, imports, annotations e type parameters.

    Ler mais

    Tempo de leitura: 7 minutos
    03/08/2026
    Monitor com código binário representando análise de bytecode com dis no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    dis no Python: entenda o bytecode

    Aprenda dis no Python para desmontar bytecode, analisar instruções, caches adaptativos, posições, tracebacks e detalhes do CPython.

    Ler mais

    Tempo de leitura: 7 minutos
    03/08/2026
    Tela de erro representando diagnóstico de crashes e deadlocks com faulthandler no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    faulthandler no Python: diagnostique travamentos

    Aprenda faulthandler no Python para diagnosticar crashes, deadlocks e timeouts com pilhas de threads e código nativo.

    Ler mais

    Tempo de leitura: 8 minutos
    03/08/2026
    Notebook com código representando análise de traceback e depuração no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    traceback no Python: erros e pilha

    Aprenda traceback no Python para capturar, formatar e registrar pilhas de erro com segurança, sem vazar dados ou reter memória.

    Ler mais

    Tempo de leitura: 6 minutos
    03/08/2026
    Análise de software representando introspecção de objetos com inspect no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect no Python: introspecção de objetos

    Aprenda inspect no Python para analisar funções, classes, assinaturas, código-fonte, decorators, generators e frames com segurança.

    Ler mais

    Tempo de leitura: 8 minutos
    02/08/2026
    Módulo de memória representando referências fracas e caches no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    weakref no Python: referências fracas

    Aprenda weakref no Python para criar referências fracas, caches automáticos, observadores e finalizadores sem reter objetos na memória.

    Ler mais

    Tempo de leitura: 9 minutos
    28/07/2026