El módulo importlib.metadata en Python permite consultar información de paquetes de distribución instalados en el entorno. Puede devolver versiones, metadatos, dependencias declaradas, archivos instalados, entry points y la relación entre nombres usados en import y nombres utilizados por herramientas como pip.
La distinción es importante. La distribución PyYAML proporciona el módulo importable yaml. Una distribución puede ofrecer varios paquetes importables y un namespace package puede estar formado por varias distribuciones. No presupongas una correspondencia uno a uno.
Distribución frente a paquete importable
Una distribución es la unidad instalada por herramientas de packaging y suele incluir metadatos .dist-info o .egg-info. Un import package es el nombre utilizado en el código.
from importlib.metadata import packages_distributions
mapa = packages_distributions()
print(mapa.get('yaml'))
# normalmente ['PyYAML']El mapping ayuda en auditorías y mensajes de soporte. Algunas instalaciones editables no proporcionan todos los nombres de nivel superior, así que puede faltar información.
Consultar la versión instalada
version() devuelve la versión de la distribución como texto.
from importlib.metadata import version
print(version('pip'))No conviertas el resultado a float. Las versiones pueden contener varios componentes, pre-releases e identificadores locales. Para comparar, usa una implementación compatible con PEP 440.
Tratar PackageNotFoundError
Las consultas a una distribución no instalada generan PackageNotFoundError.
from importlib.metadata import PackageNotFoundError, version
try:
actual = version('mi-plugin')
except PackageNotFoundError:
actual = NoneDistingue una distribución ausente de un import roto. Los metadatos pueden existir aunque el módulo falle por una biblioteca nativa o configuración.
Leer metadatos completos
metadata() devuelve un objeto parecido a mapping con campos 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'))Campos como Classifier, Project-URL y Requires-Dist pueden repetirse. Usa get_all() cuando necesites todos los valores.
Metadatos compatibles con JSON
La propiedad json presenta una forma compatible con PEP 566.
datos = metadata('pip').json
print(datos.get('requires_python'))Los valores siguen procediendo del paquete instalado. Valida campos y tipos antes de enviarlos a APIs o informes externos.
Consultar dependencias declaradas
requires() devuelve los requisitos declarados por una distribución.
from importlib.metadata import requires
requisitos = requires('mi-paquete') or []
for requisito in requisitos:
print(requisito)Las cadenas pueden incluir markers de plataforma, versión de Python y extras. Describen requisitos declarados, no confirman que el entorno sea consistente.
Listar archivos instalados
files() devuelve objetos PackagePath con tamaño, hash y distribución cuando esos datos existen.
from importlib.metadata import files
items = files('pip')
if items is not None:
for item in list(items)[:10]:
print(item, item.size, item.hash)La función puede devolver None si la instalación no incluye el archivo que registra la lista. Protege siempre la iteración.
Localizar un archivo físico
PackagePath.locate() resuelve la ubicación instalada.
for item in files('pip') or []:
if str(item).endswith('__init__.py'):
print(item.locate())
breakNo muestres rutas absolutas sin necesidad: pueden revelar usuarios, entornos virtuales y estructura del servidor.
Hashes registrados
Cuando RECORD contiene hash y tamaño, PackagePath los expone. Puedes comparar el contenido instalado con el valor registrado.
No todos los archivos tienen hash y los metadatos locales no equivalen a una firma externa. Quien pueda alterar archivo y RECORD puede modificar ambos.
Entry points instalados
entry_points() devuelve objetos EntryPoint. Selecciona por grupo y opcionalmente por nombre.
from importlib.metadata import entry_points
plugins = entry_points(group='mi_app.plugins')
for plugin in plugins:
print(plugin.name, plugin.value, plugin.dist.name)Los grupos son convenciones definidas por autores. console_scripts es un ejemplo común; los sistemas propios deberían usar un nombre claro.
Cargar un entry point
EntryPoint.load() importa el módulo y resuelve el objeto configurado.
(plugin,) = entry_points(
group='mi_app.plugins',
name='csv',
)
clase = plugin.load()Cargar ejecuta imports y puede producir efectos. Inspecciona y valida metadatos antes de cargar plugins aprobados.
Inspeccionar sin cargar
Las propiedades module, attr, extras, name, group, value y dist permiten auditar sin importar el destino.
for ep in entry_points(group='console_scripts'):
print(ep.name, ep.module, ep.attr)Los metadatos siguen siendo suministrados por paquetes instalados y no deben considerarse confiables solo por existir.
Cambios de API
Versiones antiguas devolvían estructuras diferentes. En Python moderno, entry_points() devuelve una colección EntryPoints seleccionable. Desde Python 3.13, EntryPoint ya no tiene interfaz de tupla.
Las bibliotecas que soportan versiones antiguas deben probar compatibilidad o usar el backport importlib_metadata con una versión controlada.
Obtener una Distribution
distribution() devuelve un objeto con versión, metadatos, archivos, requisitos y entry points.
from importlib.metadata import distribution
dist = distribution('pip')
print(dist.version)
print(dist.metadata.get('License'))
print(len(dist.entry_points))Instancias distintas no comparan necesariamente como iguales aunque representen la misma instalación. Compara nombre normalizado y versión.
Origen de instalaciones editables
Desde Python 3.13, Distribution.origin puede mostrar información PEP 610 de paquetes editables.
dist = distribution('mi-paquete')
if dist.origin is not None:
print(dist.origin.url)El origen puede contener rutas locales. Normaliza o elimina valores sensibles en informes compartidos.
Mapear imports a distribuciones
packages_distributions() devuelve listas porque un namespace package puede estar formado por varias distribuciones.
mapa = packages_distributions()
for paquete, distribuciones in sorted(mapa.items()):
if len(distribuciones) > 1:
print(paquete, distribuciones)Esto complementa modulefinder en Python, que sigue imports desde un script, y pkgutil, que lista módulos disponibles.
Auditar el entorno
distributions() recorre las distribuciones instaladas.
from importlib.metadata import distributions
for dist in sorted(
distributions(),
key=lambda d: d.metadata['Name'].lower(),
):
print(dist.metadata['Name'], dist.version)Los entornos grandes pueden incluir instalaciones editables, metadatos incompletos y duplicados aparentes. Registra versión de Python y ubicación del entorno.
Generar inventario 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))No incluyas descripciones largas ni rutas absolutas por defecto. Un SBOM completo requiere otros campos y formatos.
Comparar versiones mínimas
Usa un parser correcto en lugar de comparación textual.
from importlib.metadata import version
from packaging.version import Version
if Version(version('mi-plugin')) < Version('2.0'):
raise RuntimeError('Actualiza mi-plugin')packaging es una dependencia externa. Comparaciones como '10' < '2' son incorrectas.
Política de confianza para plugins
Cualquier distribución instalada puede registrar un entry point. Verifica proveedor, versión, configuración y permisos.
APROBADOS = {'plugin-oficial', 'plugin-interno'}
for ep in entry_points(group='mi_app.plugins'):
if ep.dist.name not in APROBADOS:
continue
cargar_plugin(ep)En sistemas sensibles, instala plugins desde un repositorio controlado y con hashes fijados.
Los metadatos no prueban integridad
Nombre, versión, licencia y URLs son declaraciones del paquete. No garantizan autenticidad, seguridad ni compatibilidad. Combina metadatos con fuente confiable, hashes, firmas y pruebas.
Distribuciones ZIP y providers personalizados
Por defecto, los metadatos pueden estar en el sistema de archivos o en ZIPs de sys.path. Importers personalizados pueden implementar find_distributions() y devolver objetos Distribution.
Un provider debe respetar filtros de nombre y ruta. Prueba cuidadosamente el descubrimiento personalizado.
Diferencias en sys.path
El descubrimiento de metadatos no interpreta todos los valores de sys.path igual que el import normal. Bytes se ignoran y objetos pathlib.Path pueden aceptarse incidentalmente.
Mantén sys.path convencional y usa sysconfig en Python para rutas explícitas.
Cambios del entorno
Instalar o eliminar paquetes dentro de un proceso activo puede dejar observaciones obsoletas. En producción, prepara el entorno antes de iniciar workers y reinícialos después de actualizar.
Probar código basado en metadatos
Evita depender de todos los paquetes instalados en la máquina del desarrollador. Abstrae las funciones e inyecta dobles.
def version_instalada(nombre, obtener_version=version):
try:
return obtener_version(nombre)
except PackageNotFoundError:
return NonePrueba distribuciones ausentes, versiones extrañas, listas de archivos vacías y entry points no aprobados.
Errores frecuentes
- Confundir distribución con import.
- Comparar versiones como cadenas simples.
- Cargar todos los entry points automáticamente.
- Suponer que
files()nunca devuelveNone. - Exponer rutas de instalaciones editables.
- Tratar metadatos como prueba de integridad.
- Depender de la API antigua de entry points.
Buenas prácticas
- Consulta mediante nombres de distribución.
- Trata
PackageNotFoundError. - Usa
get_all()para campos repetidos. - Valida entry points antes de cargar.
- Oculta rutas y orígenes en informes.
- Compara versiones con un parser adecuado.
- Reinicia procesos después de cambios.
Conclusión
importlib.metadata en Python proporciona una vista estructurada de las distribuciones instaladas: versiones, requisitos, archivos, metadatos, entry points y relación con paquetes importables.
Usa estos datos para diagnóstico, plugins e inventarios, pero no confundas declaración con confianza. Consulta la documentación oficial de importlib.metadata y las especificaciones de Core Metadata de PyPA.







