Published by

Il y a 4 semaines -

Temps de lecture 10 minutes

Packaging Python : déploiement et hébergement

Nous avons vu dans un premier article consacré à setup.py et setuptools comment générer des archives distribuables de notre bibliothèque, nous allons voir maintenant comment les publier sur https://pypi.org/, le dépôt central des bibliothèques Python. La procédure pourra s’appliquer sur n’importe quel hébergement de paquets Python, y compris privé, j’y reviendrai par la suite.

Twine

Nous allons utiliser twine pour déployer nos distributions. C’est aujourd’hui l’outil de référence pour déployer des distributions Python notamment grâce à son approche très sécurisée : déploiement via HTTPS et stockage du couple utilisateur/mot de passe de manière sécurisée sur le disque dur local grâce à la bibliothèque keyring.

L’installation est très simple via pip : pip install twine

Finalisation du setup.py

Avant de passer au déploiement sur PyPI, regardons le fichier setup.py que nous avions construit précédemment :

from setuptools import setup

setup(name="relink",
      version="0.0.1",
      description="A client for https://rel.ink/ API",
      author="Romain Ardiet",
      author_email="romardie@publicisgroupe.net",
      packages=["relink", "tests"],
      install_requires=["requests"],
      extras_require={
            "dev": ["requests-mock"],
      },
      license="Apache 2.0")

On remarque que la description est très brève et ne donne aucune indication sur la manière d’utiliser notre bibliothèque, ce qui n’est pas forcément une bonne chose pour l’expérience développeur.

Nous pouvons ajouter une description beaucoup plus complète via un paramètre long_description. Et pour que celle-ci soit plus facile à éditer nous pouvons l’inclure dans un fichier
README.md
à côté de setup.py et la lire depuis celui-ci.

Fichier README.md :

relink
========

A client for https://rel.ink/ API

# Usage

```
from relink.client import RelinkClient

client = RelinkClient()

shortened_url = client.shorten_url("https://news.ycombinator.com/")
print(shortened_url) # => 'https://rel.ink/Nn8y9p'

client.get_full_url(shortened_url) # => "https://news.ycombinator.com/"
```

# License

relink is licensed under the Apache 2.0 license.

Et dans le fichier setup.py :

from setuptools import setup

with open("README.md", "r") as fh:
    long_description = fh.read()

setup(name="relink",
      version="0.0.1",
      description="A client for https://rel.ink/ API",
      long_description=long_description,
      long_description_content_type="text/markdown",
      author="Romain Ardiet",
      author_email="romardie@publicisgroupe.net",
      packages=["relink", "tests"],
      install_requires=["requests"],
      extras_require={
            "dev": ["requests-mock"],
      },
      license="Apache 2.0")

Je précise que le contenu est au format markdown avec le paramètre long_description_content_type. À l’heure actuelle trois formats sont supportés par PyPI : text/plain pour du texte brut, text/x-rst pour le format reStructuredText et donc text/markdown pour le format markdown. Maintenant le contenu de notre fichier README.md s’affichera sur la future page de notre bibliothèque sur PyPI.

Une autre chose importante à renseigner sont les
classifiers

. Ce sont des metadatas qui seront parsées par PyPI pour permettre de catégoriser votre bibliothèque et de rappeler des éléments importants comme sa licence, son domaine d’utilisation et plein d’autres aspects :

setup(
      # ...
      classifiers=[
            "Development Status :: 5 - Production/Stable",
            "Topic :: Internet :: WWW/HTTP",
            "Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking",
            "Environment :: Console",
            "Environment :: Web Environment",
            "Intended Audience :: Developers",
            "Programming Language :: Python :: 3.5",
            "Programming Language :: Python :: 3.6",
            "Programming Language :: Python :: 3.7",
            "Programming Language :: Python :: 3.8",
            "License :: OSI Approved :: Apache Software License",
            "Operating System :: OS Independent",
      ]
      # ...
      )

Vous pouvez retrouver la liste complète des classifiers à cette adresse : https://pypi.org/classifiers/

Voilà, nous sommes prêts à déployer notre bibliothèque Nous pouvons régénérer les distributions de celle-ci en prenant en compte la description et les classifiers : python setup.py sdist bdist_wheel

PyPI de test

Une fonctionnalité très utile de PyPI est de proposer une instance spécifiquement dédiée aux tests : https://test.pypi.org/. Nous allons donc pouvoir tester l’envoi de notre bibliothèque en toute sécurité et valider que tout se passe bien sans venir affecter le vrai index PyPI. Pour cela, nous créons un compte à l’adresse suivante : https://test.pypi.org/account/register/. Une vérification d’e-mail nous sera demandée pour finaliser l’inscription.

Puis on se connecte et on peut voir grâce à un bandeau jaune que nous sommes bien sur l’instance de test :

Nous pouvons donc envoyer nos 2 distributions avec la commande twine upload en précisant que l’on souhaite les envoyer sur le PyPI de test avec l’option
–respository-url
:

$ twine upload --repository-url https://test.pypi.org/legacy/ dist/*
Uploading distributions to https://test.pypi.org/legacy/
Enter your username: romibuzi
Enter your password: 
Uploading relink-0.0.1-py3-none-any.whl
100%██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7.74k/7.74k [00:01<00:00, 5.24kB/s]
Uploading relink-0.0.1.tar.gz
100%██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6.82k/6.82k [00:01<00:00, 5.02kB/s]

View at:
https://test.pypi.org/project/relink/0.0.1/

ATTENTION : une fois notre package publié dans la version renseignée, il nous sera impossible d’envoyer à nouveau la même version même en ayant supprimé celle-ci auparavant que ce soit sur l’instance de test ou sur le vrai PyPI. Il est donc important de bien tester sur l’instance de test qui nous est fournie par PyPI.

 

Une chose qui peut paraître très étrange est l’url de l’API : /legacy. Un peu d’histoire va nous aider à comprendre. PyPI est un projet très ancien qui a vu le jour en 2002 et qui était hébergé sur https://pypi.python.org/. En 2017 une très grosse migration a eu lieu vers une nouvelle base de code et un nouveau domaine dédié https://pypi.org/. Mais pour ne pas casser l’écosystème de distribution de packages avec cette migration, notamment avec des outils comme twine, l’API de l’ancien projet a été émulé dans la nouvelle base de code derrière l’url legacy.

 

Notre librairie est donc déployée et nous pouvons voir le résultat à l’url indiquée par twine :

À droite se trouve le bloc où notre description est affichée qui correspond au contenu de notre fichier README.md.

Une barre latérale à gauche contient les informations sur l’auteur de la bibliothèque (moi ici en l’occurrence), la licence ainsi que toutes les metadatas que nous avons renseigné dans les fameux classifiers.

Une chose également très utile est qu’une commande d’installation pip prête à l’emploi avec le paramètre -i (pour –index-url) nous est donnée, celle-ci nous permet de tester l’installation et l’utilisation de notre bibliothèque en la téléchargeant depuis l’instance PyPI de test :

romain at macbookromain in ~/Workspaces/Python/relink
$ virtualenv testing-venv && source testing-venv/bin/activate

romain at macbookromain in ~/Workspaces/Python/relink (testing-venv)
$ pip install -i https://test.pypi.org/simple/ relink==0.0.1
Looking in indexes: https://test.pypi.org/simple/
Collecting relink==0.0.1
  Downloading https://test-files.pythonhosted.org/packages/c1/b1/1b3c9ee2e830a44b232c3c19a154ba82727d7f726aeebdf0ef9238461323/relink-0.0.1-py3-none-any.whl (2.9 kB)
Collecting requests
  Downloading https://test-files.pythonhosted.org/packages/6d/00/8ed1b6ea43b10bfe28d08e6af29fd6aa5d8dab5e45ead9394a6268a2d2ec/requests-2.5.4.1-py2.py3-none-any.whl (468 kB)
     |████████████████████████████████| 468 kB 797 kB/s
Installing collected packages: requests, relink
Successfully installed relink-0.0.1 requests-2.5.4.1
romain at macbookromain in ~/Workspaces/Python/relink (test-venv) 
$ python -c "from relink.client import RelinkClient; client = RelinkClient(); print(client.shorten_url('https://google.com'))"
https://rel.ink/RgeNkO

Succès, notre déploiement est fonctionnel !

PyPI

Maintenant que nous avons validé notre déploiement sur l’instance de test, nous pouvons déployer sur la vraie instance PyPI.

La procédure sera identique, on se rend d’abord sur https://pypi.org/account/register/ pour créer un compte (nous ne pouvons pas réutiliser notre compte de l’instance de test car les 2 instances ne sont pas liées).

Puis on utilise twine sans surcharger l’option –repository-url cette fois-ci car twine est configuré pour envoyer sur PyPI par défaut. Notre couple utilisateur/mot de passe sera demandé à nouveau et le téléchargement sera effectif sur le vrai PyPI cette fois :

$ twine upload dist/*
Uploading distributions to https://upload.pypi.org/legacy/
Enter your username: romibuzi
Enter your password: 
Uploading relink-0.0.1-py3-none-any.whl
100%██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7.74k/7.74k [00:01<00:00, 5.24kB/s]
Uploading relink-0.0.1.tar.gz
100%██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6.82k/6.82k [00:01<00:00, 5.02kB/s]

View at:
https://pypi.org/project/relink/0.0.1/

Déployer sur un hébergement privé comme Artifactory ou un PyPI privé

Tout ce qui est mis sur PyPI est public et ouvert à tous. Mais en entreprise nous voulons la plupart du temps partager du code uniquement destiné à un usage interne.

Dans ce cas la procédure sera la même qu’avec l’instance test de PyPI, en spécifiant l’option –repository-url de twine nous pouvons envoyer nos distributions sur tous les systèmes qui supportent l’hébergement de paquets Python comme artifactory ou un PyPI privé : pypiserver ou private-pypi.

Sauvegarder les identifiants et les urls

Nous venons de voir que chaque fois que nous allons envoyer nos distributions, twine nous demandera nos identifiants et nous devrons spécifier l’option –repository-url si nous voulons les envoyer à un autre endroit que PyPI. Pour nous simplifier la tâche nous pouvons stocker ces configurations dans le fichier de configuration ~/.pypirc sur Linux et MacOS ou %HOME%\.pypirc sur Windows :

[distutils]
index-servers=
    pypi
    testpypi
    artifactory
    privatepypi

[pypi]
repository: https://upload.pypi.org/legacy/
username: romibuzi

[testpypi]
repository: https://test.pypi.org/legacy/
username: romibuzi

[artifactory]
repository: https://myartifactory.com/api/pypi/my-repository
username: admin

[privatepypi]
repository: https://myprivatepypi.com:8888/simple/
username: admin

[localpypi]
repository: http://localhost:8888/simple/
username: admin

De cette manière nous pouvons désormais juste indiquer la clé de la configuration à utiliser avec l’option -r (raccourci de –repository) : twine upload -r privatepypi dist/ ou twine upload -r artifactory dist/*

La dernière chose qui nous sera encore demandé est le mot de passe de l’identifiant renseigné mais nous pouvons le sauvegarder de manière sécurisée grâce à keyring.

Sauvegarde des mots de passe avec keyring

Nous aurions pu ajouter nos mots de passe dans le fichier ~/.pypirc en renseignant la clé password dans chacune des configurations, mais ceci pose un vrai problème en terme de sécurité car les mots de passe se retrouvent stockés en clair sur notre disque local. Pour pallier à ce problème, twine vient avec la bibliothèque keyring qui agit comme proxy vers les différentes applications de stockage de mot de passe des différents OS : le trousseau d’accès sur Mac, le coffre Windows ou encore le keyring Linux.

On stocke le mot de passe de notre utilisateur et de notre repository via la commande keyring set :

$ keyring set https://test.pypi.org/legacy/ romibuzi
Password for 'romibuzi' in 'https://test.pypi.org/legacy/':

Ainsi, la prochaine fois que nous allons envoyer nos distributions, twine regardera si le mot de passe pour le couple repository/utilisateur est présent dans le keyring et l’utilisera automatiquement.

Conclusion

Nous venons de voir comment déployer les distributions source et wheel de notre bibliothèque grâce à twine ainsi que comment peaufiner la présentation de celle-ci sur PyPI via une description complète et les classifiers.

Nous avons aussi également vu comment bien gérer nos identifiants et nos mots de passe pour les différentes destinations de nos distributions.

Vous pouvez retrouver l’ensemble des sources de la bibliothèque à cette adresse : https://github.com/xebia-france/relink

References

Published by

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.