O módulo modulefinder no Python analisa um script e tenta descobrir quais módulos ele importa. O resultado pode ajudar na auditoria de dependências, na preparação de pacotes, na investigação de erros de import e na geração de relatórios para ferramentas internas.
Ele trabalha examinando o código e seguindo imports encontrados durante a análise. Isso é diferente de executar toda a aplicação em produção. Imports construídos dinamicamente, plugins descobertos por configuração, chamadas a importlib.import_module() e módulos carregados por caminhos especiais podem não aparecer. Portanto, o relatório deve ser tratado como evidência útil, não como inventário absoluto.
Primeiro relatório com ModuleFinder
A classe principal é modulefinder.ModuleFinder. O método run_script() recebe o caminho de um arquivo Python, e report() imprime os módulos encontrados.
from modulefinder import ModuleFinder
finder = ModuleFinder()
finder.run_script('aplicacao.py')
finder.report()O relatório inclui módulos encontrados, caminhos e itens ausentes ou aparentemente ausentes. Em automações maiores, você pode preferir acessar os dicionários internos e gerar JSON.
Ler o dicionário modules
finder.modules mapeia nomes para objetos que representam os módulos analisados.
from modulefinder import ModuleFinder
finder = ModuleFinder()
finder.run_script('aplicacao.py')
for nome, modulo in sorted(finder.modules.items()):
print(nome, modulo.__file__)Alguns módulos internos ou built-ins podem não possuir arquivo físico. Trate None como um caso legítimo. Para entender caminhos de instalação e bibliotecas, consulte sysconfig no Python.
Detectar módulos ausentes
Itens problemáticos aparecem em badmodules. Eles podem representar erros reais, imports opcionais protegidos por try/except ImportError, dependências específicas de plataforma ou falsos positivos.
ausentes = sorted(finder.badmodules)
for nome in ausentes:
print('Possivelmente ausente:', nome)Não falhe o build automaticamente por qualquer entrada. Classifique dependências obrigatórias e opcionais, considere sistema operacional e confirme o resultado em um ambiente limpo.
Analisar imports opcionais
Bibliotecas frequentemente tentam importar aceleradores, backends ou módulos exclusivos de uma plataforma.
try:
import uvloop
except ImportError:
uvloop = NoneO analisador pode registrar o módulo como ausente mesmo quando a aplicação possui um fallback válido. Mantenha uma política documentada de exceções, em vez de simplesmente esconder todos os avisos.
Definir caminhos de busca
O construtor aceita uma lista path. Quando omitida, usa sys.path.
from modulefinder import ModuleFinder
finder = ModuleFinder(path=[
'/projeto/src',
'/projeto/vendor',
])
finder.run_script('/projeto/src/app.py')Use caminhos absolutos e controlados. Incluir o diretório atual de forma indiscriminada pode fazer a análise encontrar módulos errados com o mesmo nome de dependências legítimas.
Excluir módulos conhecidos
O parâmetro excludes evita a análise de nomes específicos.
finder = ModuleFinder(
excludes=['tkinter', 'tests', 'devtools'],
)Exclusões podem acelerar o processo e remover dependências opcionais conhecidas. Porém, uma lista excessiva pode esconder uma dependência real. Registre a justificativa de cada item.
Substituir caminhos em relatórios
replace_paths recebe pares de caminho antigo e novo. Isso é útil para remover caminhos locais de relatórios e criar resultados reprodutíveis.
finder = ModuleFinder(
replace_paths=[
('/home/ana/projeto', '<PROJECT>'),
('/opt/build/venv', '<VENV>'),
]
)Além de melhorar comparação entre builds, a substituição evita vazar nomes de usuários e estrutura interna em logs compartilhados.
Adicionar caminho a um pacote
AddPackagePath() informa que um pacote pode ser localizado em outro diretório.
import modulefinder
modulefinder.AddPackagePath(
'meu_plugin',
'/opt/plugins/meu_plugin',
)Use isso apenas para layouts conhecidos. Se o caminho vier de entrada externa, valide e restrinja a uma raiz aprovada.
Substituir um módulo por pacote
ReplacePackage(oldname, newname) informa que um nome deve ser tratado como outro pacote. É uma ferramenta especializada para compatibilidade e layouts incomuns.
Documente a substituição, porque o relatório pode se tornar confuso para quem não conhece a regra. Prefira corrigir a estrutura de pacotes quando você controla o código.
Executar modulefinder pela linha de comando
O próprio arquivo pode ser usado como script para produzir um relatório de imports. A API costuma ser melhor para automações, pois permite filtrar e estruturar a saída.
Em um pipeline, salve o relatório como artefato e compare mudanças entre versões. Uma dependência nova inesperada pode indicar crescimento do pacote, mudança arquitetural ou import acidental.
Gerar JSON estruturado
import json
from modulefinder import ModuleFinder
finder = ModuleFinder()
finder.run_script('app.py')
dados = {
'modules': {
nome: getattr(modulo, '__file__', None)
for nome, modulo in sorted(finder.modules.items())
},
'missing': sorted(finder.badmodules),
}
with open('imports.json', 'w', encoding='utf-8') as arquivo:
json.dump(dados, arquivo, ensure_ascii=False, indent=2)Não inclua caminhos sensíveis sem normalização. Também limite o tamanho do relatório em repositórios muito grandes.
Comparar duas versões
Um caso prático é identificar módulos adicionados ou removidos.
antes = set(relatorio_antigo['modules'])
depois = set(relatorio_novo['modules'])
print('Adicionados:', sorted(depois - antes))
print('Removidos:', sorted(antes - depois))A diferença não prova que o código executa esses módulos em todas as rotas. Ela mostra mudanças na análise estática do script e seus imports alcançáveis.
Imports dinâmicos escapam
O analisador não pode prever todos os nomes construídos em runtime.
from importlib import import_module
nome = configuracao['backend']
backend = import_module(f'meu_app.backends.{nome}')Para esse padrão, mantenha um manifesto explícito de plugins ou combine a análise com testes que exercitam configurações suportadas. O guia de importlib.resources ajuda com recursos, mas descoberta dinâmica de módulos exige uma estratégia própria.
Entry points e plugins
Plugins distribuídos por metadados podem ser descobertos sem imports literais no código principal. Uma análise de modulefinder provavelmente não os incluirá.
Consulte os entry points instalados e adicione os pacotes esperados ao inventário. Mais adiante neste lote, o módulo importlib.metadata é uma ferramenta adequada para essa tarefa.
Dependências condicionais por plataforma
import sys
if sys.platform == 'win32':
import winreg
else:
import pwdO resultado pode variar conforme o ambiente de análise. Execute a ferramenta em cada plataforma suportada ou mantenha uma matriz de dependências condicionais.
Ambientes virtuais
A análise usa os caminhos disponíveis no processo. Rode-a no mesmo ambiente virtual usado pelo build ou teste.
Registre a versão de Python, sys.path normalizado e dependências instaladas. Sem esse contexto, dois relatórios podem divergir por razões externas ao código.
Integração com zipapp
Antes de criar um arquivo com zipapp no Python, um relatório pode ajudar a identificar dependências puramente Python que precisam ser copiadas para o diretório de build.
Não use o resultado automaticamente para copiar qualquer arquivo encontrado. Filtre bibliotecas padrão, pacotes nativos, arquivos externos e licenças. A lista é ponto de partida, não uma receita segura de empacotamento.
Integração com compileall
compileall verifica sintaxe e gera bytecode; modulefinder mapeia imports. Usados juntos, eles cobrem problemas diferentes.
Um projeto pode compilar perfeitamente e ainda falhar por módulo ausente. Também pode importar tudo corretamente e conter erro sintático em um arquivo nunca alcançado pela análise.
Nível de debug
O parâmetro debug aumenta mensagens internas.
finder = ModuleFinder(debug=2)
finder.run_script('app.py')Use níveis altos apenas em diagnóstico. A saída pode ser extensa e conter caminhos locais. Redirecione para um arquivo protegido e aplique retenção curta.
Desempenho e limites
Projetos com muitos imports podem exigir tempo e memória consideráveis. Defina timeout no processo externo, limite o tamanho dos scripts e execute em worker isolado quando analisar código recebido de terceiros.
Embora a ferramenta não execute a aplicação normalmente, analisar arquivos desconhecidos ainda pode expor o pipeline a consumo excessivo ou bugs no parser. Não processe repositórios arbitrários com privilégios elevados.
Teste do relatório
Crie fixtures pequenas com import obrigatório, opcional, relativo, condicional e dinâmico. Verifique o que aparece em modules e badmodules.
def test_dependencia_basica(tmp_path):
script = tmp_path / 'app.py'
script.write_text('import json\n', encoding='utf-8')
finder = ModuleFinder()
finder.run_script(str(script))
assert 'json' in finder.modulesNão escreva testes dependentes de toda a biblioteca padrão do sistema; foque nos módulos relevantes ao seu projeto.
Erros frequentes
- Tratar o relatório como inventário perfeito.
- Ignorar imports dinâmicos e entry points.
- Falhar por qualquer import opcional ausente.
- Executar a análise em ambiente diferente do build.
- Vazar caminhos locais em relatórios.
- Copiar automaticamente todos os módulos encontrados.
- Não testar plataformas diferentes.
Boas práticas
- Use
post_status anye ambientes reproduzíveis nas automações do site. - Normalize caminhos antes de salvar relatórios.
- Classifique dependências obrigatórias e opcionais.
- Combine análise estática com testes de execução.
- Mantenha manifesto para plugins dinâmicos.
- Compare relatórios entre releases.
- Execute código de terceiros em ambiente isolado.
Conclusão
O modulefinder no Python ajuda a enxergar a rede de imports alcançável a partir de um script. Ele identifica módulos carregáveis, caminhos e itens ausentes, oferecendo dados úteis para auditoria, diagnóstico e empacotamento.
Use o resultado com contexto. Imports dinâmicos, plugins e condições de plataforma exigem verificações adicionais. Consulte a documentação oficial do modulefinder e a referência do sistema de imports.







