code no Python: crie um REPL customizado

Publicado em: 12/08/2026
Tempo de leitura: 6 minutos
Terminal interativo representando um REPL customizado com o módulo code no Python

O módulo code no Python oferece as classes usadas para construir loops de leitura, avaliação e exibição, conhecidos como REPLs. Com InteractiveInterpreter e InteractiveConsole, uma aplicação pode fornecer um prompt Python embutido, controlar o namespace, capturar mensagens de erro e decidir como linhas incompletas serão acumuladas.

Esse recurso é valioso em ferramentas de ensino, depuradores, notebooks simplificados, consoles administrativos e ambientes de experimentação. Porém, ele executa código Python real. Não use um REPL embutido como sandbox para usuários não confiáveis: imports, sistema de arquivos, rede, processos e objetos já presentes no namespace podem ser explorados.

InteractiveInterpreter e InteractiveConsole

InteractiveInterpreter cuida da compilação, do estado do namespace e da execução, mas não implementa prompt nem buffering de várias linhas. InteractiveConsole herda essa base e adiciona comportamento parecido com o console padrão, incluindo os prompts principal e secundário.

from code import InteractiveConsole

console = InteractiveConsole()
console.interact(
    banner='Console de diagnóstico',
    exitmsg='Console encerrado',
)

Esse exemplo abre um REPL no processo atual. Tudo que o usuário executar terá as mesmas permissões do programa.

Definir um namespace controlado

O parâmetro locals recebe um mapeamento usado como namespace. Ele permite expor somente objetos específicos e preservar variáveis entre comandos.

from code import InteractiveConsole

contexto = {
    'status': lambda: {'fila': 4, 'saude': 'ok'},
    'versao': '2.1.0',
}
console = InteractiveConsole(locals=contexto)

Uma lista reduzida não transforma o console em ambiente seguro. O próprio Python oferece caminhos para introspecção e recuperação de objetos. Use esse controle para ergonomia e organização, não como fronteira contra atacantes.

Executar uma linha com runsource()

runsource() compila e executa uma string. O retorno informa se é necessária mais entrada: True significa comando incompleto; False significa que o comando foi executado ou rejeitado.

from code import InteractiveInterpreter

interp = InteractiveInterpreter()
precisa_mais = interp.runsource('for i in range(3):')
print(precisa_mais)  # True

Esse protocolo permite escolher entre um prompt principal e um prompt de continuação.

Acumular blocos com push()

InteractiveConsole.push() mantém um buffer interno. Cada linha é adicionada e o conjunto é compilado novamente.

from code import InteractiveConsole

console = InteractiveConsole()
print(console.push('def dobro(x):'))  # True
print(console.push('    return x * 2'))  # True
print(console.push(''))  # False
print(console.push('dobro(5)'))  # False

Quando o bloco termina ou é inválido, o buffer é limpo. resetbuffer() descarta manualmente uma entrada pendente, algo útil após cancelamento.

Detectar código incompleto

A função compile_command() tenta reproduzir a decisão do interpretador real. Ela devolve um objeto de código quando a entrada está completa, None quando falta conteúdo e lança SyntaxError quando existe um erro completo.

from code import compile_command

resultado = compile_command('if ativo:', symbol='single')
assert resultado is None

O módulo codeop no Python aprofunda o estado de compilação e flags de __future__ para consoles persistentes.

Personalizar a entrada

Subclasses de InteractiveConsole podem sobrescrever raw_input(). Isso permite ler linhas de uma interface gráfica, socket local, editor web ou fila interna.

class ConsoleFila(InteractiveConsole):
    def __init__(self, fila, **kwargs):
        super().__init__(**kwargs)
        self.fila = fila

    def raw_input(self, prompt=''):
        exibir_prompt(prompt)
        return self.fila.get()

Não conecte esse método diretamente à internet. Se o console remoto for realmente necessário, imponha autenticação forte, rede restrita, processo dedicado, usuário do sistema sem privilégios, limites de recursos e auditoria.

Capturar erros com write()

Por padrão, mensagens de sintaxe e tracebacks vão para sys.stderr. Sobrescreva write() para armazená-las ou enviá-las à interface.

class ConsoleCaptura(InteractiveConsole):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.erros = []

    def write(self, data):
        self.erros.append(data)

Evite devolver tracebacks completos para usuários externos, pois eles podem revelar caminhos, nomes de módulos e dados. Em interfaces administrativas internas, mantenha registros detalhados em canal protegido.

showsyntaxerror() e showtraceback()

showsyntaxerror() formata erros de compilação, enquanto showtraceback() lida com exceções de execução e remove o frame interno do interpretador. Desde o Python 3.5, exceções encadeadas são exibidas integralmente.

Você pode sobrescrever esses métodos para produzir JSON estruturado ou mensagens localizadas, mas preserve a exceção original em logs para diagnóstico.

Executar objetos de código com runcode()

runcode() recebe um objeto já compilado. Exceções comuns são capturadas e exibidas, mas SystemExit pode propagar. KeyboardInterrupt também pode surgir fora do ponto esperado.

codigo = compile('resultado = 6 * 7', '<console>', 'exec')
interp = InteractiveInterpreter()
interp.runcode(codigo)
print(interp.locals['resultado'])

O chamador deve definir uma política para encerramento e interrupção.

local_exit no Python 3.13+

InteractiveConsole e interact() aceitam local_exit=True. Nesse modo, chamadas a exit() e quit() encerram o console e retornam à aplicação, em vez de levantar SystemExit para todo o processo.

console = InteractiveConsole(local_exit=True)
console.interact()

Isso melhora a incorporação do REPL, mas não impede que código chame diretamente os._exit(), encerre threads ou altere o processo por outros meios.

Usar code.interact()

code.interact() é uma função de conveniência que cria um console temporário. Ela aceita banner, função de leitura, namespace, mensagem de saída e local_exit.

import code

estado = {'pedido_id': 123, 'modo': 'debug'}
code.interact(
    banner='Sessão de suporte local',
    local=estado,
    exitmsg='',
    local_exit=True,
)

É útil para abrir um console sob uma flag de desenvolvimento. Garanta que essa flag não fique habilitada em produção.

REPL para depuração

Um padrão é capturar o contexto de um ponto específico e iniciar uma sessão local.

def diagnosticar(objeto):
    code.interact(
        local={'objeto': objeto, 'resumo': objeto.resumo},
        local_exit=True,
    )

Não inclua tokens, senhas ou objetos com métodos destrutivos. Mesmo um console local pode ser exposto por logs de terminal compartilhados, gravação de sessão ou acesso indevido ao servidor.

Persistência do namespace

Variáveis, funções e classes permanecem no dicionário de locais enquanto a instância existir. Isso permite uma sessão natural, mas também pode reter grandes objetos e aumentar o consumo de memória.

Ofereça um comando para limpar o contexto ou recrie o console após determinado tempo. O guia sobre weakref no Python explica retenção de objetos, embora não substitua uma política explícita de ciclo de vida.

Pickle e objetos definidos no console

Funções e classes criadas pelo interpretador pertencem ao namespace fornecido. Elas só são serializáveis com pickle de forma convencional quando o namespace corresponde a um módulo importável existente. Não dependa de pickle para persistir uma sessão arbitrária.

Prefira salvar dados em formatos definidos e recriar funções a partir de código versionado. Nunca carregue pickle não confiável.

Isolamento real exige outro processo

Remover __builtins__ ou bloquear alguns nomes não cria sandbox. Python permite introspecção profunda, e objetos disponíveis podem abrir caminhos indiretos para funcionalidades perigosas.

Para reduzir impacto, execute o REPL em processo ou container descartável, com usuário sem privilégios, sistema de arquivos limitado, rede desabilitada, CPU e memória controladas e timeout. Mesmo assim, trate-o como execução de código, não como simples entrada de dados.

Testar um console customizado

Teste valores de retorno de push(), namespace, saída capturada, erros de sintaxe, exceções, comandos incompletos, EOF e encerramento local.

def teste_console():
    console = ConsoleCaptura(locals={})
    assert console.push('x = 10') is False
    assert console.locals['x'] == 10
    assert console.push('for i in range(2):') is True
    console.resetbuffer()

Use objetos falsos e não permita acesso real a produção durante testes.

Erros frequentes

  • Tratar um namespace reduzido como sandbox.
  • Expor o REPL em uma porta pública.
  • Deixar consoles de depuração ativos em produção.
  • Retornar tracebacks sensíveis ao cliente.
  • Ignorar SystemExit e KeyboardInterrupt.
  • Reter objetos grandes indefinidamente.
  • Tentar persistir funções de sessão com pickle.

Boas práticas

  • Use o módulo apenas para usuários confiáveis.
  • Prefira processos descartáveis para isolamento.
  • Controle namespace e ciclo de vida.
  • Capture saída por write().
  • Limite CPU, memória, rede e tempo.
  • Desative o recurso por padrão em produção.
  • Registre acesso e ações sem expor segredos.

Conclusão

O code no Python facilita a criação de REPLs personalizados com namespace persistente, buffering de blocos, prompts, tratamento de erros e encerramento local. Ele é uma base poderosa para ferramentas de desenvolvimento e diagnóstico.

Seu uso exige uma decisão de segurança explícita: o conteúdo é código executável. Para entradas não confiáveis, use isolamento de sistema operacional, não filtros no namespace. Consulte a documentação oficial do módulo code e a documentação do codeop.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026