O módulo runpy no Python localiza e executa código usando o sistema de módulos. Ele implementa parte do comportamento por trás de python -m pacote.modulo e também pode executar scripts, diretórios e arquivos ZIP que contenham um __main__.py.
A ferramenta é útil em launchers, testes, automações e ambientes que precisam executar um entry point e receber o dicionário global resultante. Porém, runpy não é sandbox. O código roda no processo atual, pode alterar arquivos, rede, variáveis globais, caches de import e outros estados. Para entrada não confiável, use outro processo com permissões reduzidas.
run_module() por nome de módulo
run_module() recebe um nome absoluto e encontra o código pelo mecanismo normal de imports.
import runpy
resultado = runpy.run_module('meu_app.diagnostico')
print(resultado.keys())O módulo é executado em um namespace novo, e a função devolve esse dicionário. Isso difere de import meu_app.diagnostico, que cria ou reutiliza uma entrada persistente em sys.modules.
Executar um pacote
Quando o nome aponta para um pacote, run_module() procura e executa pacote.__main__.
resultado = runpy.run_module('meu_app')
# executa meu_app.__main__Essa regra permite reproduzir programaticamente o comportamento de python -m meu_app. O pacote pai pode ser importado durante a localização, portanto seu __init__.py ainda pode produzir efeitos.
Variáveis especiais do namespace
Antes da execução, runpy define valores como __name__, __spec__, __file__, __cached__, __loader__ e __package__.
resultado = runpy.run_module(
'meu_app.diagnostico',
run_name='__main__',
)
print(resultado['__name__'])__spec__.name continua indicando o módulo real, mesmo quando run_name muda. Desde Python 3.12, a configuração direta de alguns globais legados está depreciada; prefira consultar __spec__.
Pré-popular globais
init_globals fornece valores iniciais sem modificar o dicionário original.
contexto = {
'CONFIGURACAO': {'modo': 'teste'},
'SERVICO': servico_falso,
}
resultado = runpy.run_module(
'meu_app.tarefa',
init_globals=contexto,
)Os nomes especiais controlados por runpy substituem valores equivalentes em init_globals. Além disso, oferecer objetos poderosos ao código executado amplia o que ele pode fazer.
run_name e comportamento de script
Muitos módulos usam:
if __name__ == '__main__':
main()Para acionar esse bloco, forneça run_name='__main__'.
runpy.run_module(
'meu_app.cli',
run_name='__main__',
)Esse padrão executa o módulo como script, mas continua no processo atual. Funções e classes devolvidas no dicionário não têm garantia de permanecer corretas depois que runpy termina; quando você precisa reutilizar APIs, um import normal costuma ser melhor.
alter_sys e compatibilidade com -m
Com alter_sys=True, run_module() altera temporariamente sys.argv[0] e adiciona um módulo temporário a sys.modules.
resultado = runpy.run_module(
'meu_app.cli',
run_name='__main__',
alter_sys=True,
)Isso aproxima a execução da CLI, mas não é thread-safe. Outros threads podem observar argumentos alterados ou um módulo parcialmente inicializado. Em servidores multithread, deixe alter_sys=False ou delegue a execução a um subprocesso.
Executar um arquivo com run_path()
run_path() executa um caminho de sistema de arquivos.
import runpy
resultado = runpy.run_path('scripts/relatorio.py')
print(resultado.get('RESULTADO'))O caminho pode apontar para código-fonte, bytecode compilado ou uma entrada válida de sys.path, como um diretório ou ZIP com __main__.py.
Executar diretórios e ZIPs
Se o caminho é uma entrada de sys.path, runpy adiciona temporariamente esse item ao início da busca e procura __main__.
runpy.run_path('dist/ferramenta.pyz', run_name='__main__')Isso combina com arquivos criados pelo zipapp no Python. Há um detalhe de segurança: se o caminho indicado não contém __main__, a busca pode encontrar outro __main__ existente em sys.path. Valide o artefato antes de executar.
run_path altera sys obrigatoriamente
Ao executar diretórios e ZIPs, run_path() precisa modificar temporariamente sys.path, sys.argv[0] e sys.modules. Essas mudanças são revertidas ao final, mas continuam visíveis para outros threads durante a execução.
Serialize chamadas com uma trava apropriada ou, preferencialmente, execute o caminho em outro processo. Isso também oferece timeout e isolamento de falhas.
Não é sandbox
Um script executado por runpy possui acesso normal ao Python e ao sistema operacional.
# código executado pode fazer isto
from pathlib import Path
Path('/tmp/marcador').write_text('executado')
Remover alguns nomes de init_globals não cria isolamento. Para código de terceiros, use container ou processo descartável, usuário sem privilégios, rede bloqueada, sistema de arquivos limitado e cotas.
Efeitos persistentes
Embora o namespace principal seja novo, imports feitos durante a execução permanecem no cache de sys.modules. Handlers, variáveis de ambiente, logs, threads, sinais e estado de bibliotecas também podem persistir.
Se você precisa de uma execução limpa e repetível, o limite correto é um processo. Reiniciar um worker é mais confiável que tentar desfazer todos os efeitos.
runpy ou importlib?
Use runpy para executar um entry point como script. Use importlib.import_module() quando deseja importar um módulo e continuar chamando suas funções e classes de forma suportada.
A própria documentação alerta que objetos definidos pelo código executado podem não funcionar corretamente depois do retorno. Para inspeção de imports, veja modulefinder no Python.
runpy ou subprocess?
Runpy é rápido e compartilha memória, mas também compartilha falhas e estado. Um subprocesso oferece isolamento, argumentos reais, código de retorno, timeout e captura de saída.
import subprocess
import sys
subprocess.run(
[sys.executable, '-m', 'meu_app.cli'],
check=True,
timeout=30,
)Prefira subprocess para plugins pouco confiáveis, tarefas longas, execução concorrente e qualquer caso que possa encerrar o processo.
Capturar o resultado
O dicionário retornado pode conter variáveis produzidas pelo script.
resultado = runpy.run_path(
'calculo.py',
init_globals={'ENTRADA': 21},
)
print(resultado['SAIDA'])Defina um contrato explícito, como um nome obrigatório e tipo esperado. Não serialize todo o namespace: ele pode conter módulos, funções e objetos não seguros.
Tratar SystemExit
Um script pode chamar sys.exit(), levantando SystemExit.
try:
runpy.run_module('meu_app.cli', run_name='__main__')
except SystemExit as saida:
codigo = saida.code
Decida se o código de saída deve ser propagado, convertido em resultado ou registrado como erro. Também trate KeyboardInterrupt no limite externo.
Exceções e diagnóstico
Erros de import, sintaxe e runtime são propagados. Registre o módulo ou caminho, versão de Python e argumentos, sem incluir segredos.
Para tracebacks localizados, use as técnicas do artigo de traceback no Python. Em serviços, mantenha detalhes internos fora da resposta ao usuário.
Executar testes ou migrações
Runpy pode ser útil em ferramentas internas que executam módulos controlados por nome.
TAREFAS = {
'migrar': 'meu_app.migracoes.aplicar',
'verificar': 'meu_app.diagnostico',
}
modulo = TAREFAS[acao]
runpy.run_module(modulo, run_name='__main__')Use uma allowlist. Não permita que o usuário informe qualquer nome importável ou caminho arbitrário.
Concorrência
Mesmo com alter_sys=False, o código executado pode modificar estado global. Com alter_sys=True ou run_path(), os riscos aumentam.
Em aplicações assíncronas, não execute tarefas pesadas diretamente no event loop. Use processo worker e limite o número de execuções simultâneas.
Testar uma camada baseada em runpy
Crie scripts temporários pequenos.
def test_run_path(tmp_path):
script = tmp_path / 'job.py'
script.write_text('SAIDA = ENTRADA * 2\n', encoding='utf-8')
resultado = runpy.run_path(
str(script),
init_globals={'ENTRADA': 5},
)
assert resultado['SAIDA'] == 10Teste SystemExit, exceções, módulos ausentes, ZIPs sem __main__, alterações de sys e concorrência.
Integração com code e codeop
O módulo code no Python cria REPLs persistentes. codeop detecta entrada incompleta. Runpy, por outro lado, localiza e executa unidades completas por módulo ou caminho.
Escolha a abstração conforme a experiência desejada. Não use runpy como substituto improvisado para um sandbox interativo.
Erros frequentes
- Tratar runpy como sandbox.
- Usar
alter_sys=Trueem código multithread. - Executar caminho não validado.
- Confiar que todos os efeitos serão revertidos.
- Reutilizar funções do namespace sem garantia.
- Permitir qualquer nome de módulo informado pelo usuário.
- Ignorar
SystemExite timeouts.
Boas práticas
- Use allowlists de módulos e caminhos.
- Prefira importlib para reutilizar APIs.
- Prefira subprocess para isolamento.
- Evite alterar
sysem threads. - Valide ZIPs e
__main__.py. - Defina contratos mínimos para o dicionário retornado.
- Registre efeitos e falhas sem segredos.
Conclusão
O runpy no Python oferece uma forma programática de executar módulos e caminhos com semântica próxima a python -m e à execução de scripts. Ele suporta pacotes, diretórios e ZIPs e devolve o namespace resultante.
Use-o apenas para código confiável no processo atual. Para concorrência, isolamento e segurança, prefira subprocessos. Consulte a documentação oficial do runpy e a documentação da opção -m.







