El módulo zipapp en Python crea archivos ZIP ejecutables que contienen una aplicación Python. Estos archivos suelen utilizar la extensión .pyz y se pueden iniciar con python aplicacion.pyz. En sistemas POSIX también pueden incluir un shebang y permiso de ejecución para funcionar como un comando normal.
El formato es útil para distribuir utilidades internas, herramientas de línea de comandos, scripts administrativos y aplicaciones puramente Python en un único archivo. No convierte el proyecto en un binario nativo ni incluye automáticamente el intérprete. La máquina de destino necesita una versión compatible de Python y las dependencias deben soportar la ejecución desde un ZIP.
Cómo funciona un archivo .pyz
Una Python Zip Application es un ZIP estándar que contiene __main__.py en la raíz. Al ejecutarlo, Python añade el archivo a sys.path y ejecuta ese módulo como punto de entrada.
mi_app/
├── __main__.py
├── comandos.py
└── datos/
└── config.jsonCrea el archivo con:
python -m zipapp mi_appDespués:
python mi_app.pyzLos imports utilizan el mecanismo normal de Python para archivos ZIP. Para datos empaquetados, usa los patrones explicados en importlib.resources en Python.
Generar un punto de entrada
Si el directorio no contiene __main__.py, la opción -m genera uno que importa y llama una función sin argumentos.
python -m zipapp mi_app \
-m "mi_app.cli:main" \
-o herramienta.pyzLa función debe formar parte del archivo:
# mi_app/cli.py
def main():
print('Aplicación iniciada')El formato requerido es paquete.modulo:funcion. No uses -m si la fuente ya contiene __main__.py.
Añadir un shebang
La opción -p añade una línea de intérprete. En POSIX, zipapp también activa el permiso ejecutable.
python -m zipapp mi_app \
-p "/usr/bin/env python3" \
-o herramienta.pyz
./herramienta.pyzEl intérprete elegido debe ser portable para el público objetivo. /usr/bin/env python3 suele ser más flexible que una ruta absoluta, pero presupone que existe un comando python3 compatible.
Comprimir o almacenar
Por defecto, los archivos se guardan sin compresión. Usa --compress para aplicar deflate:
python -m zipapp mi_app --compress -o herramienta.pyzLa compresión reduce el tamaño, aunque puede aumentar el trabajo de construcción y lectura. El código fuente suele comprimir bien; formatos ya comprimidos, como JPEG o PNG, cambian poco.
Automatizar con create_archive()
La API Python ofrece la misma funcionalidad.
import zipapp
zipapp.create_archive(
source='mi_app',
target='dist/herramienta.pyz',
interpreter='/usr/bin/env python3',
main='mi_app.cli:main',
compressed=True,
)source puede ser un directorio, un archivo existente o un stream binario. target puede ser una ruta o un stream abierto para escritura binaria. El llamador debe cerrar los streams que proporciona.
Filtrar el contenido del build
El parámetro filter recibe un Path relativo y decide si el elemento entra en el archivo.
from pathlib import Path
import zipapp
IGNORADOS = {'__pycache__', '.git', '.pytest_cache'}
def incluir(ruta: Path) -> bool:
if any(parte in IGNORADOS for parte in ruta.parts):
return False
return ruta.suffix not in {'.pyc', '.log', '.env'}
zipapp.create_archive(
'mi_app',
'dist/mi_app.pyz',
filter=incluir,
compressed=True,
)Excluye secretos, archivos de entorno, logs, caches y artefactos de desarrollo. Construye desde un directorio limpio e inspecciona el ZIP final.
Incluir dependencias puramente Python
Instala las dependencias en el árbol de build antes de empaquetar:
python -m pip install \
--requirement requirements.txt \
--target build/mi_app
python -m zipapp build/mi_app \
-m "mi_app.cli:main" \
-o dist/mi_app.pyzFija versiones y hashes para builds reproducibles. No instales dependencias directamente en la carpeta de código fuente; usa un directorio descartable.
Las extensiones C no cargan desde el ZIP
Los módulos nativos como archivos .so o .pyd normalmente no pueden cargarse directamente desde el archivo porque el loader del sistema operativo exige objetos reales en el sistema de archivos.
Paquetes como NumPy, bibliotecas criptográficas, drivers y procesadores de imágenes pueden incluir componentes nativos. Exígelos de forma externa, distribuye binarios compatibles junto al .pyz o utiliza otro método de empaquetado. Considera arquitectura y sistema operativo.
Leer recursos empaquetados correctamente
No supongas que __file__ apunta a un directorio normal. Un recurso dentro del ZIP puede no existir como ruta persistente.
from importlib.resources import files
texto = (
files('mi_app.datos')
.joinpath('config.json')
.read_text(encoding='utf-8')
)Si una API requiere una ruta física, usa as_file() dentro de un context manager. La guía de importlib.resources explica el flujo completo.
Consultar el intérprete incorporado
zipapp.get_interpreter() lee el shebang.
import zipapp
interprete = zipapp.get_interpreter('dist/mi_app.pyz')
print(interprete)En la CLI, python -m zipapp archivo.pyz --info ofrece el mismo diagnóstico. Úsalo en releases para confirmar el launcher esperado.
Copiar y modificar un archivo existente
create_archive() puede copiar un .pyz existente y reemplazar su línea de intérprete.
zipapp.create_archive(
'antiguo.pyz',
'nuevo.pyz',
interpreter='/usr/bin/env python3',
)La ruta de entrada y salida no puede ser la misma. Escribe un archivo nuevo, valídalo y sustituye el anterior de manera atómica.
Evitar sobrescrituras inseguras
La documentación muestra una modificación mediante BytesIO, pero advierte que un error durante la sobrescritura puede destruir el original. En producción, usa un archivo temporal vecino y os.replace().
import os
import zipapp
nuevo = 'app.pyz.nuevo'
zipapp.create_archive('app.pyz', nuevo, '/usr/bin/env python3')
validar(nuevo)
os.replace(nuevo, 'app.pyz')Artefactos reproducibles
Un build repetible requiere controlar versión de Python, dependencias, archivos, permisos, timestamps y orden del ZIP. Registra el checksum final.
import hashlib
from pathlib import Path
datos = Path('dist/app.pyz').read_bytes()
print(hashlib.sha256(datos).hexdigest())compileall en Python y py_compile ayudan a verificar sintaxis, pero el bytecode no elimina restricciones de versión.
Seguridad de distribución
Un .pyz es código ejecutable. Firma el artefacto o publica hashes por un canal confiable, limita quién puede crear releases y nunca ejecutes archivos desconocidos.
Empaquetar en ZIP no oculta el contenido. No incluyas claves API, contraseñas, certificados privados ni configuración de producción.
Compatibilidad de versiones
El archivo debe ser compatible con el intérprete de destino. Sintaxis nueva, APIs recientes y requisitos de dependencias pueden fallar en versiones antiguas. Declara una versión mínima y prueba una matriz de entornos limpios.
import sys
if sys.version_info < (3, 11):
raise SystemExit('Se requiere Python 3.11 o superior')Un shebang no expresa “versión X.Y o posterior”. Solo apunta a un comando.
Cuándo usar zipapp
Usa zipapp cuando la aplicación es principalmente Python puro, los usuarios ya tienen un intérprete compatible y un único archivo simplifica la operación. Las automatizaciones internas y utilidades CLI son buenos candidatos.
Evítalo cuando necesitas incluir Python, dependes de extensiones nativas, requieres rutas físicas persistentes o necesitas un instalador nativo. Wheels, containers o empaquetadores de ejecutables pueden ser mejores.
Probar el artefacto final
No pruebes únicamente el directorio fuente. Ejecuta el archivo generado en un entorno limpio:
python dist/mi_app.pyz --version
python dist/mi_app.pyz diagnosticoComprueba que no dependa de paquetes globales. Prueba encoding, recursos, argumentos, errores, códigos de salida y versiones soportadas. La documentación generada con pydoc en Python puede ayudar a revisar APIs.
Errores frecuentes
- No incluir
__main__.pyni el parámetromain. - Empaquetar secretos y archivos de desarrollo.
- Suponer que las extensiones nativas funcionan dentro del ZIP.
- Usar
__file__para todos los recursos. - Elegir un shebang no portable.
- Sobrescribir el original sin reemplazo atómico.
- Probar solo en la máquina de build.
Buenas prácticas
- Construye en un directorio limpio.
- Fija dependencias y registra hashes.
- Filtra caches, logs y secretos.
- Usa importlib.resources para datos.
- Prueba el
.pyzreal en entornos limpios. - Documenta la versión mínima de Python.
- Distribuye mediante un canal confiable.
Conclusión
zipapp en Python convierte un árbol de código en una aplicación ejecutable de archivo único. Los entry points, shebangs, filtros y dependencias puramente Python lo hacen práctico para muchas herramientas internas.
No incluye el intérprete ni resuelve dependencias nativas. Planifica compatibilidad, recursos, seguridad y pruebas. Consulta la documentación oficial de zipapp y la documentación de zipimport.







