modulefinder en Python: analiza imports

Publicado el: 13/08/2026
Tempo de leitura: 6 minutos
Red de código binario que representa el grafo de imports analizado con modulefinder en Python

El módulo modulefinder en Python analiza un script e intenta determinar qué módulos importa. El resultado puede ayudar en auditorías de dependencias, preparación de paquetes, diagnóstico de errores de import y generación de informes para herramientas internas.

La herramienta examina el código y sigue imports encontrados durante el análisis. Esto no equivale a ejecutar todas las rutas posibles de la aplicación. Imports construidos dinámicamente, plugins descubiertos por configuración, llamadas a importlib.import_module() y hooks personalizados pueden no aparecer. El informe es evidencia útil, no un inventario perfecto.

Primer informe con ModuleFinder

La clase principal es modulefinder.ModuleFinder. run_script() recibe un archivo Python y report() imprime un resumen.

from modulefinder import ModuleFinder

finder = ModuleFinder()
finder.run_script('aplicacion.py')
finder.report()

El informe contiene módulos descubiertos, rutas e imports ausentes o aparentemente ausentes. En automatizaciones, conviene acceder a los mappings y generar JSON.

Leer el mapping modules

finder.modules asocia nombres con objetos que representan los módulos analizados.

from modulefinder import ModuleFinder

finder = ModuleFinder()
finder.run_script('aplicacion.py')

for nombre, modulo in sorted(finder.modules.items()):
    print(nombre, modulo.__file__)

Los módulos built-in o internos pueden no tener archivo físico. Trata None como un caso válido. Para comprender rutas de instalación, consulta sysconfig en Python.

Detectar módulos ausentes

Los elementos problemáticos aparecen en badmodules. Pueden ser fallos reales, imports opcionales protegidos por try/except ImportError, módulos específicos de plataforma o falsos positivos.

for nombre in sorted(finder.badmodules):
    print('Posiblemente ausente:', nombre)

No hagas fallar el build automáticamente por cualquier entrada. Clasifica dependencias obligatorias y opcionales, considera la plataforma y verifica en un entorno limpio.

Imports opcionales

Muchas bibliotecas intentan cargar aceleradores, backends o integraciones específicas.

try:
    import uvloop
except ImportError:
    uvloop = None

El analizador puede marcar uvloop como ausente aunque exista un fallback válido. Mantén una lista documentada de excepciones en lugar de ignorar todos los avisos.

Personalizar rutas de búsqueda

El constructor acepta una lista path. Si se omite, utiliza sys.path.

finder = ModuleFinder(path=[
    '/proyecto/src',
    '/proyecto/vendor',
])
finder.run_script('/proyecto/src/app.py')

Usa rutas absolutas y controladas. Añadir indiscriminadamente el directorio actual puede hacer que archivos locales oculten dependencias legítimas.

Excluir módulos conocidos

El argumento excludes evita analizar nombres seleccionados.

finder = ModuleFinder(
    excludes=['tkinter', 'tests', 'devtools'],
)

Las exclusiones pueden acelerar el proceso y eliminar árboles opcionales conocidos, pero una lista excesiva oculta dependencias reales. Registra una justificación por elemento.

Normalizar rutas del informe

replace_paths recibe pares (antigua, nueva).

finder = ModuleFinder(
    replace_paths=[
        ('/home/ana/proyecto', '<PROJECT>'),
        ('/opt/build/venv', '<VENV>'),
    ]
)

La normalización facilita comparar máquinas y evita filtrar nombres de usuario o estructuras internas.

Añadir una ruta de paquete

AddPackagePath() registra otra ubicación para un paquete.

import modulefinder

modulefinder.AddPackagePath(
    'mi_plugin',
    '/opt/plugins/mi_plugin',
)

Úsalo solo para layouts conocidos. Si la ruta procede de configuración, resuélvela y limítala a una raíz aprobada.

Reemplazar módulo por paquete

ReplacePackage(oldname, newname) indica que un nombre debe tratarse como otro paquete. Es una función especializada para compatibilidad y estructuras inusuales.

Documenta cada reemplazo. Los informes se vuelven confusos cuando quien los lee no conoce la regla. Si controlas el código, prefiere corregir la estructura.

Usar la línea de comandos

El módulo también puede ejecutarse como script y recibir un archivo Python. La API suele ser mejor en pipelines porque permite filtrar, normalizar y estructurar la salida.

Guarda el informe como artefacto y compáralo entre releases. Una dependencia inesperada puede revelar crecimiento arquitectónico, un import accidental o un cambio de empaquetado.

Generar JSON

import json
from modulefinder import ModuleFinder

finder = ModuleFinder()
finder.run_script('app.py')

informe = {
    'modules': {
        nombre: getattr(modulo, '__file__', None)
        for nombre, modulo in sorted(finder.modules.items())
    },
    'missing': sorted(finder.badmodules),
}

with open('imports.json', 'w', encoding='utf-8') as archivo:
    json.dump(informe, archivo, ensure_ascii=False, indent=2)

Normaliza rutas sensibles antes de escribir y limita el tamaño en grafos enormes.

Comparar versiones

antes = set(informe_anterior['modules'])
despues = set(informe_nuevo['modules'])

print('Añadidos:', sorted(despues - antes))
print('Eliminados:', sorted(antes - despues))

La diferencia refleja alcanzabilidad estática desde el entry point. No prueba que todos los módulos se utilicen en cada ejecución.

Imports dinámicos

from importlib import import_module

nombre = configuracion['backend']
backend = import_module(f'mi_app.backends.{nombre}')

El nombre final no aparece como import literal. Mantén un manifiesto de plugins o combina modulefinder con pruebas de configuraciones soportadas. importlib.resources resuelve recursos empaquetados, no descubrimiento dinámico.

Entry points y plugins

Los plugins instalados pueden descubrirse por metadatos de distribución sin imports literales en el código principal. Modulefinder probablemente no los incluirá.

Consulta entry points instalados y añade los paquetes esperados al inventario. importlib.metadata es la herramienta estándar adecuada.

Imports condicionales por plataforma

import sys

if sys.platform == 'win32':
    import winreg
else:
    import pwd

El informe depende del entorno de análisis. Ejecuta la herramienta en cada plataforma soportada o mantén una matriz documentada.

Entornos virtuales

El analizador utiliza las rutas del proceso actual. Ejecútalo en el mismo entorno virtual usado para pruebas o empaquetado.

Registra versión de Python, sys.path normalizado y conjunto de dependencias. Sin contexto, dos informes pueden diferir por motivos externos al código.

Integración con zipapp

Antes de crear un artefacto con zipapp en Python, el informe puede sugerir dependencias puramente Python que deben copiarse al directorio de build.

No copies automáticamente todo lo encontrado. Filtra biblioteca estándar, extensiones nativas, rutas externas, datos y licencias. El informe es un punto de partida.

Integración con compileall

compileall en Python verifica sintaxis y genera bytecode; modulefinder mapea imports. Cubren fallos distintos.

Un proyecto puede compilar y fallar por dependencia ausente. También puede importar correctamente y contener un error sintáctico en un archivo no alcanzado.

Salida de debug

El parámetro debug aumenta los mensajes internos.

finder = ModuleFinder(debug=2)
finder.run_script('app.py')

Los niveles altos pueden producir logs extensos con rutas locales. Guárdalos en ubicaciones protegidas y con retención corta.

Rendimiento y aislamiento

Grafos grandes consumen tiempo y memoria. Aplica timeout externo, limita tamaño del repositorio y usa un worker aislado para proyectos de terceros.

Aunque la herramienta no ejecuta normalmente toda la aplicación, código desconocido puede estresar parsers y lógica de análisis. No uses privilegios elevados.

Probar la capa de informes

Crea fixtures con imports obligatorios, opcionales, relativos, condicionales y dinámicos.

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

Prueba módulos relevantes al proyecto y evita depender del grafo completo de la biblioteca estándar.

Errores frecuentes

  • Tratar el informe como inventario perfecto.
  • Ignorar imports dinámicos y entry points.
  • Fallar por cualquier módulo opcional ausente.
  • Analizar en un entorno diferente al build.
  • Filtrar rutas locales en informes.
  • Copiar automáticamente todo lo descubierto.
  • Probar una sola plataforma.

Buenas prácticas

  • Usa entornos reproducibles.
  • Normaliza rutas antes de guardar.
  • Clasifica dependencias obligatorias y opcionales.
  • Combina análisis estático con pruebas runtime.
  • Mantén manifiestos para plugins dinámicos.
  • Compara informes entre releases.
  • Aísla código de terceros.

Conclusión

modulefinder en Python revela el grafo de imports alcanzable desde un script, incluyendo rutas descubiertas y módulos potencialmente ausentes. Es útil para auditorías, diagnóstico y preparación de paquetes.

Interpreta la salida con contexto. Imports dinámicos, plugins y condiciones de plataforma requieren comprobaciones adicionales. Consulta la documentación oficial de modulefinder y la referencia del sistema de imports.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador configurando un servidor HTTPS y certificado TLS con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crea un servidor HTTPS local en Python

    Aprende HTTPSServer en Python para crear servicios HTTPS locales, configurar certificados, usar hilos y comprender sus límites.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026