O módulo codeop ajuda a compilar trechos de Python recebidos de forma interativa. Seu principal diferencial é distinguir três situações: código completo e válido, código inválido e código que ainda está incompleto, mas pode se tornar válido quando o usuário digitar mais linhas. Essa capacidade é essencial em REPLs, consoles embutidos, notebooks, shells administrativos, editores educacionais e ferramentas que executam comandos multilinha.
Uma chamada comum a compile() não foi projetada para decidir se um bloco precisa apenas de mais entrada. O módulo codeop encapsula heurísticas compatíveis com o console do Python e também mantém o efeito de declarações from __future__ entre comandos quando usamos CommandCompiler.
O problema do código incompleto
Considere uma função digitada linha por linha:
def dobro(valor):
return valor * 2
Depois da primeira linha, o código não deve ser rejeitado; o console deve pedir a continuação. Depois do corpo e de uma linha que finalize a entrada, ele pode compilar e executar.
compile_command
A função compile_command(source, filename="<input>", symbol="single") tenta decidir o estado do trecho.
import codeop
resultado = codeop.compile_command("1 + 2")
print(resultado)
Quando o código está completo, o retorno é um code object. Quando parece incompleto, o retorno é None. Quando está definitivamente inválido, uma exceção de sintaxe é lançada.
Três resultados possíveis
Uma aplicação deve tratar os três estados explicitamente:
def analisar(entrada):
try:
codigo = codeop.compile_command(entrada)
except (SyntaxError, OverflowError, ValueError) as erro:
return "invalido", erro
if codigo is None:
return "incompleto", None
return "completo", codigo
Não transforme None em erro. Ele é o sinal de que a interface deve continuar coletando linhas.
O parâmetro symbol
O argumento symbol controla o tipo de entrada esperado. Os valores comuns são single, exec e eval.
singlerepresenta uma instrução interativa e pode produzir exibição automática de expressões.execrepresenta um conjunto de instruções.evalaceita uma expressão.
expressao = codeop.compile_command(
"10 * 4",
filename="<calculadora>",
symbol="eval",
)
Escolha o modo correto
Um console tradicional costuma usar single. Um editor que executa células com várias instruções pode usar exec. Uma calculadora restrita pode usar eval, embora executar expressões não confiáveis continue sendo perigoso.
O modo não cria segurança; ele apenas define a gramática e o tipo de code object.
Acumule linhas
Um REPL simples mantém um buffer até que a entrada fique completa.
import codeop
linhas = []
while True:
prompt = "... " if linhas else ">>> "
linha = input(prompt)
linhas.append(linha)
fonte = "\n".join(linhas)
try:
codigo = codeop.compile_command(fonte)
except SyntaxError as erro:
print(f"erro: {erro}")
linhas.clear()
continue
if codigo is None:
continue
exec(codigo, namespace)
linhas.clear()
Um console real também precisa tratar EOF, KeyboardInterrupt, histórico, encoding e exceções de execução.
Linhas em branco
Consoles usam linhas em branco para concluir alguns blocos compostos. A interface deve preservar essa semântica em vez de remover toda entrada vazia.
Não aplique strip() ao buffer completo, pois isso pode alterar indentação e finalização.
Indentação
Espaços iniciais são parte da sintaxe. Ao acumular linhas, preserve exatamente o texto digitado.
Uma interface pode exibir dicas de indentação, mas não deve reformatar silenciosamente o código antes de compilar.
SyntaxError
Código definitivamente inválido gera SyntaxError.
try:
codeop.compile_command("if :")
except SyntaxError as erro:
print(erro.msg, erro.lineno, erro.offset)
Mostre filename, linha, coluna e trecho sem expor buffers de outros usuários.
Outras exceções de compilação
Entradas extremas podem produzir OverflowError, ValueError ou outras falhas associadas à compilação. Defina limites de tamanho, profundidade e tempo antes de chamar o compilador em um serviço público.
Não aumente o limite de recursão como resposta automática a uma entrada hostil.
Filename simbólico
O parâmetro filename aparece em tracebacks e diagnósticos.
codigo = codeop.compile_command(
fonte,
filename="<console-admin>",
)
Use um nome que identifique a sessão sem revelar dados pessoais. Para células, inclua um ID estável que permita recuperar o conteúdo correspondente.
Integração com linecache
Code objects gerados dinamicamente não possuem um arquivo real. Se você quiser que tracebacks mostrem a linha correta, mantenha um armazenamento de fonte associado ao filename e integre-o cuidadosamente ao mecanismo de cache.
Veja linecache no Python para recuperação de linhas.
CommandCompiler
A classe CommandCompiler funciona como uma versão stateful de compile_command().
import codeop
compilador = codeop.CommandCompiler()
codigo = compilador("x = 10")
Ela é importante quando comandos sucessivos pertencem à mesma sessão interativa.
Declarações from __future__
Em um módulo, uma declaração from __future__ import ... afeta o restante do arquivo. Em um console, ela deve afetar comandos posteriores da sessão.
CommandCompiler lembra as flags futuras observadas em compilações anteriores, aproximando o comportamento do interpretador interativo.
compilador = codeop.CommandCompiler()
compilador("from __future__ import annotations")
proximo = compilador("def f(x: Tipo) -> Outro: pass")
Um compilador por sessão
Não compartilhe uma única instância de CommandCompiler entre usuários independentes. Flags futuras e estado de sessão poderiam vazar de um contexto para outro.
Crie uma instância por console, notebook, conexão ou tenant.
Compile versus execute
Compilar verifica sintaxe e cria bytecode; não executa o corpo. A execução ocorre com exec() ou eval().
Mesmo a compilação pode consumir recursos, mas o risco principal surge na execução, que permite imports, arquivos, rede, subprocessos e introspecção.
Não é sandbox
codeop não restringe o código. Um modo eval, uma lista de builtins reduzida ou uma inspeção superficial de AST não transforma Python em uma linguagem segura.
Para código não confiável, use isolamento forte: processo separado, usuário sem privilégios, filesystem restrito, limites de CPU e memória, rede controlada e tempo máximo.
Namespaces
Um console costuma manter um dicionário de globals entre comandos.
namespace = {"__name__": "__console__"}
exec(codigo, namespace, namespace)
Isso permite que variáveis e funções persistam. Também significa que memória e recursos podem acumular durante a sessão.
Separe sessões
Cada usuário deve possuir seu próprio namespace. Compartilhar globals permite leitura e alteração de dados de outras sessões.
Destrua o processo ou namespace ao encerrar, mas lembre que objetos nativos e threads iniciadas pelo código podem sobreviver se o isolamento for fraco.
Exibição de expressões
O modo single integra-se ao mecanismo de display do interpretador para expressões. Em consoles personalizados, configure sys.displayhook com cuidado se quiser controlar representação e histórico.
Use reprlib para limitar objetos gigantes. Veja reprlib no Python.
Resultados grandes
Uma expressão pode produzir uma representação enorme ou recursiva. Limite tamanho de saída, linhas e tempo de serialização.
Não faça repr() de objetos controlados por código hostil no processo principal; métodos especiais podem executar lógica arbitrária.
Captura de stdout e stderr
Consoles web normalmente capturam saída. contextlib.redirect_stdout() altera estado global e não é seguro para várias sessões concorrentes no mesmo processo.
Isolar cada sessão em processo simplifica streams, sinais, limites e encerramento.
Exceções de execução
Depois da compilação, envolva a execução em um bloco que capture BaseException apenas no boundary correto, sem engolir sinais de shutdown por acidente.
try:
exec(codigo, namespace, namespace)
except SystemExit:
encerrar_sessao()
except Exception:
traceback.print_exc()
Defina uma política explícita para KeyboardInterrupt, GeneratorExit e SystemExit.
Timeout de execução
Uma thread não consegue interromper com segurança qualquer código Python ou nativo. Use processos descartáveis e finalize o worker quando o prazo expirar.
Antes de matar, um dump com faulthandler pode ajudar no diagnóstico de sessões internas confiáveis.
Async e await
Um console assíncrono pode querer suportar await no nível superior. Isso exige flags de compilação e um event loop apropriado; codeop básico não fornece sozinho uma política completa de top-level await.
Use APIs do compilador e do framework interativo compatíveis com a versão do Python.
Notebooks
Notebooks mantêm uma sessão, histórico, IDs de células, display protocol, execução assíncrona e armazenamento de fonte. codeop resolve apenas a decisão sintática de completude e flags futuras.
Não tente recriar um kernel completo apenas com um loop de input().
Consoles administrativos
Um console dentro de um serviço é uma superfície de alto risco. Proteja com autenticação forte, autorização, auditoria, rede privada e acesso temporário.
Prefira comandos administrativos declarativos a execução arbitrária de Python.
Histórico
Se armazenar comandos, criptografe e defina retenção. Entradas podem conter tokens, dados pessoais e segredos digitados acidentalmente.
Permita apagar o histórico e não registre automaticamente conteúdo de sessões sensíveis.
Limites de entrada
Defina máximo de bytes, linhas, tempo esperando continuação e nesting. Um cliente pode enviar um bloco incompleto indefinidamente para ocupar recursos.
Após o timeout, descarte o buffer e encerre a sessão ou solicite nova entrada.
Cancelamento do buffer
Ofereça um comando para limpar o trecho atual sem destruir todo o namespace.
if linha == ":cancelar":
linhas.clear()
continue
Use comandos fora da sintaxe Python e documente conflitos.
Autocomplete
A decisão de completude não fornece autocomplete. Para sugestões, use análise de tokens, AST parcial, symbol tables ou um language server.
Evite executar propriedades e descriptors apenas para descobrir atributos; isso pode gerar side effects.
Versionamento
A gramática e as heurísticas de código incompleto evoluem com o Python. Teste seu console em cada versão suportada.
Não copie implementações internas antigas; use o módulo da própria versão em execução.
Testes
Inclua expressões simples, funções multilinha, classes, decorators, parênteses abertos, strings triplas, comprehensions, try/except, match, async, erros definitivos, EOF, interrupção e declarações futuras.
Teste também que sessões independentes não compartilham namespace nem flags.
Erros comuns
Os erros mais frequentes são tratar None como falha, remover indentação, usar o modo errado, compartilhar CommandCompiler, executar código não confiável no processo principal, capturar streams globalmente, não limitar buffers e confundir compilação com sandbox.
Conclusão
codeop fornece a lógica necessária para saber se uma entrada interativa está completa, incompleta ou inválida. Use compile_command() em casos stateless e CommandCompiler para sessões que precisam preservar flags de __future__.
Separe namespaces, preserve a fonte para tracebacks, limite recursos e isole a execução. Consulte a documentação oficial de codeop e o guia de symtable no Python para analisar nomes de uma sessão.







