Empaquetado de proyectos Python para Debian / Raspbian con dh-virtualenv

Este artículo pretende explicar algunas cosas a los desarrolladores que no usan mucho Python, y que podrían tener problemas con algunos de los conceptos de otra manera.

Recomiendo encarecidamente la lectura del siguiente artículo como introducción a los conceptos tratados aquí:

pypi.org

pypi.org es un repositorio oficial de software para el lenguaje de programación Python. Incluye las bibliotecas que queremos utilizar en nuestros proyectos y que no se entregan por defecto con Python.

El problema

Nuestro objetivo es empaquetar una aplicación escrita en Python (dirigida a Python3) como un paquete .deb para Raspbian (que es, en definitiva, equivalente a Debian).

Nuestra aplicación tiene dependencias de varias bibliotecas de Python.

Por ejemplo, nos gustaría utilizar el solicita biblioteca. La propia biblioteca de peticiones depende de otras bibliotecas, por ejemplo, chardet, urllib3, etc.

Gestionar manualmente estas dependencias y enviar manualmente el código de la biblioteca requerida es intrínsecamente poco práctico.

pip

Python tiene su propio sistema de gestión de paquetes/dependencias, llamado pip

pip le permite instalar bibliotecas y aplicaciones de python, para que su propio código pueda utilizar estas bibliotecas.

Aquí hay dos problemas:

  • queremos enviar el paquete como Paquete Debianpara que los usuarios finales puedan instalarlo fácilmente. Podemos asumir con seguridad que en un número significativo de sistemas de destino las bibliotecas python apropiadas no habrán sido instaladas por el usuario
  • si intentamos instalar estas bibliotecas en todo el sistema en nombre del usuarioEn el caso de las librerías, es posible que se rompa la compatibilidad con otros paquetes (por ejemplo, debido a los cambios de la API entre las versiones de las librerías).

Por lo tanto, necesitamos una solución.

virtualenv

virtualenv nos permitirá crear entornos virtuales para Python.

En estos entornos virtuales tenemos:

  • nuestro propio intérprete de Python
  • copias de las bibliotecas estándar necesarias
  • nuestro propio pip
  • nuestra propia biblioteca "de todo el sistema"

Parte práctica: Probar virtualenv

Instale virtualenv (como parte de dh-virtualenv, al que llegaremos en un rato) haciendo:

sudo apt install dh-virtualenv

Como requests ya está instalado en todo el sistema en mi caja de pruebas, utilizaremos un paquete diferente como ejemplo. randstr le permitirá generar una cadena aleatoria.

Compruebe si randstr se instala en todo el sistema:

pip3 show randstr

Esto no debería devolver nada. Indica que este paquete no está instalado en todo el sistema:

imagen

Ahora crearemos un entorno virtual. La carpeta del entorno virtual NO tiene que vivir dentro de su carpeta de código, pero puede hacerlo. Deberías ignorarla en GIT.

digamos que tenemos un directorio /home/pi/tutorial

Cambia a este directorio, y crea una nueva carpeta de entorno env:

cd /home/pi/tutorial

que python3

virtualenv -p /usr/bin/python3 env

Nota: debes especificar el intérprete de Python si quieres Python 3, ya que de lo contrario se utilizará Python 2. Yo estoy usando Python 3. (Ejecutando virtualenv con el intérprete /usr/bin/python2)

que python3 le dirá dónde reside el binario de python3, ajuste la ruta para virtualenv en consecuencia.

imagen

activar el entorno:

source env/bin/activate

(Esta ruta de comando asume que usted se quedó en el directorio del tutorial). Tenga en cuenta también, que esto no es un script ejecutable - usted tiene que usar fuente para utilizarlo con su terminal bash.

Fíjese en que (env) se antepone al símbolo del sistema:

imagen

Esto significa que el entorno está activo. Ahora puede ejecutar pip3 de nuevo, pero esta vez pip3 realizará su trabajo en el entorno virtual:

pip3 show randstr

pip3 mostrar solicitudes

no debería mostrar nada. La segunda línea tampoco mostrará nada - mostrando que (si requests está instalado en todo el sistema) estamos efectivamente en un entorno virtual.

Ahora instala randstr:

pip3 install randstr

y compruebe de nuevo si está instalado:

pip3 show randstr

imagen

Esta vez muestra que randstr está instalado. Se ha instalado en env/lib/python3.5/site-packages:

imagen

Mientras el entorno está activado, vamos a escribir una pequeña aplicación de ejemplo en Python:

nano sample.py

pegar el siguiente código, generando una cadena aleatoria utilizando la nueva biblioteca que acabamos de instalar:

from randstr import randstr
print("hola mundo")
print(randstr())

y ahorrar.

Ejecuta el código usando python3:

python3 ejemplo.py

mientras el entorno esté activo, esto funcionará:

imagen

Ahora es el momento de salir del entorno, para ver si el código sigue funcionando:

desactivar

Su indicador volverá a la normalidad. Y

python3 ejemplo.py

arrojará un mensaje de error, sobre la inexistencia del módulo 'randstr' en su sistema. Este es exactamente el comportamiento que queremos.

imagen

Puedes echar un vistazo a la carpeta env, viendo cómo se envía todo lo necesario para ejecutar tu código Python, incluyendo las librerías, el ejecutable python, pip, y más.

Empaquetado de Debian virtualenv con dh-virtualenv

Bien, ahora con lo básico fuera del camino, lo que queremos es empaquetar un virtualenv junto con nuestro código, para que nuestra aplicación se ejecute de forma fiable y sin molestar a otras aplicaciones en los ordenadores de nuestros usuarios.

Aquí es donde dh-virtualenv entra en escena. dh-virtualenv es una adición a los scripts de construcción de Debian, que le permite empaquetar entornos virtuales.

Tenemos que crear una estructura de paquete básica para nuestro nuevo paquete. Aquí es donde cookiecutter ayudará.

cookiecutter

cookiecutter crea nuevos proyectos para usted a partir de plantillas, reduciendo así la sobrecarga y el tiempo que dedica al desarrollo y a la comprensión de los numerosos archivos necesarios.

https://cookiecutter.readthedocs.io/en/latest/

sudo apt install cookiecutter

Nota: el paquete python3-cookiecutter proporcionará un módulo de Python para Python 3. Lo que queremos es la aplicación en sí, que se instala con el paquete cookiecutter como se ve arriba. Esto podría tirar en Python 2, pero - oh bien.

cookiecutter no es necesario para sus usuarios finales, sólo se utiliza para crear varios archivos para su paquete.

crear un directorio de proyecto de ejemplo, en el que aplicaremos la plantilla (todavía estoy en el directorio del tutorial):

mkdir sampleproject

cd proyecto de muestra

Usando un molde especial para dh-virtualenv podemos configurar muchos de los archivos necesarios:

https://github.com/Springerle/dh-virtualenv-mold

cookiecutter https://github.com/Springerle/dh-virtualenv-mold.git

Esto le hará varias preguntas.

imagen

Tenga en cuenta que la carpeta debe llamarse debian, incluso cuando se empaqueta para Raspbian - por lo tanto, mantenga el nombre como debian.

Esto instalará los siguientes archivos:

imagen

Ahora necesitas ejecutar los siguientes comandos, según las instrucciones bajo el dh-virtualenv-mold:

sudo apt-get install build-essential debhelper devscripts equivs
sudo mk-build-deps --install debian/control
Para construir el paquete más tarde, ejecutará el siguiente comando desde el directorio de nivel superior de su proyecto:
dpkg-buildpackage -uc -us -b
Ahora mismo, sin embargo, el comando fallará porque falta setup.py. Llegaremos a setup.py y a requirements.txt en un momento:
/usr/bin/python3: no se puede abrir el archivo 'setup.py': [Errno 2] No such file or directory

Información sobre los archivos individuales

registro de cambios

Este archivo contendrá la información de la versión del paquete. Puede actualizarlo utilizando una herramienta especial, dch.

Este archivo no es un registro de cambios para su aplicación, sino un registro de cambios para el embalaje de su aplicación.

compat

Este archivo incluye un número mágico especial "9". (por cuestiones de compatibilidad, está bien dejarlo como está)

controlar

Este es el archivo principal para establecer la configuración de los paquetes, y las dependencias a nivel de Debian. Si su aplicación depende, por ejemplo, de que se instale el omxplayer, tendrá que ir aquí como dependencia:

Fuente: sampleproject
Sección: contrib/python
Prioridad: extra
Mantenedor: Maximilian Batz
Build-Depends: debhelper (>= 9), python, python-dev, dh-virtualenv (>= 0.10), tar
Versión de las normas: 3.9.5
Página web:
https://picockpit.com


Paquete: sampleproject
Arquitectura: cualquiera
Pre-Depends: dpkg (>= 1.16.1), python2.7 | python3, ${misc:Pre-Depends}
Depende: ${python:Depends}, ${misc:Depends}
Descripción: Un paquete de ejemplo para demostrar el empaquetado de virtualenv Debian con dh-virtualenv
     .
     Se trata de una distribución de "sampleproject" como un
     Python virtualenv envuelto en un paquete Debian (paquete "omnibus",
     todos los pasajeros a bordo). El virtualenv empaquetado se mantiene sincronizado con
     el intérprete del anfitrión automáticamente.
     .
     Ver
https://github.com/spotify/dh-virtualenv para más detalles.

También querrá editar la Descripción larga.

cookiecutter.json

Incluye su configuración inicial. No es necesario para el embalaje.

derechos de autor

Tu archivo de copyright, con tu licencia (por ejemplo, MIT). Está pre-rellenado con tu nombre, año, e-Mail y "algunos derechos reservados".

reglas

Este es un Makefile, para construir realmente su paquete. Está precargado por la plantilla cookiecutter.

ejemplo de proyecto.enlaces

Este archivo permite crear enlaces durante la instalación. El nombre del archivo incluirá el nombre de su proyecto en lugar de ejemplo de proyecto.

Ref: https://stackoverflow.com/questions/9965717/debian-rules-file-make-a-symlink

sampleproject.postinst

Este archivo le permite ejecutar pasos de configuración adicionales después de la instalación (por ejemplo, la activación de su script de python como un servicio). El nombre del archivo incluirá el nombre de su proyecto en lugar de ejemplo de proyecto.

sampleproject.triggers

Por lo que entiendo esto es para que dh-virtualenv instale un intérprete de python más nuevo en el entorno virtual para enviar, si el del sistema (tu caja de desarrollo) se actualiza. El nombre del archivo incluirá el nombre de su proyecto en lugar de ejemplo de proyecto.

Esto podría ser así o no, no lo he probado.

setup.py

Como se ha mencionado anteriormente, necesitamos un setup.py archivo. Esta es la forma estándar en que se empaquetan las aplicaciones en Python (se envían con un setup.py, que se ejecuta para varias tareas relacionadas con el empaquetado).

Un sencillo setup.py puede verse en esta página:

https://github.com/benjaminirving/python-debian-packaging-example/blob/master/setup.py

No todas las entradas de estos archivos son necesarias. Por ejemplo, puedes omitir los clasificadores. Tengo el siguiente setup.py:

imagen

Nota: el número de versión y otra información aquí dentro son para su paquete Python (que será creado en el curso de la construcción de un paquete Debian con su virtualenv).

Estructura de su código

Su código vivirá ahora en un subdirectorio del directorio principal de sampleproject:

imagen

En este caso, el subdirectorio tiene el mismo nombre que el directorio principal.

Observe el archivo __init__.py

El sample.py es el que usamos arriba, pero un poco retocado para que se vea así:

imagen

Además, configuré un entorno virtual para realizar pruebas mientras desarrollaba dentro del directorio principal (nivel superior) de sampleproject:

virtualenv -p /usr/bin/python3 sp_env

llamado sp_env para el entorno del proyecto de muestra

Entrar en el entorno:

source sp_env/bin/activate

Instale la biblioteca randstr en este entorno:

pip3 install randstr

y ahora podemos ejecutar el código:

python3 sampleproject/sample.py

imagen

OK. Salir del entorno (! importante !)e intente construir el paquete de nuevo:

desactivar

dpkg-buildpackage -us -uc

Por defecto (debido a la galleta), su paquete se creará para su instalación en /opt/venvs/:

Nuevo ejecutable de python en /home/pi/tutorial/sampleproject/debian/sampleproject/opt/venvs/sampleproject/bin/python3

Esto se puede cambiar en el debian/reglas archivo.

Ahora el paquete ha sido construido, y depositado fuera del directorio superior sampleproject (dentro del directorio tutorial):

imagen

Puedes instalar el archivo .deb usando dpkg:

sudo dpkg -i sampleproject_0.2.1+nmu1_armhf.deb

Vea los resultados de la instalación con

árbol /opt/venvs

Se han instalado 127 directorios y 945 archivos.

Ahora puede intentar ejecutar el proyecto de ejemplo utilizando

/opt/venvs/sampleproject/bin/sampleproject

requisitos / dependencias

La aplicación no se ejecuta como (quizás) se esperaba, sino que arroja un error acerca de que randstr no está presente. ¿Qué ocurre?

imagen

La razón es que dh-virtualenv no conoce el requisito que tenemos, de agrupar el paquete randstr durante el proceso de construcción. Esta es la última pieza del rompecabezas.

Los requisitos deben añadirse al setup.pycomo una forma de hacer saber a pip qué paquetes adicionales deben ser instalados en el entorno virtual que se está creando.

añada lo siguiente a setup.py:

install_requires=[

        'randstr'

]

El archivo completo tendrá entonces este aspecto:

imagen

En mis pruebas el requirements.txt colocado en el nivel superior hizo no influyen en el comportamiento de dh-virtualenv y los paquetes de Python realmente incluidos en el paquete de Debian. Si todavía lo quieres, aquí tienes cómo:

pip le ofrece una manera de poner las dependencias exactas que utilizó para desarrollar en el entorno virtual. Entra en el entorno virtual:

source sp_env/bin/activate

Y crear el archivo requirements.txt:

pip3 freeze > requirements.txt

Ahora puedes echar un vistazo a ese archivo:

cat requisitos.txt

imagen

Aunque este archivo parece no ser utilizado por dh-virtualenv (tal vez necesita ir en un subdirectorio diferente), es una buena referencia para usted, para ver qué paquetes debe poner como dependencias en la parte install_requires de setup.py.

Tenga en cuenta que el archivo sampleproject.links nos ofreció una manera conveniente de enlazar esto en la carpeta /usr/bin, por lo tanto, voy a eliminar el signo de comentario para la próxima construcción:

imagen

Con esto fuera del camino, es el momento de purgar el sampleproject, y construirlo de nuevo, y luego instalarlo. ¡No te olvides de desactivar primero!

desactivar

sudo apt purge sampleproject

dpkg-buildpackage -us -uc

cd ..

sudo dpkg -i sampleproject_0.2.1+nmu1_armhf.deb

Esta vez, el paquete debería ser accesible mediante sampleproject:

qué ejemplo de proyecto

ejemplo de proyecto

imagen

Ref: https://packaging.python.org/discussions/install-requires-vs-requirements/

Consejo adicional: cambiar el lugar de instalación

/opt/venvs no es un "lugar" muy Debian para poner un paquete.

Las especificaciones están aquí:

http://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html

https://unix.stackexchange.com/questions/10127/why-did-my-package-get-installed-to-opt

  • /usr/local es no para los paquetes
  • /opt no es para los paquetes de Debian propiamente dichos (es para el software de terceros instalado por el usuario)

http://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s06.html

Las aplicaciones pueden utilizar un único subdirectorio bajo /usr/lib. Si una aplicación utiliza un subdirectorio, todos los datos dependientes de la arquitectura utilizados exclusivamente por la aplicación deben colocarse dentro de ese subdirectorio.

https://wiki.debian.org/HowToPackageForDebian

https://www.debian.org/doc/debian-policy/ch-opersys.html#file-system-hierarchy

"El requisito del FHS de que los archivos estáticos específicos de la aplicación, independientes de la arquitectura, se ubiquen en /usr/share se relaja a una sugerencia. En particular, un subdirectorio de /usr/lib puede ser utilizado por un paquete (o una colección de paquetes) para contener una mezcla de archivos independientes y dependientes de la arquitectura. Sin embargo, cuando un directorio está compuesto en su totalidad por archivos independientes de la arquitectura, debe ubicarse en /usr/share."

La mejor ubicación para mí parece ser /usr/share

Tenemos que editar dos archivos:

en debian/rules DH_VIRTUALENV_INSTALL_ROOT debe cambiarse a /usr/share, así:

imagen

y debian/sampleproject.links deben ser cambiados:

imagen

Además, haremos una nueva entrada en el registro de cambios:

dch -i

imagen

simplemente editar el texto, para reflejar un nuevo número de versión, y una entrada para el registro de cambios.

A continuación, el proyecto puede volver a construirse:

dpkg-buildpackage -us -uc

Después de la instalación, puede comprobar que efectivamente se utiliza la nueva ubicación:

imagen

El paquete ocupará unos 16 MB en su sistema de destino, gracias al entorno virtual y a todo lo que viene con él. Probablemente se podría reducir con algunos parámetros, pero ese ejercicio se deja al lector.

¡Diviértase empaquetando!