Setuptools¶
En Python todo el tema de empaquetar puede ser un poco lioso, ya que encontramos varios módulos desintados a ello. Nosotros vamos a centrarnos en setuptools, ya que es la forma más utilizada, nos proporciona todo lo necesario para distribuir nuestros propios módulos e incluso nos permite publicar paquetes en el respositorio público PyPI (Python Package Index) de forma directa desde la propia terminal.
Si lo recordáis, en la lección de módulos ya os enseñé como crear un distribuible con setuptools, a lo largo de esta lección vamos a repasar y aprender varios conceptos nuevos.
Paquete básico¶
Antes de comenzar es importante repasar la estructura de un paquete en Python,ya que para distribuir nuestro código es indispensable estructurarlo dentro de un paquete:
| setup.py # Fichero que contiene toda la información de instalación
+ prueba/ # Directorio del paquete al mismo nivel que setup.py
| __init__.py # Fichero que indica que el directorio es un paquete
| modulo.py # Módulo o script que contiene definiciones
Vamos a aprender un poco más sobre el fichero de instalación.
El fichero de configuración incluye toda la información necesaria para realizar la instalación de nuestro paquete. Algunos campos incluyen sólo metadatos como el nombre, la versión, la descripción o el autor. Pero otros sirven para extender la instalación.
Como sería un caos que cada desarrollador pusiera los campos que quisiera, hay una serie de parámetros comunes y avanzados, pero como son muchos lo más común es utilizar una plantilla base como la siguiente que pasa la configuración a la función setup:
from setuptools import setup
setup(name="Prueba", # Nombre
version="0.1", # Versión de desarrollo
description="Paquete de prueba", # Descripción del funcionamiento
author="Hector Costa", # Nombre del autor
author_email='me@hcosta.info', # Email del autor
license="GPL", # Licencia: MIT, GPL, GPL 2.0...
url="http://ejemplo.com", # Página oficial (si la hay)
packages=['prueba'],
)
¿Hasta aquí fácil no? Son simples metadatos para definir el paquete, con la excepción de packages, en el que tenemos que indicar todos los paquetes que formarán parte del paquete distribuido en forma de lista.
Aunque en este caso únicamente tendríamos al paquete prueba, imaginaros que tenemos docenas de subpaquetes y tubiéramos que añadirlos uno a uno... Pues para estos casos podemos importar una función que se encargará de buscar automáticamente los subpaquetes, se trata de find_packages y la podemos encontrar dentro de setuptools:
from setuptools import setup, find_packages
setup(...
packages=find_packages()
)
Dependencias¶
Ahora imaginaros que en vuestro paquete algún código utiliza funciones de un módulo externo o paquete que hay que instalar manualmente. Esto se conoce como dependencias del paquete, y por suerte podemos indicar a un parámetro que descargue todos los paquetes en la versión que nosotros indiquemos, se trata de install_requires.
Por ejemplo imaginad que dentro de nuestro paquete necesitamos utilizar el módulo Pillow para manejar imágenes. Por regla general podemos instalarlo desde la terminal con el comando:
pip install pillow
setup(...,
install_requires=["pillow"],
)
Lo bueno que tiene es que podemos indicar la versión exacta que queremos instalar, por ejemplo. Si mi programa utilizase la versión 1.1.0 de Pillow tendría que poner:
setup(...,
install_requires=["pillow==1.1.0"],
)
En cambio si fuera compatible con cualquier versión a partir de la 1.1.5 podría poner:
setup(...,
install_requires=["pillow>=1.1.5"],
)
De forma similar a antes, quizá llega el momento donde tenemos muchísimas dependencias y es un engorro tener que cambiar directamente el fichero setup.py. Para solucionarlo podemos utilizar una técnica que se basa en crear un fichero de texto y escribir las dependencias, una por línea.
Luego podemos abrir el fichero y añadir las dependencias automáticamente en forma de lista. Generalmente a este fichero se le llama requirements.txt y debe estar en el mismo directorio que setup.py:
pillow==1.1.0
django>=1.10.0,<=1.10.3
pygame
Luego en las dependencias indicaríamos lo siguiente:
setup(...,
install_requires=[i.strip() for i in open("requirements.txt").readlines()],
)
Suite Test¶
Otra cosa interesante que podemos hacer es adjuntar una suite de tests unitarios para nuestro paquete, ya sabéis, los que aprendimos en la unidad anterior.
Para incluirlos tendremos indicar un parámetro en el instalador llamado test_suite, al que le pasaremos el nombre del directorio que los contiene, por lo general llamado tests:
| setup.py
| requeriments.txt
+ prueba/
| __init__.py
| modulo.py
+ tests/
| __init__.py
| test_pillow.py
| test_django.py
| test_pygame.py
setup(...,
test_suite="tests"
)
python setup.py test
PyPI y PIP¶
Por último hablemos un poco más del Python Package Index.
Como ya sabéis se trata de un repositorio público con miles y miles de paquetes creados por la enorme comunidad de Python.
La forma de instalar cómodamente los paquetes de PyPI es con la herramienta PIP (un acrónimo recursivo de Pip Installs Packages), utilizando el comando pip install nombre_paquete.
Además podemos listar los paquetes instalados con pip list, borrar alguno con pip uninstall nombre_paquete o incluso instalar todas las dependencias de un fichero requisites.txt utilizando pip install requisites.txt.
Si queréis saber más sobre pip, simplemente escribid pip en la terminal.
Clasificadores¶
Por lo tanto tenemos un repositorio inmenso, así que ¿cómo podemos añadir información para categorizar nuestro paquete en PyPI? Pues utilizando un parámetro llamado classifiers de la siguiente forma:
setup(...,
classifiers=[
"Development Status :: 3 - Alpha",
"Topic :: Utilities",
"License :: OSI Approved :: GNU General Public License (GPL)",
],
)
Probando el paquete¶
Una vez tenemos toda la información configurada, podemos probar nuestro paquete fácilmente realizando una instalación en modo desarrollo. Para ello utilizaríamos el siguiente comando:
python setup.py develop
Una vez hayamos hecho las probaturas y estemos satisfechos, podemos desinstalar el paquete de desarrollo:
python setup.py develop --uninstall
Para instalar el paquete definitivo utilizaríamos:
python setup.py install
Distribuyendo el paquete¶
Ya tenemos el paquete, hemos creado el instalador, lo hemos probado y estamos preparados para distribuirlo. Hay dos formas:
- Localmente: Generando un fichero comprimido que podemos compartir con nuestros conocidos.
- Públicamente: En el repositorio PyPI para que todo el mundo pueda utilizarlo.
Evidentemente si distribuimos localmente no tenemos que tener mucho cuidado, y además podemos hacer pruebas. Pero si decidimos hacerlo públicamente tendremos que intentar que el paquete tenga un mínimo de calidad.
Localmente¶
Distribuir el paquete localmente es muy fáci. Simplemente tenemos que utilizar el comando:
python setup.py sdist
Este fichero ya podremos compartirlo con quien queramos, y para instalarlo sólo tendremos que utilizar la herramienta pip:
pip install nombre_del_fichero.zip
Luego para desinstalarlo de la misma forma pero utilizando el nombre del paquete:
pip uninstall nombre_paquete
Públicamente¶
Aunque no voy a hacer la demostración porque ahora mismo no dispongo de un paquete para publicar en el repositorio de PyPI, sí que os voy a enseñar los pasos a seguir para hacerlo. Lo bueno de registrar un paquete en PyPI es que podemos instalarlo desde cualquier lugar a través de internet utilizando la herramienta PIP.
Dicho ésto, si algún día creáis un paquete de calidad y queréis compartirlo con la comunidad, lo primero es registrar una cuenta en PyPI.
A continuación desde el directorio de nuestro paquete tenemos que ejecutar el comando:
python setup.py register
Una vez hecho esto ya hemos creado nuestro paquete, pero todavía no hemos publicado una versión, así que vamos a hacerlo utilizando el comando:
python setup.py sdist upload
pip install nombre_paquete
Última edición: 7 de Octubre de 2018