Paquete de aplicaciones para instalar en otras máquinas con Python

En mi última publicación de esta serie, mostré cómo escribir un script en Python que devolvía una lista de RPM-software instalado instalado en una máquina. La salida se ve así:

$ rpmqa_simple.py --limit 20
linux-firmware-20210818: 395,099,476
code-1.61.2: 303,882,220
brave-browser-1.31.87: 293,857,731
libreoffice-core-7.0.6.2: 287,370,064
thunderbird-91.1.0: 271,239,962
firefox-92.0: 266,349,777
glibc-all-langpacks-2.32: 227,552,812
mysql-workbench-community-8.0.23: 190,641,403
java-11-openjdk-headless-11.0.13.0.8: 179,469,639
iwl7260-firmware-25.30.13.0: 148,167,043
docker-ce-cli-20.10.10: 145,890,250
google-noto-sans-cjk-ttc-fonts-20190416: 136,611,853
containerd.io-1.4.11: 113,368,911
ansible-2.9.25: 101,106,247
docker-ce-20.10.10: 100,134,880
ibus-1.5.23: 90,840,441
llvm-libs-11.0.0: 87,593,600
gcc-10.3.1: 84,899,923
cldr-emoji-annotation-38: 80,832,870
kernel-core-5.14.12: 79,447,964

Ahora quiero empaquetar una aplicación para poder instalarla fácilmente, incluidas todas las dependencias, en otras máquinas. En este artículo mostraré cómo usar el herramientas de configuración paquete para hacerlo.

Eso es mucho para cubrir, por lo que se requiere un conocimiento básico de Python. Incluso si no sabe mucho, el código es fácil de seguir y el código maestro es pequeño.

Índice

Configura tu entorno

Mi artículo anterior explicaba cómo instalar el código y usar entornos virtuales, pero aquí hay un atajo:

$ sudo dnf install -y python3-rpm
$ git clone [email protected]:josevnz/tutorials.git
$ cd rpm_query
$ python3 -m venv --system-site-packages ~/virtualenv/rpm_query
. ~/virtualenv/rpm_query/bin/activate
(rpm_query)$ 

Empaquetar e instalar la distribución.

El siguiente paso es empaquetar la aplicación. Sin embargo, no usaré RPM para esto.

¿Por qué no usar un RPM para empaquetar la aplicación de Python?

Bueno, no hay una respuesta corta para eso.

RPM es Estupendo si desea compartir su aplicación con todo usuarios de su sistema, especialmente porque RPM puede instalar automáticamente las dependencias asociadas.

Por ejemplo, los enlaces RPM de Python (rpm-python3) se distribuyen de esta manera, lo que tiene sentido ya que está vagamente ligado a RPM.

En algunos casos, esto también es una desventaja:

  • Los usuarios necesitan un alto acceso de raíz para instalar un RPM. Si el código es malicioso, tomará el control del servidor muy fácilmente. (Es por eso que siempre verifica las firmas RPM y descarga el código de fuentes conocidas, ¿verdad?)
  • Falla el intento de actualizar a un RPM incompatible con aplicaciones dependientes más antiguas.
  • RPM no es adecuado para compartir código de "prueba" creado durante la integración continua, al menos en implementaciones completas. Con un contenedor, esa es probablemente una historia diferente.
  • Si el código de Python tiene dependencias, es probable que deban empaquetarse como RPM.

Utilice entornos virtuales y pip + herramientas de configuración

El uso de entornos virtuales, pip y herramientas de configuración resuelve estas limitaciones de RPM porque:

  • Entornos virtuales permitir la instalación de aplicaciones sin permisos elevados.
  • La aplicación es autónoma en el entorno virtual. Los administradores pueden instalar diferentes versiones de las bibliotecas sin afectar todo el sistema.
  • Es muy fácil integrar un entorno virtual con integración continua y pruebas unitarias. Una vez que las pruebas son exitosas, el medio ambiente se puede reciclar.
  • herramientas de configuración resuelve el problema de empaquetar su aplicación en una buena estructura de directorios para que los scripts y las bibliotecas estén disponibles para los usuarios.
  • setuptools también maneja el seguimiento de dependencias mediante comprobaciones de versión adecuadas para que el proceso de compilación sea repetible.
  • las herramientas de configuración funcionan con semilla, el administrador de paquetes de Python.
  • Los entornos virtuales y las herramientas de configuración tienen un excelente soporte en IDE como PyCharm y Código VS.

[ Decrease the complexity of getting into the cloud by downloading the Hybrid cloud strategy for dummies eBook. ]

Trabajar con herramientas de configuración

Una vez que esté listo para implementar la aplicación, puede empaquetarla, copiar su archivo de rueda y luego instalarla en un nuevo entorno virtual. Primero, defina un archivo muy importante, setup.py, que es utilizado por herramientas de configuración.

Las secciones más importantes del archivo a continuación son:

  • * _requiere secciones: Dependencias de construcción e instalación
  • paquetes: La ubicación de las clases de Python
  • guión: Scripts que el usuario final llama para interactuar con las bibliotecas (si corresponde)
"""
Project packaging and deployment
More details: https://setuptools.pypa.io/en/latest/userguide/quickstart.html
"""
import os
from setuptools import setup
from reporter import __version__

def __read__(file_name):
    return open(os.path.join(os.path.dirname(__file__), file_name)).read()

setup(
    name="rpm_query",
    version=__version__,
    author="Jose Vicente Nunez Zuleta",
    author_email="[email protected]",
    description=__doc__,
    license="Apache",
    keywords="rpm query",
    url="https://github.com/josevnz/rpm_query",
    packages=[
        'reporter'
    ],
    long_description=__read__('README.md'),
    # https://pypi.org/pypi?%3Aaction=list_classifiers
    classifiers=[
        "Development Status :: 3 - Alpha",
        "Topic :: Utilities",
        "Environment :: X11 Applications",
        "Intended Audience :: System Administrators"
        "License :: OSI Approved :: Apache Software License"
    ],
    setup_requires=[
        "setuptools==49.1.3",
        "wheel==0.37.0",
        "rich==9.5.1",
        "dearpygui==1.0.2"
    ],
    install_requires=[
        "rich==9.5.1",
    ],
    scripts=[
        "bin/rpmq_simple.py",
    ]
)

Anotar :

  • He almacenado la versión en el reporter/__init__.py módulo de paquete para ser compartido con otras partes de la aplicación, no solo con las herramientas de configuración. También usé un esquema de versión semántica convenio de denominación.
  • El archivo README del módulo también se almacena como un archivo externo. Esto hace que editar el archivo sea mucho más fácil sin preocuparse por el tamaño o romper la sintaxis de Python.
  • Clasificadores que sea más fácil ver la intención de la aplicación.
  • Puedo definir dependencias de empaquetado (setup_requires) y dependencias de tiempo de ejecución (install_requires).
  • Tengo necesidad de wheel paquete para crear un precompilado distribución más rápido de instalar que otros modos.

el pyproject.toml El archivo también es muy importante. Aquí está el archivo:

[build-system]
requires = ["setuptools", "wheel"]

el pyproject.toml El archivo especifica qué se usa para empaquetar scripts e instalar desde fuentes.

Comprobación rápida antes de la descarga

Antes de descargar la rueda, debe pedirle a Twine que verifique su configuración en busca de errores, como este:

(rpm_query) [[email protected] rpm_query]$ twine check dist/rpm_query-0.0.1-py3-none-any.whl 
Checking dist/rpm_query-0.0.1-py3-none-any.whl: FAILED
  `long_description` has syntax errors in markup and would not be rendered on PyPI.
    line 20: Warning: Bullet list ends without a blank line; unexpected unindent.
  warning: `long_description_content_type` missing. defaulting to `text/x-rst`.

Markdown es incorrecto en el archivo. Una forma de solucionar este problema es instalar:

$ pip install readme_renderer[md]

también long_description_content_type la sección está ahí:

    long_description_content_type="text/markdown",
    long_description=__read__('README.md'),

Si lo vuelve a iniciar después de realizar los cambios anteriores, siempre ve la advertencia:

rpm_query) [[email protected] rpm_query]$ twine check dist/rpm_query-0.0.1-py3-none-any.whl 
Checking dist/rpm_query-0.0.1-py3-none-any.whl: PASSED, with warnings
  warning: `long_description_content_type` missing. defaulting to `text/x-rst`.

Sin errores graves y una falsa alarma. Estás listo para descargar tu volante.

Cómo implementar durante las pruebas

No necesita empaquetar e implementar la aplicación en modo completo. Esto se debe a que setuptools tiene una función muy conveniente desarrollar modo que instala las dependencias y le permite editar el código mientras lo prueba:

(rpm_query)$ python setup.py develop

Este modo crea enlaces simbólicos especiales que ponen scripts (recuerde que scripts: sección interior setup.py?) en su camino.

Por cierto, una vez hechas las pruebas, puedes quitar el modo de desarrollo:

(rpm_query)$ python setup.py develop --uninstall

La documentación oficial recomienda migrar desde setup.py configuración en setup.cfg. decidí usar setup.py porque sigue siendo el formato más popular.

Crear una distribución precompilada

Compilar es tan fácil como escribir esto:

(rpm_query)$ python setup.py bdist_wheel
running bdist_wheel
...  # Omitted output
(rpm_query)$ ls dist/
rpm_query-0.0.1-py3-none-any.whl

Luego puede instalarlo en la misma máquina o en una nueva máquina virtual:

(rpm_query)$ python setup.py install 
dist/rpm_query-1.0.0-py3-none-any.whl

O con el nuevo método preferido, usando build. Primero asegúrese de que el módulo esté instalado:

(rpm_query) $ pip install build
Collecting build
  Downloading build-0.7.0-py3-none-any.whl (16 kB)
Collecting tomli>=1.0.0
  Downloading tomli-1.2.2-py3-none-any.whl (12 kB)
Requirement already satisfied: packaging>=19.0 in /usr/lib/python3.9/site-packages (from build) (20.4)
Collecting pep517>=0.9.1
  Downloading pep517-0.12.0-py2.py3-none-any.whl (19 kB)
Requirement already satisfied: pyparsing>=2.0.2 in /usr/lib/python3.9/site-packages (from packaging>=19.0->build) (2.4.7)
Requirement already satisfied: six in /usr/lib/python3.9/site-packages (from packaging>=19.0->build) (1.15.0)
Installing collected packages: tomli, pep517, build
Successfully installed build-0.7.0 pep517-0.12.0 tomli-1.2.2

Y luego puede empaquetar su módulo de esta manera (tenga en cuenta que no decimos build para usar un entorno virtual porque ya estamos en a):

(rpm_query) $ python3 -m build --no-isolation
* Getting dependencies for wheel...
* Building wheel...
running bdist_wheel
running build
running build_scripts
# ... Omitted output 
whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'rpm_query-0.1.0.data/scripts/rpmq_dearpygui.py'
adding 'rpm_query-0.1.0.data/scripts/rpmq_rich.py'
adding 'rpm_query-0.1.0.data/scripts/rpmq_simple.py'
adding 'rpm_query-0.1.0.data/scripts/rpmq_tkinter.py'
adding 'rpm_query-0.1.0.dist-info/LICENSE.txt'
adding 'rpm_query-0.1.0.dist-info/METADATA'
adding 'rpm_query-0.1.0.dist-info/WHEEL'
adding 'rpm_query-0.1.0.dist-info/top_level.txt'
adding 'rpm_query-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built rpm_query-0.1.0-py3-none-any.whl

¿Qué sucede si desea compartir el archivo .pip con otros usuarios? Podría copiar el archivo de la rueda en las diferentes máquinas y pedirles a los usuarios que lo instalen, pero hay una mejor manera.

Configurar un servidor PyPI privado

Notar: esta configuración no es la calidad de producción porque:

  • No es seguro porque utiliza contraseñas en lugar de tokens para la autenticación.
  • No hay encriptación SSL. HTTP envía contraseñas en texto claro sobre el hilo.
  • No hay redundancia de almacenamiento. Idealmente, el almacenamiento de pip debería tener algún tipo de redundancia y copias de seguridad.

Puede hacer mucho más que simplemente instalar desde un archivo de rueda. Por ejemplo, puede configurar un servidor privado compatible con PyPI utilizando un contenedor en ejecución pypiserver.

[ Download the datasheet for information about Red Hat OpenShift Container Platform. ]

Primero, cree un directorio para almacenar los paquetes:

$ mkdir -p -v $HOME/pypiserver
$ mkdir: created directory '/home/josevnz/pypiserver'

Entonces usa htcontraseña para configurar un usuario y una contraseña para descargar los paquetes:

htpasswd -c $HOME/.htpasswd josevnz
New password: 
Re-type new password: 
Adding password for user josevnz

Después de eso, ejecute el contenedor en modo separado:

$ docker run --detach --name privatepypiserver --publish 8080:8080 --volume ~/.htpasswd:/data/.htpasswd --volume $HOME/pypiserver:/data/packages pypiserver/pypiserver:latest -P .htpasswd --overwrite packages
f95f59a882b639db4509081de19a670fa8fdd93c63c3d4562c89e49e70bf6ee5
$ docker ps
CONTAINER ID   IMAGE                          COMMAND                  CREATED         STATUS         PORTS                                       NAMES
f95f59a882b6   pypiserver/pypiserver:latest   "/entrypoint.sh -P .…"   7 seconds ago   Up 6 seconds   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   privatepypiserver

Confirme que se está ejecutando apuntando curl o lynx al nuevo privatepypiserver:

[[email protected] ~]$ lynx http://localhost:8080                                                                                                                                            Welcome to pypiserver!
                                                                         Welcome to pypiserver!

   This is a PyPI compatible package index serving 0 packages.

   To use this server with pip, run the following command:
        pip install --index-url http://localhost:8080/simple/ PACKAGE [PACKAGE2...]

   To use this server with easy_install, run the following command:
        easy_install --index-url http://localhost:8080/simple/ PACKAGE [PACKAGE2...]

   The complete list of all packages can be found here or via the simple index.

   This instance is running version 1.4.2 of the pypiserver software.

Luego cargue la rueda en su servidor PyPI privado.

Descarga la aplicación en un repositorio con Twine

La forma más común de compartir el código de Python es subirlo a un administrador de artefactos como Sonatype Nexus o pypiserver usando una herramienta como Cuerda.

(rpm_query)$ pip install twine

El siguiente paso es configurar ~ / .pypirc para permitir descargas sin contraseña en el servidor PyPI local:

[distutils]
index-servers =
    pypi
    privatepypi

[pypi]
repository = https://upload.pypi.org/legacy/

[privatepypi]
repository = http://localhost:8080/
username = josevnz

Es mejor no poner el password = XXXX dentro del archivo. Deje que Twine lo solicite en su lugar. Además, haga que la configuración sea accesible solo para el propietario:

$ chmod 600 ~/.pypirc

Finalmente, descargue la rueda usando el twine pedido:

(rpm_query) twine upload -r privatepypi dist/rpm_query-0.0.1-py3-none-any.whl 
Uploading distributions to http://localhost:8080/
Uploading rpm_query-0.0.1-py3-none-any.whl
100%|██████████████████████████████████

Confirme que se instaló con lynx http://localhost:8080/packages/:

                                                                           Index of packages

   rpm_query-0.0.1-py3-none-any.whl

Commands: Use arrow keys to move, '?' for help, 'q' to quit, '<-' to go back.
  Arrow keys: Up and Down to move.  Right to follow a link; Left to go back.
 H)elp O)ptions P)rint G)o M)ain screen Q)uit /=search [delete]=history list

Instalar desde el servidor local privado de pypi

Espera, no te vayas todavía. Es hora de instalar el paquete desde su servidor PyPI privado:

Ante todo, di pip para buscar paquetes en el servidor privado PyPI:

$ mkdir --verbose --parents ~/.pip
$ cat<<PIPCONF>~/.pip/.pip.conf
[global]
extra-index-url = http://localhost:8080/simple/
trusted-host = http://localhost:8080/simple/
PIPCONF

Para probar que funciona bien, instálelo en un entorno virtual diferente (o puede reemplazar cualquier instalación anterior con pip install --force):

$ python3 -m venv ~/virtualenv/test2
$ . ~/virtualenv/test2/bin/activate
(test2)$ pip install --index-url http://localhost:8080/simple/ rpm_query
Looking in indexes: http://localhost:8080/simple/
Collecting rpm_query
  Downloading http://localhost:8080/packages/rpm_query-0.0.1-py3-none-any.whl (12 kB)
Collecting rich==9.5.1
  Using cached rich-9.5.1-py3-none-any.whl (180 kB)
Collecting pygments<3.0.0,>=2.6.0
  Downloading Pygments-2.10.0-py3-none-any.whl (1.0 MB)
     |████████████████████████████████| 1.0 MB 5.4 MB/s            
Collecting colorama<0.5.0,>=0.4.0
  Downloading colorama-0.4.4-py2.py3-none-any.whl (16 kB)
Collecting typing-extensions<4.0.0,>=3.7.4
  Downloading typing_extensions-3.10.0.2-py3-none-any.whl (26 kB)
Collecting commonmark<0.10.0,>=0.9.0
  Downloading commonmark-0.9.1-py2.py3-none-any.whl (51 kB)
     |████████████████████████████████| 51 kB 5.8 MB/s             
Installing collected packages: typing-extensions, pygments, commonmark, colorama, rich, rpm-query
Successfully installed colorama-0.4.4 commonmark-0.9.1 pygments-2.10.0 rich-9.5.1 rpm-query-0.0.1 typing-extensions-3.10.0.2

lo que has aprendido

He proporcionado mucha información aquí. Esto es lo que cubrí:

En mi próxima publicación de esta serie, exploraré la escritura de aplicaciones de interfaz de usuario en Python.


Si quieres conocer otros artículos similares a Paquete de aplicaciones para instalar en otras máquinas con Python puedes visitar la categoría Package management.

Artículos de interés

Subir

Si continuas utilizando este sitio aceptas el uso de cookies. Más información