domingo, 20 de febrero de 2022

Los Paquetes en Python.

imagen de tres paquetes de regalo


Crear, Empaquetar y Distribuir los paquetes en Python.

Podemos decir que un paquete en Python es una carpeta que contiene varios módulos que podemos utilizar en nuestros programas. Para que Python sepa que se trata de un paquete y no de una carpeta normal, basta en principio, con poner dentro un archivo llamado __init__.py sin contenido. 

La estructura tipo de un paquete es:

paquete/
    |-- __init__.py
     |-- modulo_1.py
     |-- modulo_2.py

Los paquetes se parecen a las antiguas cadenas de música que se formaban añadiéndole módulos. (cassettes, amplificador, plato, cd etc). Vamos a imaginar que tenemos un programa (paquete) de sonido y que ese paquete a su vez tiene dos módulos. Uno que sirve para reproducir diferentes formatos de música y otro que aplica diversos efectos a  los sonidos. 

La forma de acceder a el contenido de esos paquetes es siguiendo la estructura:

<paquete>.<subdirectorio>.<módulo>

En el ejemplo que planteábamos puede tener la siguiente estructura:

sound/
    |-- __init__.py
     |-- formatos/
            __init__.py
            wadread.py
            mp3read.py
     |-- efectos/
            __init__.py
            soundround.py
            reverse.py
            autotune

De esta forma si queremos utilizar alguno de los módulos que lo componen haríamos:

# carga el modulo reverse para utilizar todo lo que lo compone
import sound.efectos.reverse

O bien también podemos usar:

# carga el modulo reverse para utilizar todo lo que lo compone
from sound.efectos import reverse

Python incluye muchos módulos y paquetes en su instalación que es lo que se conoce como librería estándar. También aún más grande, son los paquetes que podemos instalar y que han sido desarrollados por la comunidad para hacer miles de cosas.

Aunque el archivo __init__.py basta con que este vacío para que Python entienda que esa carpeta se trata de un paquete, se pueden poner instrucciones dentro de ese archivo. Por ejemplo si en __init__.py el que está debajo de la carpeta efectos/ ponemos la variable __all__ así:

__all__= ['sounround', 'reverse'] 
Si realizamos la importación de todos los módulos usando el *, pej. from sound.efectos import * solo nos cargará los módulos especificados en la variable y no el modulo autotune que también forma parte del mismo.

Empaquetar paquetes para que estén disponibles en cualquier parte.


Si queremos que un paquete que hemos creado este disponible para poder utilizarlo en otro equipo, o para que se pueda instalar directamente en el equipo al igual que los paquetes que forman parte de la biblioteca estándar, lo primero que tenemos que hacer es empaquetarlo. 

Para ello, aunque existen varios módulos que se pueden utilizar, vamos a centrarnos en uno llamado Setuptools. Este nos va a facilitar todo lo que vamos a necesitar para distribuir nuestros propios módulos e incluso publicar nuestros paquetes en el repositorio público PyPi (Python Package Index) directamente desde el terminal. 

Para distribuir nuestro código es imprescindible que lo tengamos en forma de paquete. Su estructura básica sería:

| setup.py # Fichero que contiene toda la información de la instalación + mates/ # 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 el código principal.

 Lo que estamos diciendo es que vamos a empaquetar el paquete al que hemos llamado "mates" cuyo código esta recogido en fichero-modulo.py

Empecemos viendo un poco el fichero setup.py. Este archivo contiene toda la información que el programa necesita para realizar la instalación del paquete. Una parte del mismo son metadatos que identifican el quien y que del programa, mientras que la otra es la que se utiliza para preparar el proceso de instalación propiamente dicho.

De forma estándar se suelen usar una serie de campos ya definidos para estructurar el paquete. Creamos el archivo setup.py con la siguiente estructura:

from setuptools import setup setup(
    name = "Mates", # Nombre version = "0.1", # Versión de desarrollo description = "Paquete de matemáticas", # Descripción del funcionamiento author = "Nombre_del_autor", # Nombre del autor author_email = 'correo@delautor', # Email del autor license = "GPL", # Licencia: MIT, GPL, GPL 2.0... url = "http://ejemplo.com", # Página oficial (si la hay) packages = ['prueba'], )


De aquí lo más importante es el apartado packages, donde se va a recoger todos los paquetes que vamos a empaquetar. En el ejemplo previo solo tenemos un paquete, mates. Pero eso no implica que no pudiésemos tener cientos de ellos en cuyo caso o bien los vamos añadiendo uno a uno (con el trabajo que ello implica o bien) o bien utilizamos (después de importarla) la siguiente función que hará el trabajo por nosotros:

from setuptools import setup, find_packages setup(# aqui irían todos los metadatos, packages = find_packages(),
    )
Otra cosa importante son las dependencias de las que dependa el buen funcionamiento de nuestro paquete. Por ejemplo si nuestro paquete necesitará el paquete externo matplotlib (para mostrar algún tipo de gráfico) para funcionar, a la hora de empaquetarlo hay que tener en cuenta todos los paquetes de los que depende el nuestro y que va a necesitar para su buen funcionamiento.

En su momento lo instalamos desde el terminal antes de importarlo con:

pip3 install matplotlib
Pero como lo que queremos al distribuir nuestro paquete es que este se instale de forma automática, lo haremos con la siguiente instrucción:

setup(...,
      install_requires=["matplotlib"],
)
Como no hemos indicado ninguna versión, se nos instalará la más actual que haya en ese momento. Pero por compatibilidad quizá queramos que se instale la librería exacta que hayamos utilizado. Esto se puede hacer fácilmente, solo hay que variar un poco el comando:

setup(...,
      install_requires=["matplotlib==3.5.0"],
)
Hay que decir que cuando tienes un programa completamente elaborado normalmente has usado muchas librerías que a su vez requieren de otras. Para saber exactamente las librerías que has usado en tu proyecto, puedes listarlas desde el terminal usando alguna de estas dos expresiones:

pip3 list
pip3 freeze

Se suele utilizar más la segunda porque podemos pasar esa lista de todas las librerías que hemos utilizado a un archivo:

pip3 freeze > requirement
y luego incorporarlas a setup.py para indicar las dependencias de la siguiente forma:

setup(...,
      install_requires=[i.strip() for i in open("requirements.txt").readlines()],
)

Probando el paquete en modo de desarrollo.


Una vez que tenemos el paquete preparado, si queremos probar que funciona correctamente en modo de desarrollo, lo mejor es ejecutarlo en un entorno local, en nuestro usuario. Para ello empezaremos creando una carpeta y dentro crearemos un entorno virtual

> python3 -m venv miEntorno

Luego pondremos dentro, la carpeta que contiene el paquete y el archivo setup.py. Quedaría algo como esto:


Desde el terminal activamos el entorno virtual:

 > source miEntorno/bin/activate
y una vez ahí, tecleamos el siguiente comando:

(miEntorno)$ python -m pip install -e .
Se intentará desinstalar la versión anterior si existe y se instalará la nueva versión de nuestro paquete, que es de momento la única que tenemos.

Si hacemos dentro del entorno virtual un pip list, veremos algo así:


Si finalmente quisiéramos instalarlo definitivamente en nuestro equipo entonces utilizaríamos, ya fuera del entorno virtual:

 > python3 setup.py install
Pero tenemos que tener en cuenta que una vez hecho esto, el paquete se instala internamente en el ordenador y ya no podremos modificarlo sin antes desinstalarlo, lo cual tenemos que hacer con el comando pip3, buscando el nombre del paquete con pip3 list y desinstalarlo con:

 > pip3 uninstall <nombre del paquete>

¿Y que pasa si queremos pasar el paquete a un amigo es decir DISTRIBUIRLO?

Una vez que ya lo tenemos todo listo preparar un paquete para compartir con un amigo, por ejemplo, es muy sencillo.  Solamente tenemos que ejecutar desde el terminal es siguiente comando:

python3 setup.py sdist
Este comando nos creará un fichero comprimido que podremos compartir con quien queramos. Se va a generar un directorio llamado dist/ en la carpeta del paquete y si miramos dentro, estará el fichero que necesitamos.

Este fichero ya podremos compartirlo con quien queramos y para instalarlo lo trataremos igual que cualquier otro paquete:

pip install nombre_del_fichero_comprimido 
Lo podemos también desinstalar de la misma forma pero usando el nombre del paquete:

pip uninstall nombre_paquete
Y aquí finalizamos este capitulo. Dejamos para uno próximo como subir paquetes al repositorio de PyPi aunque si quieres ver material sobre el tema en español puedes encontrarlo aquí.

No hay comentarios:

Publicar un comentario