runpy no Python: execute módulos

Publicado em: 13/08/2026
Tempo de leitura: 6 minutos
Código em execução representando módulos e caminhos executados com runpy no Python

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__.

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'] == 10

Teste 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=True em 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 SystemExit e timeouts.

Boas práticas

  • Use allowlists de módulos e caminhos.
  • Prefira importlib para reutilizar APIs.
  • Prefira subprocess para isolamento.
  • Evite alterar sys em 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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Pacote de software representando descoberta de módulos com pkgutil no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pkgutil no Python: descubra pacotes

    Aprenda pkgutil no Python para descobrir módulos, percorrer pacotes, resolver objetos, estender caminhos e acessar recursos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026
    Rede de código binário representando o grafo de imports analisado com modulefinder no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    modulefinder no Python: analise imports

    Aprenda modulefinder no Python para mapear imports, detectar módulos ausentes, personalizar caminhos e auditar dependências com limites claros.

    Ler mais

    Tempo de leitura: 7 minutos
    13/08/2026
    Pastas organizadas representando aplicações empacotadas em arquivos .pyz com zipapp no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    zipapp no Python: crie executáveis .pyz

    Aprenda zipapp no Python para empacotar aplicações em arquivos .pyz, definir entry points, incluir dependências e distribuir com segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    13/08/2026
    Editor de código representando autocompletar em REPL com rlcompleter no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    rlcompleter no Python: autocompletar REPL

    Aprenda rlcompleter no Python para adicionar autocompletar a REPLs, consoles e editores, controlar namespaces e evitar efeitos colaterais.

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026
    Janela de terminal representando console interativo criado com cmd no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    cmd no Python: crie consoles interativos

    Aprenda cmd no Python para criar consoles interativos com comandos, ajuda, histórico, autocompletar, testes e controle seguro de ações.

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Terminal interativo representando um REPL customizado com o módulo code no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    code no Python: crie um REPL customizado

    Aprenda o módulo code no Python para criar REPLs customizados, controlar namespaces, prompts, saída, blocos incompletos e encerramento seguro.

    Ler mais

    Tempo de leitura: 6 minutos
    12/08/2026