inspect.ispackage es una función de introspección que permite comprobar si un objeto de módulo representa un paquete de Python. Esta diferencia resulta útil en generadores de documentación, sistemas de plugins, analizadores de código, herramientas para IDE y utilidades que recorren árboles de módulos.
En esta guía aprenderás qué considera Python un paquete, cómo utilizar inspect.ispackage, cómo mantener compatibilidad con versiones anteriores y qué errores conviene evitar al importar módulos, trabajar con paquetes de namespace o cargar extensiones.
Qué es un paquete de Python
Un paquete es un módulo que puede contener submódulos o subpaquetes. Los paquetes tradicionales suelen corresponder a directorios con un archivo __init__.py, pero los paquetes de namespace pueden abarcar varios directorios y no necesitan ese archivo. Después de la importación, el objeto del paquete expone metadatos como __spec__, __package__ y normalmente __path__.
Antes de disponer de una función específica, muchas herramientas comprobaban manualmente si existía __path__. Esa técnica funciona en numerosos casos, pero distribuye detalles internos del sistema de importación por todo el proyecto. Una función estándar expresa mejor la intención.
Uso básico
import inspect
import email
import math
print(inspect.ispackage(email))
print(inspect.ispackage(math))
La función devuelve verdadero para objetos que representan paquetes y falso para módulos normales, funciones, clases y otros valores. Recibe un objeto ya importado, no una cadena con el nombre del módulo.
Importar antes de clasificar
import importlib
import inspect
def es_nombre_de_paquete(nombre):
modulo = importlib.import_module(nombre)
return inspect.ispackage(modulo)
print(es_nombre_de_paquete("json"))
print(es_nombre_de_paquete("math"))
Importar un paquete puede ejecutar su código de inicialización. No aceptes nombres arbitrarios enviados por usuarios dentro de un proceso privilegiado. Los sistemas de plugins deben limitar los namespaces permitidos, aislar extensiones riesgosas y registrar los errores de importación.
Compatibilidad con versiones anteriores
Una biblioteca que todavía soporte versiones sin inspect.ispackage puede centralizar un fallback.
import inspect
def es_paquete(objeto):
funcion = getattr(inspect, "ispackage", None)
if funcion is not None:
return funcion(objeto)
return inspect.ismodule(objeto) and hasattr(objeto, "__path__")
Centralizar la compatibilidad facilita las pruebas y evita repetir condiciones de versión en distintos archivos.
Diferencia entre módulo y paquete
Todo paquete importado es un módulo, pero no todo módulo es un paquete. Por eso, inspect.ismodule no indica si el objeto puede contener hijos importables.
import inspect
import pathlib
import statistics
for objeto in (pathlib, statistics):
print(objeto.__name__)
print("módulo:", inspect.ismodule(objeto))
print("paquete:", inspect.ispackage(objeto))
Esta clasificación permite decidir si una herramienta debe inspeccionar únicamente los miembros del módulo o continuar recorriendo una jerarquía.
Descubrir submódulos con pkgutil
import inspect
import pkgutil
import email
if inspect.ispackage(email):
for info in pkgutil.iter_modules(email.__path__):
print(info.name, info.ispkg)
La verificación evita acceder a __path__ en un módulo común. Además, descubrir nombres no obliga a importar todos los hijos, lo que reduce efectos secundarios y tiempo de inicio.
Crear un registro de plugins
import importlib
import inspect
import pkgutil
def descubrir_plugins(nombre_raiz):
raiz = importlib.import_module(nombre_raiz)
if not inspect.ispackage(raiz):
raise TypeError(f"{nombre_raiz!r} no es un paquete")
prefijo = raiz.__name__ + "."
encontrados = []
for info in pkgutil.iter_modules(raiz.__path__, prefijo):
if info.name.endswith("_plugin"):
encontrados.append(info.name)
return sorted(encontrados)
Conviene separar descubrimiento y activación. Primero identifica candidatos, después valida la configuración e importa solamente los plugins autorizados.
Generadores de documentación
Una herramienta de documentación puede usar inspect.ispackage para decidir si debe recorrer submódulos. Aun así, debe aplicar límites de profundidad, excluir módulos privados cuando corresponda y tolerar dependencias opcionales que no estén disponibles.
Importar toda una jerarquía puede abrir recursos, registrar manejadores o requerir bibliotecas nativas. Cuando el código no sea confiable, utiliza aislamiento.
Paquetes de namespace
Los paquetes de namespace pueden reunir partes del mismo espacio de nombres desde distintos directorios. Pueden no tener __init__.py. Por eso es mejor utilizar la semántica del sistema de importación que depender únicamente de la estructura física.
Al recorrer estos paquetes, elimina duplicados y conserva nombres totalmente cualificados.
Distribución instalada y paquete importado
inspect.ispackage clasifica un objeto que ya está en memoria. No verifica si una distribución está instalada, no obtiene su versión ni relaciona automáticamente el nombre importado con el nombre usado por el gestor de paquetes. Para eso utiliza importlib.metadata.
from importlib.metadata import version, PackageNotFoundError
try:
print(version("requests"))
except PackageNotFoundError:
print("distribución no instalada")
Una distribución puede proporcionar varios paquetes y su nombre puede ser distinto del nombre importado.
Manejo correcto de errores
import importlib
import inspect
def describir(nombre):
try:
modulo = importlib.import_module(nombre)
except ModuleNotFoundError as error:
if error.name == nombre:
return {"nombre": nombre, "encontrado": False}
return {"nombre": nombre, "encontrado": True, "dependencia": str(error)}
except Exception as error:
return {"nombre": nombre, "encontrado": True, "error": str(error)}
return {
"nombre": nombre,
"encontrado": True,
"paquete": inspect.ispackage(modulo),
}
No informes todos los errores como si el módulo raíz no existiera. Una dependencia interna ausente es un problema diferente.
Seguridad
Que un objeto sea un paquete no significa que sea confiable. La importación puede ejecutar código arbitrario. Para extensiones utiliza listas permitidas, permisos mínimos, procesos separados, límites de recursos y aprobación administrativa.
Estrategia de pruebas
Incluye un módulo normal, un paquete tradicional y, si es relevante, un paquete de namespace. Prueba el fallback de compatibilidad de forma aislada. Para sistemas de plugins, crea estructuras temporales controladas y verifica que el orden de resultados sea determinista.
No dependas de paquetes externos instalados accidentalmente en la máquina de CI.
Rendimiento
La clasificación es barata. El coste real suele estar en importar y recorrer árboles grandes. Limita la recursión, omite namespaces pesados y separa una fase rápida de descubrimiento de otra fase opcional de análisis profundo.
Aplica caché solamente cuando las mediciones indiquen que es necesaria y define cómo invalidarla durante recargas en desarrollo.
Buenas prácticas
Utiliza nombres totalmente cualificados, ordena los resultados, centraliza la compatibilidad, separa descubrimiento de ejecución y conserva el contexto de los errores. No utilices la estructura del directorio como única fuente de verdad y no confundas clasificación con seguridad.
Contenidos relacionados
Consulta las guías de Academify sobre módulos y paquetes, importlib, entornos virtuales y publicación de paquetes. Revisa también la documentación oficial de inspect y la referencia del sistema de importación.
Conclusión
inspect.ispackage ofrece una forma clara y estándar de distinguir paquetes de módulos normales. Es útil en introspección, plugins, documentación y diagnóstico. Úsala sobre objetos importados, añade un fallback centralizado cuando sea necesario y mantén separadas la clasificación estructural, los metadatos de distribución y las decisiones de confianza.







