# `zipapp` — Manage executable Python zip archives

Added in version 3.5.

Código fuente: Lib/zipapp.py

Este módulo provee herramientas para administrar la creación de archivos zip que contengan código Python, los que pueden ser ejecutados directamente por el intérprete de Python. El módulo provee tanto una Interfaz de línea de comando y una API de Python.

## Ejemplo básico

El siguiente ejemplo muestra cómo la Interfaz de línea de comando puede utilizarse para crear un archivador ejecutable de un directorio que contenga código en Python. Al ponerse en funcionamiento, el archivador ejecutará la función `main` del módulo `myapp` en el archivador.

    $ python -m zipapp myapp -m "myapp:main"
    $ python myapp.pyz
    <output from myapp>

## Interfaz de línea de comando

En la ejecución como programa desde la línea de comandos, se utiliza el siguiente formato:

    $ python -m zipapp source [options]

Si source es un directorio, se creará un archivador zip para los contenidos de source. Si source es un archivo, debería ser un archivador, y se copiará al archivador de destino (o los contenidos de su línea shebang se mostrarán si se especifica la opción –info).

Se aceptan las siguientes opciones:

-o <output>, --output=<output>

  Escribe la salida a un archivo llamado output. Si esta opción no está especificada, el nombre del archivo de salida será el mismo que la entrada source, con la extensión `.pyz` agregada. Si se provee de un nombre de archivo explícito, se usa tal como está (por lo que una extensión `.pyz` debería incluirse si esto se requiere).

  Un nombre de archivo de salida debe especificarse si source es un archivador (y en ese caso, output no debería ser igual que source).

-p <interpreter>, --python=<interpreter>

  Agrega una línea `#!` al archivador especificando interpreter como comando a ejecutar. También en POSIX, convierte al archivador en ejecutable. La opción por defecto es no escribir una línea `#!`, y no hacer que el archivo sea ejecutable.

-m <mainfn>, --main=<mainfn>

  Escribe un archivo `__main__.py` en el archivador que ejecuta mainfn. El argumento mainfn debería tener la forma «pkg.mod:fn», donde «pkg.mod» is un paquete/módulo en el archivador, y «fn» es un invocable (callable) en ese módulo. El archivo `__main__.py` ejecutará ese invocable.

  `--main` no puede especificarse al copiar un archivador.

-c, --compress

  Comprime los archivos con el método deflate, lo que reduce el tamaño del archivo de salida. Por defecto, los archivos se guardan sin comprimir en el archivador.

  `--compress` no surte efecto al copiar un archivador.

  Added in version 3.7.

--info

  Muestra el intérprete incrustado en el archivador, para diagnósticos. En este caso cualquier otra opción se ignora, y SOURCE debe ser un archivador, no un directorio.

-h, --help

  Muestra un breve mensaje sobre el modo de uso, y sale.

## API de Python

El módulo define dos funciones convenientes:

zipapp.create_archive(source, target=None, interpreter=None, main=None, filter=None, compressed=False)

  Crea un archivador de aplicación a partir de source. Este source (origen), puede ser cualquiera de los siguientes:

  - El nombre de un directorio, o un path-like object que se refiera a un directorio, en cuyo caso un nuevo archivador de aplicación se creará a partir del contenido de dicho directorio.

  - El nombre de un archivador de aplicación existente o un path-like object que refiera a dicho archivo, en cuyo caso el archivo se copiará al destino (modificándolo para reflejar el valor dado por el argumento interpreter. El nombre de archivo debería incluir la extensión `.pyz`, si se requiere.

  - Un objeto archivo abierto para lectura en modo bytes. El contenido del archivo debería ser un archivador de aplicación. Se infiere que el objeto archivo está posicionado al comienzo del archivador.

  El argumento target determina dónde quedará escrito el archivador resultante:

  - Si es el nombre de un archivo, o un path-like object, el archivador será escrito a ese archivo.

  - Si es un objeto archivo abierto, el archivador se escribirá en ese objeto archivo, el cual debe estar abierto para escritura en modo bytes.

  - Si el destino (target) se omite (o es `None`), el origen (source) debe ser un directorio, y el destino será un archivo con el mismo nombre que el origen, con la extensión `.pyz` añadida.

  El argumento interpreter especifica el nombre del intérprete Python con el que el archivador será ejecutado. Se escribe como una línea «shebang» al comienzo del archivador. En POSIX, el Sistema Operativo será quien lo interprete, y en Windows será gestionado por el lanzador Python. Omitir el interpreter tendrá como consecuencia que no se escribirá ninguna línea shebang. Si se especifica un intérprete y el destino (target) es un nombre de archivo, el bit de ejecución del archivo destino será activado.

  El argumento main especifica el nombre de un invocable (callable) que se utilizará como programa principal para el archivador. Solamente se puede especificar si el origen (source) es un directorio que no contiene un archivo `__main__.py`. El argumento main debería tener la forma «pkg.module:callable», y el archivador será ejecutado importando «pkg.module» y ejecutando el callable sin argumentos. Es un error omitir main si el origen es un directorio que no contiene un archivo `__main__.py`, ya que esto resultaría en un archivador no ejecutable.

  El argumento opcional filter especifica una función callback que se pasa como objeto Path, para representar el path (ruta) al archivo que se está añadiendo (ruta relativa al directorio origen, source). Debería retornar `True` si el archivo será añadido.

  El argumento opcional compressed determina si los archivos están comprimidos. Si se define como `True`, los archivos en el archivador serán comprimidos con el método deflate.

  Si se especifica un objeto archivo para source o target, es responsabilidad de quien invoca cerrarlo luego de invocar a create_archive.

  Al copiar un archivador existente, los objetos archivo provistos, solamente necesitan los métodos `read` y `readline`, o bien `write`. Al crear un archivador a partir de un directorio, si el destino (target) es un objeto archivo, éste se pasará a la clase `zipfile.ZipFile`, y debe proveer los métodos que esa clase necesita.

  Distinto en la versión 3.7: Added the filter and compressed parameters.

zipapp.get_interpreter(archive)

  Retorna el intérprete especificado en la línea `#!` al comienzo del archivador. Si no hay línea `#!`, retorna `None`. El argumento archive (archivador), puede ser un nombre de archivo o un objeto tipo archivo abierto para lectura en modo bytes. Se supone que está al principio del archivador.

## Ejemplos

Empaqueta un directorio en un archivador, y lo ejecuta.

    $ python -m zipapp myapp
    $ python myapp.pyz
    <output from myapp>

Lo mismo puede lograrse utilizando la función `create_archive()`:

    >>> import zipapp
    >>> zipapp.create_archive('myapp', 'myapp.pyz')

Para que la aplicación sea ejecutable directamente en POSIX, especifica un intérprete.

    $ python -m zipapp myapp -p "/usr/bin/env python"
    $ ./myapp.pyz
    <output from myapp>

Para reemplazar la línea shebang en un archivador existente, cree un archivador modificado, utilizando la función `create_archive()`:

    >>> import zipapp
    >>> zipapp.create_archive('old_archive.pyz', 'new_archive.pyz', '/usr/bin/python3')

Para actualizar el archivo en el lugar, reemplaza en memoria utilizando un objeto `BytesIO`, y luego sobreescribe el origen (source). Nótese que hay un riesgo al sobreescribir un archivo en el lugar, ya que un error resultará en la pérdida del archivo original. Este código no ofrece protección contra este tipo de errores, pero los códigos de producción deberían hacerlo. Además, este método solamente funcionará si el archivador cabe en la memoria:

    >>> import zipapp
    >>> import io
    >>> temp = io.BytesIO()
    >>> zipapp.create_archive('myapp.pyz', temp, '/usr/bin/python2')
    >>> with open('myapp.pyz', 'wb') as f:
    >>>     f.write(temp.getvalue())

## Especificar el intérprete

Nótese que si se especifica el intérprete y luego se distribuye el archivador de aplicación, es necesario asegurarse de que el intérprete utilizado es portable. El lanzador Python para Windows soporta las formas más comunes de líneas `#!` POSIX, pero hay otras cuestiones a considerar:

- Si se utiliza «/usr/bin/env python» (u otras formas del comando «python», como «/usr/bin/python»), es necesario considerar que los usuarios quizá tengan tanto Python2 como Python3 como versión por defecto, y el código debe escribirse bajo ambas versiones.

- Si se utiliza una versión específica, por ejemplo «/usr/bin/env python3», la aplicación no funcionará para los usuarios que no tengan esa versión. (Esta puede ser la opción deseada si no se hecho el código compatible con Python 2).

- No hay manera de decir «python X.Y o posterior», así que se debe ser cuidadoso al utilizar una versión exacta, tal como «/usr/bin/env python3.4», ya que será necesario cambiar la línea shebang para usuarios de Python 3.5, por ejemplo.

Normalmente, se debería utilizar «/usr/bin/env python2» o «/usr/bin/env python3», según si el código está escrito para Python 2 ó 3.

## Creando aplicaciones independientes con zipapp

Utilizando el módulo `zipapp`, es posible crear programas Python auto-contenidos, que pueden ser distribuidos a usuarios finales que solo necesitarán una versión adecuada de Python instalada en sus sistemas. La clave es empaquetar todas las dependencias de la aplicación dentro del archivador, junto al código de la misma.

Los pasos para crear un archivador independiente son los siguientes:

1. Crea tu aplicación en un directorio normalmente, tal que tengas una directorio `myapp` que contenga un archivo `__main__.py`, y cualquier código extra de la aplicación.

2. Instala todas las dependencias de la aplicación en el directorio `myapp`, usando pip:

       $ python -m pip install -r requirements.txt --target myapp

   (se supone que tienes los requisitos de tu proyecto en un archivo `requirements.txt`, de lo contrario, puedes listar manualmente las dependencias en la línea de comandos de pip).

3. Empaqueta la aplicación utilizando:

       $ python -m zipapp -p "interpreter" myapp

Esto producirá un ejecutable independiente, que puede ser ejecutado en cualquier máquina que disponga del intérprete apropiado. Véase Especificar el intérprete para más detalles. Puede enviarse a los usuarios como un solo archivo.

En Unix, el archivo `myapp.pyz` será ejecutable tal como está. Puede ser renombrado, para quitar la extensión `.pyz` si se prefiere un nombre de comando «simple». En Windows, el archivo `myapp.pyz[w]` es ejecutable, ya que el intérprete Python registra las extensiones `.pyz` y `pyzw` al ser instalado.

### Advertencias

Si la aplicación depende de un paquete que incluye una extensión C, ese paquete no puede ser ejecutado desde un archivo zip (esta es una limitación del sistema operativo, dado que el código ejecutable debe estar presente en el sistema de archivos para que el loader del SO lo pueda cargar). En este caso, se puede excluir la dependencia del archivo zip, y requerir que los usuarios la tengan instalada, o bien distribuirla conjuntamente con el archivo zip, y agregar código al `__main__.py` para que incluya el directorio que contiene el módulo descomprimido en `sys.path`. En este caso, será necesario asegurarse de distribuir los binarios adecuados para la/s arquitectura/s a las que esté destinada la aplicación (y potencialmente elegir la versión correcta para agregar a `sys.path` en tiempo de ejecución, basándose en la máquina del usuario).

## El formato de archivado Zip de aplicaciones Python

Python puede ejecutar archivadores zip que contengan un archivo `__main__.py` desde la versión 2.6. Para que sea ejecutada por Python, basta con que una aplicación de archivador sea un archivo zip estándar que contenga un archivo `__main__.py`, el cual será ejecutado como punto de entrada para la aplicación. Como es usual para cualquier script Python, el elemento padre del script (en este caso, el archivo zip), será ubicado en `sys.path`, por lo que pueden importarse otros módulos desde el archivo zip.

El formato de archivo zip, permite que se antepongan al archivo datos arbitrarios. El formato de aplicación zip utiliza esta capacidad para anteponer una línea «shebang» POSIX estándar al archivo (`#!/ruta/al/interprete`).

Formalmente, el Formato de archivado Zip de aplicaciones Python es:

1. An optional shebang line, containing the characters `b'#!'` followed by an interpreter name, and then a newline (`b'\n'`) character. The interpreter name can be anything acceptable to the OS «shebang» processing, or the Python launcher on Windows. The interpreter should be encoded in UTF-8 on Windows, and in `sys.getfilesystemencoding()` on POSIX.

2. Los datos estándares de archivo zip, tal como se generan en el módulo `zipfile`. El contenido del archivo zip debe incluir un archivo llamado `__main__.py` (que debe estar en la «raíz» del archivo zip, es decir, no puede estar en un subdirectorio). El los datos del archivo zip pueden estar comprimidos o no.

Si un archivador de aplicación tiene una línea de shebang, puede tener el bit de ejecución activado en los sistemas POSIX, para permitir que sea ejecutado directamente.

No se requiere que las herramientas de este módulo sean las que se utilicen para crear archivadores de aplicación. El módulo es útil, pero cualquier archivo que esté en el formato descripto anteriormente es aceptable para Python, no importa cómo haya sido creado.