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 = NoneEl 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 pwdEl 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.modulesPrueba 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.







