O módulo importlib.metadata no Python acessa informações de pacotes de distribuição instalados no ambiente. Ele permite consultar versão, metadados, dependências declaradas, arquivos instalados, entry points e a relação entre nomes usados em import e nomes usados por ferramentas como pip.
Essa distinção é importante. O pacote instalado chamado PyYAML, por exemplo, fornece o módulo importável yaml. Uma distribuição pode oferecer vários pacotes importáveis, e um namespace package pode ser fornecido por várias distribuições. Portanto, não presuma uma relação 1:1.
Distribuição não é módulo importável
Uma distribuição é a unidade instalada por ferramentas de empacotamento, normalmente acompanhada de diretórios .dist-info ou .egg-info. Um import package é o nome utilizado no código.
from importlib.metadata import packages_distributions
mapa = packages_distributions()
print(mapa.get('yaml'))
# ['PyYAML'] em um ambiente típicoO mapeamento é especialmente útil em auditorias e mensagens de erro. Instalações editáveis podem não fornecer todos os nomes de topo, então trate ausências como resultado possível.
Consultar a versão instalada
version() devolve a versão da distribuição como string.
from importlib.metadata import version
print(version('pip'))Não converta versões com float. Versões podem conter múltiplos componentes, pré-releases e identificadores locais. Quando precisar comparar, use uma implementação compatível com as regras de versionamento da PyPA.
Tratar PackageNotFoundError
Consultas a uma distribuição não instalada levantam PackageNotFoundError.
from importlib.metadata import PackageNotFoundError, version
try:
atual = version('meu-plugin')
except PackageNotFoundError:
atual = NoneDiferencie pacote ausente de import quebrado. A distribuição pode estar instalada, mas seu módulo pode falhar por dependência nativa ou configuração.
Ler metadados completos
metadata() retorna um objeto semelhante a mapping, com campos definidos pelas especificações de Core Metadata.
from importlib.metadata import metadata
meta = metadata('pip')
print(meta['Name'])
print(meta['Version'])
print(meta.get('Requires-Python'))
print(meta.get_all('Project-URL'))Alguns campos podem ocorrer várias vezes, como Classifier, Project-URL e Requires-Dist. Use get_all() quando a multiplicidade importa.
Metadados em JSON
A propriedade json apresenta os metadados em forma compatível com PEP 566.
dados = metadata('pip').json
print(dados.get('requires_python'))O conteúdo ainda vem do pacote instalado. Valide tipos e campos antes de enviar a APIs ou relatórios externos.
Consultar dependências declaradas
requires() devolve os requisitos declarados pela distribuição.
from importlib.metadata import requires
requisitos = requires('meu-pacote') or []
for requisito in requisitos:
print(requisito)As strings podem conter markers de plataforma, versão de Python e extras. Elas descrevem requisitos declarados, não confirmam que o ambiente está consistente. Uma dependência pode estar ausente ou possuir versão incompatível.
Listar arquivos instalados
files() retorna objetos PackagePath com informações de tamanho, hash e distribuição.
from importlib.metadata import files
itens = files('pip')
if itens is not None:
for item in list(itens)[:10]:
print(item, item.size, item.hash)A função pode retornar None quando a instalação não contém o arquivo de metadados que registra a lista. Sempre proteja a iteração.
Localizar um arquivo físico
PackagePath.locate() resolve a localização instalada.
for item in files('pip') or []:
if str(item).endswith('__init__.py'):
print(item.locate())
breakNão exponha caminhos absolutos sem necessidade, pois podem revelar usuários, ambientes virtuais e estrutura de servidores.
Verificar hashes registrados
Quando o arquivo RECORD contém hash e tamanho, PackagePath expõe essas informações. É possível comparar o conteúdo instalado com o valor registrado.
Nem todos os arquivos possuem hash, e os metadados locais não substituem uma assinatura externa confiável. Um invasor com permissão para alterar pacote e RECORD pode modificar ambos.
Entry points instalados
entry_points() retorna objetos EntryPoint. Selecione por grupo e, opcionalmente, nome.
from importlib.metadata import entry_points
plugins = entry_points(group='meu_app.plugins')
for plugin in plugins:
print(plugin.name, plugin.value, plugin.dist.name)Grupos são nomes convencionados pelos autores. console_scripts é um exemplo comum, mas sistemas próprios devem usar um namespace claro.
Carregar um entry point
EntryPoint.load() importa o módulo e resolve o objeto indicado.
plugins = entry_points(
group='meu_app.plugins',
name='csv',
)
(plugin,) = plugins
classe = plugin.load()Carregar executa imports e pode produzir efeitos colaterais. Descubra e valide metadados primeiro; carregue somente plugins aprovados.
Inspecionar sem carregar
As propriedades module, attr, extras, name, group e value permitem analisar um entry point sem importar.
for ep in entry_points(group='console_scripts'):
print(ep.name, ep.module, ep.attr)Essa fase é adequada para auditoria e allowlists. Mesmo assim, os metadados foram instalados por um pacote e não devem ser considerados confiáveis apenas por estarem presentes.
Mudanças da API de entry_points()
Versões antigas retornavam estruturas diferentes. Em Python moderno, entry_points() retorna um objeto EntryPoints selecionável. Desde Python 3.13, EntryPoint não oferece mais comportamento de tupla.
Bibliotecas que suportam versões antigas devem testar compatibilidade ou usar o backport importlib_metadata com uma faixa conhecida.
Obter uma Distribution
distribution() retorna um objeto com versão, metadados, entry points, arquivos e requisitos.
from importlib.metadata import distribution
dist = distribution('pip')
print(dist.version)
print(dist.metadata.get('License'))
print(len(dist.entry_points))Instâncias distintas de Distribution não comparam necessariamente como iguais, mesmo representando a mesma instalação. Compare atributos relevantes, como nome normalizado e versão.
Origem de instalações editáveis
Desde Python 3.13, a propriedade origin pode apresentar informações PEP 610 sobre a origem de pacotes editáveis.
dist = distribution('meu-pacote')
if dist.origin is not None:
print(dist.origin.url)Essas URLs podem conter caminhos locais. Remova ou normalize informações sensíveis em relatórios compartilhados.
Mapear imports para distribuições
packages_distributions() devolve listas porque namespace packages podem reunir partes de várias distribuições.
mapa = packages_distributions()
for pacote, distribuicoes in sorted(mapa.items()):
if len(distribuicoes) > 1:
print(pacote, distribuicoes)Esse mapeamento complementa modulefinder no Python, que segue imports a partir de um script, e pkgutil, que descobre módulos disponíveis.
Auditar o ambiente
Uma ferramenta pode listar distribuições e campos básicos.
from importlib.metadata import distributions
for dist in sorted(distributions(), key=lambda d: d.metadata['Name'].lower()):
print(dist.metadata['Name'], dist.version)Ambientes grandes podem conter duplicatas aparentes, instalações editáveis e metadados incompletos. Limite a saída e registre a versão do Python e o ambiente virtual.
Gerar um inventário JSON
import json
from importlib.metadata import distributions
inventario = []
for dist in distributions():
inventario.append({
'name': dist.metadata['Name'],
'version': dist.version,
'requires_python': dist.metadata.get('Requires-Python'),
})
print(json.dumps(inventario, ensure_ascii=False, indent=2))Não inclua descrições extensas e caminhos por padrão. Um Software Bill of Materials completo exige formatos e campos adicionais.
Verificar versão mínima
Uma aplicação pode confirmar que uma distribuição compatível está instalada, mas a comparação deve respeitar versionamento adequado.
from importlib.metadata import version
from packaging.version import Version
if Version(version('meu-plugin')) < Version('2.0'):
raise RuntimeError('Atualize meu-plugin')packaging é uma dependência externa comum. Evite comparações lexicográficas como '10' < '2'.
Plugins com política de confiança
Descobrir entry points é conveniente, mas qualquer pacote instalado pode registrar um nome no grupo. A aplicação deve verificar distribuição fornecedora, versão, configuração e permissões.
APROVADOS = {'plugin-oficial', 'plugin-interno'}
for ep in entry_points(group='meu_app.plugins'):
if ep.dist.name not in APROVADOS:
continue
carregar_plugin(ep)Em ambientes sensíveis, fixe hashes e instale plugins em um ambiente controlado.
Metadados não provam integridade
Nome, versão e licença são declarações do pacote. Eles não garantem segurança, autenticidade ou compatibilidade. Combine metadados com fonte de instalação confiável, assinaturas, hashes e testes.
Distribuições em ZIP e providers customizados
Por padrão, o módulo encontra metadados em sistema de arquivos e ZIPs presentes em sys.path. Importers personalizados podem implementar find_distributions() e fornecer objetos Distribution.
Isso permite metadados em bancos ou sistemas virtuais, mas o provider precisa respeitar filtros de nome e caminho. Teste cuidadosamente a integração.
importlib.metadata e sys.path
A interpretação de sys.path não é idêntica à do import normal. Bytes são ignorados, enquanto objetos pathlib.Path podem ser aceitos incidentalmente.
Mantenha sys.path com strings normais e evite depender dessas diferenças. O artigo sobre sysconfig ajuda a construir caminhos de forma explícita.
Cache e mudanças no ambiente
Após instalar ou remover pacotes dentro de um processo ativo, resultados podem não refletir imediatamente todas as mudanças. Em produção, prefira criar o ambiente antes de iniciar os workers e reiniciá-los após atualizações.
Testar código baseado em metadados
Evite depender de todos os pacotes instalados na máquina do desenvolvedor. Use uma distribuição conhecida do ambiente de teste ou abstraia as funções.
def versao_instalada(nome, obter_versao=version):
try:
return obter_versao(nome)
except PackageNotFoundError:
return NoneInjete uma função falsa para testar ausências, versões e formatos incomuns.
Erros frequentes
- Confundir nome da distribuição com nome do import.
- Comparar versões como strings simples.
- Carregar todos os entry points sem validação.
- Presumir que
files()nunca retornaNone. - Expor caminhos de instalações editáveis.
- Tratar metadados como prova de integridade.
- Depender da API antiga de entry points.
Boas práticas
- Use nomes de distribuição nas consultas.
- Trate
PackageNotFoundError. - Use
get_all()para campos repetidos. - Valide entry points antes de carregar.
- Proteja caminhos e origens em relatórios.
- Compare versões com ferramenta adequada.
- Reinicie processos após mudanças no ambiente.
Conclusão
O importlib.metadata no Python oferece uma visão estruturada das distribuições instaladas: versões, requisitos, arquivos, metadados, entry points e relação com pacotes importáveis.
Use esses dados para diagnóstico, plugins e inventários, mas não confunda declaração com confiança. Consulte a documentação oficial do importlib.metadata e as especificações de Core Metadata da PyPA.







