Aprende a distribuir aplicaciones Python

Una vez terminemos con el desarrollo de nuestra nueva aplicación es conveniente empaquetarla de forma que sea sencillo para los usuarios instalarla, y para nosotros distribuirla.
En Python existen dos módulos principales para este cometido: distutils, que es parte de la librería estándar y era el método más utilizado hasta hace poco, y setuptools, que extiende la funcionalidad de distutils y es cada vez más popular.
En este capítulo veremos el funcionamiento de ambas herramientas, y terminaremos explicando cómo crear ejecutables .exe para Windows a partir de nuestro programa en Python.
DISTUTILS
Todo programa distribuido con distutils contiene un script llamado por convención setup.py, que se encarga de instalar la aplicación llamando a la función setup de distutils.core. Esta función tiene montones de argumentos, que controlan, entre otras cosas, cómo instalar la aplicación.
Destinados a describir la aplicación tenemos los siguientes argumentos:
  • name: El nombre del paquete.
  • version: El número de versión.
  • description: Una línea describiendo el paquete.
  • long_description: Descripción completa del paquete.
  • author: Nombre del autor de la aplicación.
  • author_email: Correo electrónico del autor.
  • maintainer: Nombre de la persona encargada de mantener el paquete, si difiere del autor.
  • maintainer_email: Correo de la persona encargada de mantener el paquete, si difiere del autor.
  • url: Web de la aplicación.
  • download_url: Url de la que descargar la aplicación.
  • license: Licencia de la aplicación
También tenemos argumentos que controlan los archivos y directorios que deben instalarse, como son packages, py_modules, scripts y ext_modules.
El parámetro scripts, que es una lista de cadenas, indica el nombre del módulo o módulos principales, es decir, los que ejecuta el usuario. Si nuestra aplicación consistiera, por ejemplo, en un solo script ejemplo.py, el código de setup.py podría tener un aspecto similar al siguiente:
1 from distutils.core import setup
2
3 setup(name=”Aplicacion de ejemplo”,
4  version=”0.1″,
5  description=”Ejemplo del funcionamiento de distutils”,
6  author=”Raul Gonzalez”,
7  author_email=”zootropo en gmail”,
8  url=”http://comoprogramar.org/tutorial-python/”,
9  license=”GPL”,
10  scripts=[“ejemplo.py”]
11 )
Si hemos escrito otros módulos para ser utilizados por el script principal, estos se indican mediante el parámetro py_modules. Por ejemplo, supongamos que la aplicación consiste en un script principal ejemplo.py, y un módulo de apoyo apoyo.py:
1 from distutils.core import setup
2
3 setup(name=”Aplicacion de ejemplo”,
4  version=”0.1″,
5  description=”Ejemplo del funcionamiento de distutils”,
6  author=”Raul Gonzalez”,
7  author_email=”zootropo en gmail”,
8  url=”http://comoprogramar.org/tutorial-python/”,
9  license=”GPL”,
10  scripts=[“ejemplo.py”],
11  py_modules=[“apoyo”]
12 )
Para instalar paquetes Python (directorios que contienen varios módulos y un archivo __init__.py) usaríamos el parámetro packages. Si además del módulo ejemplo.py quisiéramos instalar los paquetes gui y bbdd, por ejemplo, haríamos algo así:
1 from distutils.core import setup
2
3 setup(name=”Aplicacion de ejemplo”,
4  version=”0.1″,
5  description=”Ejemplo del funcionamiento de distutils”,
6  author=”Raul Gonzalez”,
7  author_email=”zootropo en gmail”,
8  url=”http://comoprogramar.org/tutorial-python/”,
9  license=”GPL”,
10  scripts=[“ejemplo.py”],
11  packages=[“gui”, “bbdd”]
12 )
ext_modules, por último, sirve para incluir extensiones que utilice el programa, en C, C++, Fortran, …
Veamos ahora cómo se utilizaría el archivo setup.py una vez creado.
Al ejecutar el comando
python setup.py install
los módulos y paquetes especificados por py_modules y packages se instalan en el directorio Lib de Python. Los programas indicados en scripts, se copian al directorio Scripts de Python.
Una vez hemos comprobado que la aplicación se instala correctamente, procedemos a crear archivos mediante los que distribuir la aplicación a los usuarios. Para crear archivos con el código fuente se utiliza la opción sdist de setup.py, que crea por defecto un archivo tar.gz en Unix y un zip en Windows.
python setup.py sdist
Sin embargo se puede utilizar –formats para especificar el formato o formatos que queramos generar
bztar .tar.bz2
gztar .tar.gz
tar .tar
zip .zip
ztar .tar.Z
Para crear un archivo tar.bz2, un tar.gz y un zip, por ejemplo, se utilizaría la siguiente orden:
python setup.py sdist –formats=bztar,gztar,zip
Para generar un archivo de distribución binaria, se usa la opción bdist:
python setup.py bdist
Los formatos que soporta bdist son los siguientes:
rpm RPM
gztar .tar.gz
bztar .tar.bz2
ztarp .tar.Z
tar .tar
wininst Instalador Windows
zip .zip
Para crear un archivo rpm y un instalador de Windows, por ejemplo, escribiríamos:
python setup.py bdist –formats=wininst,rpm
También es posible crear otros tipos de archivos de distribución utilizando scripts que extienden distutils, como es el caso de los paquetes deb mediante el script stdeb (http://stdeb.python-hosting.com/)
SETUPTOOLS
setuptools extediende distutils añadiendo una serie de funcionalidades muy interesantes: introduce un nuevo formato de archivo para distribución de aplicaciones Python llamado egg, se encarga de buscar todos los paquetes que deben instalarse y añadir las posibles dependencias, permite instalar paquetes de PyPI con un solo comando, etc.
Además, como setuptools se basa en distutils, un script de instalación básico utilizando setuptools es prácticamente igual a su equivalente con distutils. Tan sólo cambiaría la sentencia de importación.
1 from setuptools import setup
2
3 setup(name=”Aplicacion de ejemplo”,
4       version=”0.1″,
5       description=”Ejemplo del funcionamiento de distutils”,
6       author=”Raul Gonzalez”,
7       author_email=”zootropo en gmail”,
8       url=”http://comoprogramar.orgt/tutorial-python/”,
9       license=”GPL”,
10       scripts=[“ejemplo.py”],
11 )
El único inconveniente que podríamos encontrar al uso de setuptools es que no está incluido por defecto en Python 2.5, aunque es probable que esto cambie en próximas versiones debido a su gran uso. Pero los desarrolladores de setuptools han pensado en todo, e incluso esto no debería suponer ningún problema, ya que con un mínimo esfuerzo por nuestra parte podemos hacer que setuptools se descargue e instale automáticamente en la máquina del usuario si este no se encuentra ya en el sistema. Basta distribuir con nuestro paquete un pequeño módulo extra ez_setup.py que viene incluido por defecto con setuptools (http://peak.telecommunity.com/dist/ez_setup.py) y llamar a la función use_setuptools del módulo al inicio de setup.py:
1 from ez_setup import use_setuptools
2 use_setuptools()
3
4 from setuptools import setup
5
6 setup(name=”Aplicacion de ejemplo”,
7       version=”0.1″,
8       description=”Ejemplo del funcionamiento de distutils”,
9       author=”Raul Gonzalez”,
10       author_email=”zootropo en gmail”,
11       url=”http://comoprogramar.org/tutorial-python/”,
12       license=”GPL”,
13       scripts=[“ejemplo.py”],
14 )
Veamos ahora con más detenimiento algunos de los cambios y novedades que introduce setuptools.
Integración con PyPI
Al estilo de CPAN en Perl setuptools permite instalar de forma fácil y sencilla los paquetes pertenecientes a PyPI, el Índice de Paquetes Python (http://pypi.python.org/pypi), así como subir nuestros propios paquetes.
PyPI cuenta en el momento de escribir estas líneas con 4782 paquetes, por lo que poder instalar los paquetes de este repositorio con un simple comando supone una ayuda muy a tener en cuenta.
Instalar un paquete de PyPI es tan sencillo como pasar al comando easy_install el nombre del paquete a instalar
easy_install docutils
Searching for docutils
Reading http://pypi.python.org/simple/docutils/
Reading http://docutils.sourceforge.net/
Best match: docutils 0.5
Downloading http://prdownloads.sourceforge.net/docutils/docutils-0.5.tar.gz?download
Processing docutils-0.5.tar.gz
Running docutils-0.5/setup.py -q bdist_egg –dist-dir /tmp/easy_install-wUAyUZ/docutils-0.5/egg-dist-tmp-kWkkkv
“optparse” module already present; ignoring extras/optparse.py.
“textwrap” module already present; ignoring extras/textwrap.py.
zip_safe flag not set; analyzing archive contents…
docutils.writers.newlatex2e.__init__: module references __file__
docutils.writers.pep_html.__init__: module references __file__
docutils.writers.html4css1.__init__: module references __file__
docutils.writers.s5_html.__init__: module references __file__
docutils.parsers.rst.directives.misc: module references __file__
Adding docutils 0.5 to easy-install.pth file
Installing rst2pseudoxml.py script to /usr/bin
Installing rst2html.py script to /usr/bin
Installing rst2latex.py script to /usr/bin
Installing rst2s5.py script to /usr/bin
Installing rst2newlatex.py script to /usr/bin
Installing rstpep2html.py script to /usr/bin
Installing rst2xml.py script to /usr/bin
Installed /usr/lib/python2.5/site-packages/docutils-0.5-py2.5.egg
Processing dependencies for docutils
Finished processing dependencies for docutils
Poder subir nuestros paquetes a PyPI requiere de un proceso un poco más laborioso. Primero registramos los detalles de nuestra aplicación en PyPI mediante la opción register del script setup.py, el cuál nos preguntará por nuestro nombre de usuario, contraseña y correo electrónico si no tenemos cuenta en PyPI, o nombre de usuario y contraseña si nos registramos anteriormente:
python setup.py register
running register
running egg_info
creating Aplicacion_de_ejemplo.egg-info
writing Aplicacion_de_ejemplo.egg-info/PKG-INFO
writing top-level names to Aplicacion_de_ejemplo.egg-info/top_level.txt
writing dependency_links to Aplicacion_de_ejemplo.egg-info/dependency_links.txt
writing manifest file ‘Aplicacion_de_ejemplo.egg-info/SOURCES.txt’
reading manifest file ‘Aplicacion_de_ejemplo.egg-info/SOURCES.txt’
writing manifest file ‘Aplicacion_de_ejemplo.egg-info/SOURCES.txt’
We need to know who you are, so please choose either:
1. use your existing login,
2. register as a new user,
3. have the server generate a new password for you (and email it to you), or
4. quit
Your selection [default 1]: 1
Username: zootropo
Password:
Server response (200): OK
I can store your PyPI login so future submissions will be faster.
(the login will be stored in /home/zootropo/.pypirc)
Save your login (y/N)?y
Para crear y subir una distribución con el código fuente de nuestra aplicación se utiliza la opción sdist upload:
python setup.py sdist upload
También podríamos crear y subir un egg (un formato de archivo para distribuir aplicaciones Python que veremos en la próxima sección) utilizando la opción bdist_egg upload:
python setup.py bdist_egg upload
O combinar los tres pasos en un solo comando:
python setup.py register sdist bdist_egg upload
Una vez subido el paquete cualquier persona podría instalarlo en su sistema utilizando easy_install, de la misma forma que cualquier otro paquete de PyPI:
easy_install mi-paquete
Eggs
Los eggs (huevo en inglés) son archivos de extensión .egg mediante los que distribuir aplicaciones en Python. Serían algo así como el equivalente a los archivos .jar del mundo Java. Son multiplataforma, permiten manejar dependencias, y permiten instalar distintas versiones del mismo paquete.
La forma más sencilla de instalar aplicaciones distribuidas como archivos egg es mediante el comando easy_install, el cuál comentamos brevemente en el punto anterior al hablar sobre su uso para instalar paquetes de PyPI. Para instalar un archivo egg no tenemos más que pasarle el nombre del archivo al comando easy_install:
easy_install mi-aplicacion.egg
o bien podemos pasarle la URL de la que descargar el egg:
easy_install http://www.comoprogramar.org/mi-aplicacion.egg
Para construir nuestros propios eggs podemos utilizar el comando bdist_egg de setup.py, de forma similar a la manera en que construíamos paquetes RPM o instaladores para Windows con distutils:
python setup.py bdist_egg
Otros cambios destacables
Uno de los cambios más interesantes es la incorporación de un nuevo argumento para la función setup llamado install_requires, que consiste en una cadena o lista de cadenas que indica los paquetes de los que depende la aplicación. Si nuestra aplicación necesitara tener instalado el paquete apoyo para poder ejecutarse, por ejemplo, escribiríamos lo siguiente:
1 install_requires = [“apoyo”]
Y de esta forma, easy_install se encargaría de buscar e instalar el paquete si fuera necesario, bien en PyPI, o en cualquier otro repositorio indicado por el parámetro dependency_links.
Además podemos especificar que se necesita una versión concreta del paquete requerido, que sea mayor o menor que una cierta versión, o que no se trate de una versión determinada utilizando operadores relacionales (==, !=, <, <=, >, >=):
1 install_requires = [“apoyo >= 1.0 < 2.0”]
También existen argumentos similares para declarar paquetes que deben instalarse para poder ejecutar el script de instalación (setup_requires), para poder ejecutar las posibles pruebas incluídas con el paquete (tests_require) y para conseguir funcionalidades adicionales (extras_require, que consiste en este caso en un diccionario).
setuptools incluye también atajos útiles, como la función find_packages() que nos evita tener que listar todos y cada uno de los paquetes que utiliza nuestro script en el parámetro packages, como era el caso de distutils:
1 from ez_setup import use_setuptools
2 use_setuptools()
3
4 from setuptools import setup, find_packages
5
6 setup(name=”Aplicacion de ejemplo”,
7       version=”0.1″,
8       description=”Ejemplo del funcionamiento de distutils”,
9       author=”Raul Gonzalez”,
10       author_email=”zootropo en gmail”,
11       url=”http://comoprogramar.orgt/tutorial-python/”,
12       license=”GPL”,
13       scripts=[“ejemplo.py”],
14       packages = find_packages()
15 )
CREAR EJECUTABLES .EXE

Tanto en Mac OS como en la mayor parte de las distribuciones Linux el intérprete de Python está instalado por defecto, por lo que los usuarios de estos sistemas no tienen mayor complicación a la hora de instalar y ejecutar aplicaciones escritas en Python.

En el caso de Windows, esto no es así, por lo que sería interesante que los usuarios de este sistema operativo no tuvieran que instalar el intérprete de Python. También sería interesante que nuestro programa consistiera en un archivo .exe en lugar de uno o varios archivos .py, para simplificar las cosas.
Todo esto lo podemos lograr gracias a py2exe, una extensión para distutils que, como su nombre indica, permite crear ejecutables para Windows a partir de código Python, y que permite ejecutar estas aplicaciones sin necesidad de tener instalado el intérprete de Python en el sistema.
Py2exe funciona examinando nuestro código fuente en busca de los módulos y paquetes que utilizamos, compilándolos y construyendo un nuevo archivo que incluye estos archivos y un pequeño intérprete de Python integrado.
Para probar el funcionamiento de py2exe creemos un pequeño programa ejemplo.py
1 print “Soy un .exe”
y el archivo setup.py correspondiente. Los cambios que tenemos que realizar a setup.py son sencillos: importar py2exe, y utilizar los argumentos console y windows para indicar el nombre del script o scripts que queramos convertir en ejecutables de consola o ejecutables de interfaz gráfica, respectivamente.
1 from distutils.core import setup
2 import py2exe
3
4 setup(name=”Aplicacion de ejemplo”,
5 version=”0.1″,
6 description=”Ejemplo del funcionamiento de distutils”,
7 author=”Raul Gonzalez”,
8 author_email=”zootropo en gmail”,
9 url=”http://comoprogramar.orgt/tutorial-python/”,
10 license=”GPL”,
11 scripts=[“ejemplo.py”],
12 console=[“ejemplo.py”]
13 )
Para crear el ejecutable, utilizamos una nueva opción de línea de comandos para setup.py disponible tras importar el módulo y llamada, cómo no, py2exe:
python setup.py py2exe
Con esto py2exe generará un directorio build, con las librerías compiladas, y un directorio dist, con los archivos que conforman nuestra aplicación.
Entre los archivos que podemos encontrar en dist tendremos uno o varios ejecutables con el mismo nombre que los scripts indicados en console y windows, un archivo python*.dll, que es el intérprete de Python, y un archivo library.zip, que contiene varios archivos pyc que son los módulos que utiliza la aplicación compilados.
Si queremos reducir el número de archivos a distribuir, podemos utilizar la opción –bundle de py2exe para añadir a library.zip las dll y los pyd (–bundle 2) o las dll, los pyd y el intérprete (–bundle 1).
python setup.py py2exe –bundle 1
o bien podemos añadir un nuevo argumento options a la función setup que indique el valor a utilizar (opción bundle_files), de forma que no tengamos que añadir el flag –bundle cada vez que usemos el comando py2exe:
1 from distutils.core import setup
2 import py2exe
3
4 setup(name=”Aplicacion de ejemplo”,
5       version=”0.1″,
6       description=”Ejemplo del funcionamiento de distutils”,
7       author=”Raul Gonzalez”,
8       author_email=”zootropo en gmail”,
9       url=”http://comoprogramar.org/tutorial-python/”,
10       license=”GPL”,
11       scripts=[“ejemplo.py”],
12       console=[“ejemplo.py”],
13       options={“py2exe”: {“bundle_files”: 1}}
14 )
Por último podemos incluso prescindir de library.zip e incrustarlo en el ejecutable utilizando el argumento zipfile=None
1 from distutils.core import setup
2 import py2exe
3
4 setup(name=”Aplicacion de ejemplo”,
5       version=”0.1″,
6       description=”Ejemplo del funcionamiento de distutils”,
7       author=”Raul Gonzalez”,
8       author_email=”zootropo en gmail”,
9       url=”http://comoprogramar.org/tutorial-python/”,
10       license=”GPL”,
11       scripts=[“ejemplo.py”],
12       console=[“ejemplo.py”],
13       options={“py2exe”: {“bundle_files”: 1}},
14       zipfile=None

Fuente: enlace

Related posts