El módulo zipimport en Python permite que el sistema de imports cargue módulos y paquetes directamente desde archivos ZIP. La mayoría de las aplicaciones no necesita instanciarlo manualmente: cuando una ruta ZIP aparece en sys.path, Python selecciona automáticamente un zipimporter.
Esta capacidad sirve para distribuir colecciones de módulos en un solo archivo, cargar plugins empaquetados, reducir miles de archivos pequeños y comprender aplicaciones .pyz. Importar desde ZIP no vuelve seguro el código externo. El módulo se ejecuta en el proceso actual con los mismos privilegios de la aplicación.
Cómo funciona la importación desde ZIP
Una entrada de sys.path suele apuntar a un directorio, pero también puede nombrar un ZIP. El importador busca archivos .py y .pyc respetando la estructura de paquetes.
import sys
sys.path.insert(0, "plugins.zip")
import mi_plugin
print(mi_plugin.__file__)
# plugins.zip/mi_plugin.pyNo necesitas escribir import zipimport en este caso. El mecanismo de imports elige el finder apropiado.
Estructura de paquetes
Los paquetes tradicionales conservan su estructura normal, incluyendo __init__.py cuando corresponda.
plugins.zip
├── mi_plugin/
│ ├── __init__.py
│ ├── comandos.py
│ └── validacion.py
└── utilidad.pyDespués de añadir el ZIP, instrucciones como from mi_plugin import comandos siguen las reglas habituales.
Usa un subdirectorio interno como raíz
La entrada puede apuntar a una carpeta dentro del archivo:
sys.path.insert(0, "paquete.zip/lib")
import bibliotecaSolo lib/ se trata como raíz de imports. Esto ayuda cuando el ZIP contiene documentación, recursos y metadatos además del código.
Instancia zipimporter directamente
Herramientas de diagnóstico y loaders personalizados pueden crear zipimport.zipimporter.
from zipimport import zipimporter, ZipImportError
try:
importador = zipimporter("plugins.zip")
except ZipImportError as error:
print(f"Archivo inválido: {error}")
else:
spec = importador.find_spec("mi_plugin")
print(spec)El constructor acepta el archivo completo o un prefijo interno como plugins.zip/lib. Una ruta inválida genera ZipImportError, también subclase de ImportError.
Prefiere find_spec
Los métodos antiguos fueron eliminados. Usa find_spec(), que devuelve un ModuleSpec compatible con importlib.
spec = importador.find_spec("mi_plugin.comandos")
if spec is None:
raise ModuleNotFoundError("Plugin no encontrado")
modulo = __import__("mi_plugin.comandos", fromlist=["*"])En código normal, una instrucción import sigue siendo más sencilla.
Lee fuente sin ejecutar
get_source() devuelve el texto fuente cuando está presente.
fuente = importador.get_source("mi_plugin")
if fuente is not None:
print(fuente[:200])Esto evita ejecutar el módulo, pero la fuente continúa siendo dato no confiable. Limita su tamaño y no la publiques sin autorización.
Para listar clases y funciones sin importar, consulta pyclbr en Python.
Obtén el objeto de código
get_code() devuelve el objeto de código del módulo.
objeto = importador.get_code("mi_plugin")
print(objeto.co_filename)Ejecutarlo con exec() tiene los mismos riesgos que importar. No es un sandbox.
Lee datos auxiliares
get_data() devuelve bytes de una ruta dentro del archivo.
datos = importador.get_data(
"plugins.zip/mi_plugin/config.json"
)Para recursos de paquetes, importlib.resources ofrece una interfaz más estructurada. Consulta importlib.resources en Python.
Inspecciona nombre y tipo
get_filename() devuelve el valor que tendría __file__. is_package() informa si el nombre representa un paquete.
print(importador.get_filename("mi_plugin"))
print(importador.is_package("mi_plugin"))Son útiles para navegadores, validadores de plugins y diagnósticos.
Limitación de extensiones nativas
El importador carga fuente y bytecode Python, pero no extensiones dinámicas .so o .pyd directamente. El loader del sistema operativo necesita binarios como archivos reales.
Instala las dependencias nativas o extrae binarios verificados a un directorio controlado. La guía de zipapp en Python explica la misma limitación.
Fuente frente a bytecode
Si el ZIP solo contiene .py, Python compila durante el import, pero no modifica el archivo para añadir .pyc. Nuevos procesos pueden repetir ese trabajo.
El bytecode incluido debe coincidir con el intérprete. El formato cambia entre versiones, por lo que la fuente suele ser más portable. Un .pyc no es cifrado.
Crea el ZIP
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
with ZipFile("plugins.zip", "w", ZIP_DEFLATED) as archivo:
for ruta in Path("plugins").rglob("*.py"):
archivo.write(ruta, ruta.relative_to("plugins"))Excluye caches, artefactos, archivos de entorno, claves y credenciales. Normaliza las rutas y valida el resultado.
Sistemas de plugins empaquetados
Un servicio puede aceptar solo ZIPs firmados en un directorio aprobado, verificar hashes y cargar un entry point conocido. No debe ejecutar todos los módulos descubiertos.
from importlib import import_module
PERMITIDOS = {"informes", "exportacion"}
nombre = validar_nombre()
if nombre not in PERMITIDOS:
raise PermissionError("Plugin no autorizado")
plugin = import_module(f"plugins.{nombre}")Una allowlist reduce errores, pero no aísla código hostil. Ejecuta plugins de terceros en otro proceso o contenedor.
Colisiones y orden de sys.path
Insertar el ZIP en la posición cero puede sombrear módulos confiables con el mismo nombre. Un json.py empaquetado podría sustituir la biblioteca estándar esperada.
Usa namespaces propios, evita poner archivos no confiables primero y registra el origen. Consulta site en Python y pkgutil en Python.
Cache del importador
invalidate_caches() limpia el listado interno.
importador.invalidate_caches()Si el archivo cambia durante la ejecución, invalida caches y evita reemplazos concurrentes. Es mejor publicar nombres versionados e inmutables y reiniciar workers.
Actualizaciones atómicas
No edites el ZIP en el lugar mientras otros procesos importan. Crea uno nuevo, verifica integridad y firma, sincroniza la escritura y reemplaza la ruta de forma atómica. Los módulos ya presentes en sys.modules siguen cargados.
ZIP64 y tamaño
Las versiones actuales soportan ZIP64, pero archivos enormes aumentan tiempo de apertura, memoria y riesgo de denegación de servicio. Define límites de tamaño y entradas.
Seguridad
- Importar ejecuta código con privilegios del proceso.
- Verifica editor, firma y hash.
- Restringe rutas y nombres calificados.
- No importes directamente uploads arbitrarios.
- Evita sombrear módulos confiables.
- Limita tamaño, entradas y profundidad.
- Aísla plugins no confiables.
- Registra versión y entry point.
Pruebas
Prueba paquetes, subpaquetes, archivos solo con fuente, solo con bytecode, prefijos internos, ZIP corrupto, comentarios, ZIP64 y colisiones. Verifica todas las versiones y plataformas objetivo.
Confirma que dependencias nativas fallen con un mensaje claro y que el sistema no use un módulo inesperado instalado en otra ruta.
Conclusión
zipimport en Python integra archivos ZIP con el mecanismo estándar de imports y permite cargar módulos sin extracción. Ofrece acceso a specs, fuente, objetos de código, datos, estado de paquete y nombres de archivo.
Úsalo con políticas claras de origen, rutas e aislamiento. Consulta la documentación oficial de zipimport y la PEP 273 sobre imports desde ZIP.







