« Distribuer un projet en python » : différence entre les versions

Contenu supprimé Contenu ajouté
Aucun résumé des modifications
Ligne 1 :
La plus simple manière de partager un projet écrit en python est de le paqueter dans une archive zip, et de laisser chacun extraire et installer votre projet là où c'est nécessaire. Ce procédé fastidieux a été automatisé par des librairies tierces, intégrées dans lesla librairies standard distributionsde python ellesdès mêmesPython 1.6. Parmi celles-citous les outils qui sont apparus pas la suite on peut citer distutils, easy_install, pkg_resources, setuptools, pip, pyprojectbuildout... La meilleure manière de paqueter son projet et de le distribuer ne cesse d'évoluer, ce livre essaiera de vous aider à vous repérer dans cet univers. Une solide connaissance du langage python est nécessaire avant de continuer, vous pouvez lire ce livre pour cela : [[Programmation Python]].
 
== Module tiers ==
 
Dans le vocabulaire python, le projet que vous chercher à paqueter et distribuer est appelé un « '''module tiers''' » (''third-party module''). Quel que soit l'outil que vous utilisez (easyinstall, pip...), l'endroit où vos fichiers s'installeront doit être trouvable par le module <code>site</code> de python. Vous pouvez lire ce fichier vous même (fichier Lib/site.py), il est assez court, pour comprendre comment il fonctionne. Pour simplifier, ce fichier va mettre à jour la propriété <code>sys.path</code> avec le dossier <code>site-packages</code> pour que vous puissiez simplement <code>import ''module tiers''</code> dans vos projets, et que ceux-ci soient centralisés dans un unique dossier utilisable par tous vos projets. C'est très utile pour les librairies lourdes. Pour savoir comment site.py va altérer votre environnement, tapez simplement <code>python -m site</code>. Pour empêcher python d'exécuter ce script, lancez-le avec l'option -S, c'est à dire <code>python -S</code>, cela vous laissera loisir de modifier l'environnement d'exécution puis d'importer vous même ce module via <code>import site</code>, sachant qu'il va faire beaucoup de lectures de dossiers et peut ralentir le démarrage de votre script python.
 
Non seulement d'inclure les site-packages, site.py essaiera de détecter si l'instance de python courante s'exécute dans un « '''environnement virtuel''' » (dit aussi venv, pour ''virtual environment''). Il détermine cela si il existe un fichier <code>pyenv.cfg</code> dans le répertoire immédiatement au dessus de l'exécutable python en cours. Si c'est le cas, le fichier site-packages de l'environnement virtuel est placé tout en haut de la liste sys.path afin que les imports soient en priorité cherchés dans cet environnement, et utiliser le site-packages de l'utilisateur, puis le site-packages de l'installation python en deuxième recours, à moins que le fichier pyenv.cfg contienne la ligne <code>include-system-site-packages = false</code>, auquel cas tout import de module ne peut réussir que si il existe bien dans l'environnement virtuel. Cette ligne est indispensable si on veut s'assurer que son environnement de travail nécessaire au fonctionnement de son module soit reproductible au mieux par les tierces personnes avec qui vous partagerez les ''plans'' de votre environnement virtuel.
Ligne 56 :
Les principales commandes sont <code>build</code>, <code>install</code>, <code>sdist</code>, <code>bdist</code> et <code>bdist_*</code>.
 
La commande <code>build</code> copie les fichiers python dans un répertoire, par défaut <code>build/lib*</code>, compile les modules en C/C++ et les copie également, par simplicité lorsqu'on parle de librairie plus loin dans ce cours, c'est de ce dossier qu'on parlera. La commande <code>install</code> recopie tout ce qui se trouve dans le répertoire peuplé par <code>build</code> dans le dossier <code>site-packages</code>, la commande <code>sdist</code> produit une archive zip avec tout ce qu'il faut pour que la commande bdist réussisse. Sachant cela il apparait que la fonction install peut fonctionner à partir de la commande build, sdist et bdist, la commande bdist à partir de la commande <code>build</code> et <code>sdist</code>, mais les commandes <code>build</code> et <code>sdist</code> ne peuvent pas fonctionner à partir d'une archive faite par <code>bdist</code>. setup/build fonctionnent à partir d'un répertoire local, sdist/bdist servent à partager le répertoire local, bdist avec le minimum vital (fichiers pyc, pyd, ...), et sdist avec les tests, fichiers source (py, pyx, c, c++, ...).
 
Notre objectif premier était de produire un répertoire zip facilement installable avec pip, donc doit-on utiliser ici sdist ou bdist? Cela dépend du contenu de notre paquetage, si il ne contient que des fichiers en python, les deux commandes sont assez similaires, mais si ils contients du code en cython, ou des modules C/C++ natifs, alors dans le cas de sdist les clients de votre librairie doivent posséder un compilateur C (qui occupe un espace disque jusque 30 gigaoctets) et les librairies python de dev, par défaut non installées. Si vous optez pour bdist, les modules binaires doivent être compatible avec chaque machine, c'est à dire qu'une distribution bdist faite en 64 bits ne fonctionnera pas sur une machine 32 bits, un module fait avec une librairie python 3.7 probablement ne fonctionnera pas dans un environnement 3.6 (même si l'usage de l'ABI stable de python peut mitiger cela<ref>https://docs.python.org/fr/3/c-api/stable.html</ref>), une librairie construite sous mac, windows ou linux ne fonctionnera pas sur les autres machines. Nous verrons cette problématique bien plus tard car elle nécessite une machinerie complexe hors de notre propos, mais dont le mot-clé est ''continous integration'' (CI), et nous concentrons sur sdist dans la suite du livre, et sur la forme de bdist la plus simple : celle faite par et pour soi.
Ligne 75 :
# ou alors
<nowiki>setup(packages=['', 'a.b'], package_data={'': ['README'], 'a.b': ['*.pyi', '*.pyc']}</nowiki> # fonctionne aussi, mais génère une erreur car le package vide est ambigu
On remarque que si le répertoire <code>a>b</code> contient un fichier <code>README</code>, il sera inclus aussi. Seul les règles de la racine s'appliquent à tous les sous-paquetages, si vous définissez un sous-paquetage <code>a.b.c</code>, les règles propre à <code>a.b</code> ne s'appliquent pas à <code>a.b.c</code>.
 
Si vous ne voulez pas inclure certains scripts en python, par exemple des scripts contentant des données locales dans votre paquetage, le plus simple est de les mettre dans un répertoire séparé non listé dans l'entrée <code>packages</code>, mais vous pouvez aussi utiliser <code>py_modules</code> juste pour le répertoire où ces fichiers se trouvent, mais cela peut être dangeureux avec setuptools et son outils de découverte automatique.
 
<code>package_data</code> est une fonctionnalité créé par setuptools et importée dans distutils<ref>https://setuptools.readthedocs.io/en/latest/userguide/datafiles.html</ref> depuis python 2, setuptools fonctionne différemment vis à vis de ce paramètre et peut l'ignorer complètement<ref>https://setuptools.readthedocs.io/en/latest/userguide/datafiles.html</ref>, nous verrons avec <code>sdist</code> que la présence d'un fichier <code>MANIFEST.in</code> peut être mutuellement exclusif avec ce paramètre.
 
=== <code>sdist</code> ===
Ligne 88 :
* les fichiers de test
La liste à jour et complète est dans la documentation ([https://docs.python.org/fr/3/distutils/sourcedist.html#specifying-the-files-to-distribute lien]).
Un fichier <code>PKG-INFO</code> est également ajouté qui contient des informations utiles pour ensuite créer des packages de type rpm, il contient le nom du projet, sa version, un résumé expliquant ce qu'il fait, un lien vers un site web, un nom d'auteur, des informations de license et une plateforme préférée, toutes ces informations sont passées via <code>setup()</code>, par exemple
 
setup(
Ligne 100 :
)
Un fichier <code>'''MANIFEST.in'''</code> peut indiquer des fichiers supplémentaires à inclure dans la distribution, mais si ces fichiers ne sont pas spécifiés dans <code>package_data</code>, l'étableils ne seront pas dans les distributions binaire et la commande d'<code>install</code> ne les copieratrouvera pas, le fonctionnement de <code>build</code> ignore en effet les fichiers de manifest, qui ne servent que pour <code>sdist</code>, l'idée est que les extras comme la documentation peut être consultés dans l'archive zip, et n'ont pas besoin d'être dans le répertoire exécutable de python. setuptools change cette interaction, et distutils lui même aété changé à de nombreuses reprises, commentsi ilbien interagitque avecde lenombreuses fichierchoses MANIFEST.innon estconcordante ont été écrits à ce gérésujet<ref>https://docs.python.org/fr/3/distutils/sourcedist.html#specifying-the-files-to-distribute</ref>.
 
=== <code>bdist</code> ===
 
Les fonctionnalités de <code>bdist</code> de <code>distutils</code> est largement obsolète, par défaut il produit un installeur qui copie les fichiers vers des chemins absolus, si <code>bdist</code> a été exécuté dans <code>/usr/local/python</code>, alors <code>install</code> va copier votre librairie vers ce même dossier pour les clients,. ceCe n'est pas compatible avec venv, ou tout systeme non unix. Autrement bdist peut aussi produire des paquatages rpm, des installeurs windows de type .msi ou .exe qui sont eux aussi largement obsolètes. C'est avec <code>setuptools</code> et le format egg que la commande bdist peut être réellement utilisée.
 
Une archive bdist de base ne contient pas le script setup.py, de PKG-INFO, et ignore les MANIFEST et autres fichiers inclus par sdist, c'est en effet à l'outil final (apt-get, .msi, ...) que revient la tâche de faire l'installation. Il produit cependant un fichier d'extension *.egg-info qui contient les mêmes données que PKG-INFO.
Ligne 110 :
== Setuptools ==
 
Setuptools permet en utilisant le format binaire <code>bdist_egg</code>, un répertoire de modules python centralisé '''pypi''' et l'utilitaire <code>easy_install</code>, de télécharger depuis une invite de commande un projet en python quel que soit la plateforme, contrairement aux anciens format .rpm ou .exe non specifiquesspécifiques à python, depuis une invite de commande. Par exemple <code>easy_install Pil</code> télécharge ce module, le décompresse puis l'installe suivant les spécifications du format egg. Pour l'utiliser on remplace <code>from distutil.core import setup</code> par <code>from setuptools import setup</code> dans le fichier <code>setup.py</code>, la génération d'un egg se fait ainsi
 
python setup.py bdist_egg # a condition d'importer setuptools dans setup.py
 
Une fois l'egg produit, on peut l'installer en faisant pointer easy_install dessus, <code>easy_install d:chemin/vers/le/fichier/egg</code>, ou l'uploader sur pypi, le champ <code>name</code> de <code>setup()</code> est utilisé comme clé doit être unique et ne pas être déjà utilisé, ce qui permet à tout le monde d'installer son module avec <code>easy_install <name></code>. La documentation du format egg peut se trouver [http://peak.telecommunity.com/DevCenter/PythonEggs ici] et [http://svn.python.org/projects/sandbox/trunk/setuptools/doc/formats.txt ici], ce format est toutefois obsolète.
 
=== Découverte de packages ===
 
Avec distutils, MANIFEST.in ne sert que pour les distributions source, et <code>package_data</code> pour les installations, ce qui entraine une redondance. Setuptools ajoute une option booleene <code>include_package_data</code><ref name="">https://github.com/pypa/setuptools/commit/4cd66c4147bef3ee8096f7161d407fb37582f1c9</ref> de sorte que le fichier MANIFEST.in soitserve lepour seulles à être utilisédeux, pour le paquetage et pour l'installation finale, ce qui déprécie <code>package_data</code>. L'usage de package_data a toutefois été rétabli plus tard pour permettre de n'installer des fichiers que lors d'une installation et non lors d'une distribution source, depuissi uneon arborescencene complètesouhaite récupéréepas depuispasser par sdist mais par git par exemple<ref>https://github.com/pypa/setuptools/commit/d98923012</ref>, il devient aussi possible d'exlureexclure des fichiers en<code>glob</code>és par package_data, de la même manière que <code>distutils</code> via MANIFEST.in<ref>https://docs.python.org/fr/3/distutils/commandref.html#sdist-cmd</ref>, avec l'argument <code>exclude_package_data</code>. Pour éviter la redondance, lL'usage exclusif de MANIFEST.in est généralement recommandé.
 
EnIl 2005,fut il estensuite possible d'inclure dans les distributions source tout le contenu suivi par cvs ou svn, à condition qu'il n'y ait pas de fichier MANIFEST.in à la racine du projet. Pour git cette fonctionnalité est assurée par le greffon <code>'''setuptools_scm'''</code>, qu'on l'active ainsi
 
from setuptools import setup
Ligne 141 :
== Pip ==
 
La commande <code>'''pip'''</code> sert à remplacer l'utilitaire <code>easy_install</code>. etIl lea contribué au format wheel (<code>egg.whl</code>) pour parremplacer le format wheel (<code>.whlegg</code>) , il fonctionne de manière similaire sinon. Il permet de facilement établir un arbre de dépendances grâce à la commande <code>freeze</code>.
 
== Pep517 ==
 
Dans les années 2010 des outils qui ne s'appuyantsont pas surdes distutilsextensions etde écrits en python 3distutils voient le jour et ont pour objectif de remplacer pip, on peut citer <code>bento</code> (obsolète), <code>flit</code> ou <code>peotrypoetry</code>. flitFlit est le seul outils qui permette la création de distributions binairement reproductibles<ref>https://reproducible-builds.org/</ref>, quand à poetry il permet d'une simple commande de créer un environnement virtuel, télécharger un ''wheel'' et de l'installer. Ces nouveaux outils en plus d'utiliser le format ''wheel'' savent lire le format <code>sdist</code> produitdirigé par les outils distutils/setuptoolsMANIFEST.in. Ils n'assurent pas toutes les fonctionnalités assuréesrendues possibles par setuptools, commenotamment la commande <code>setup.py develop</code> (ou assimilé). Il est possible de déclarer son paquetage de plusieurs manières grâce à un fichier <code>'''pyproject.toml'''</code> de sorte qu'il est possible d'utiliseraccomoder lesplusieurs formatssystèmes anciensd'installation. etOn nouveauxpeut deforcer pip à utiliser le nouveau ou l'ancien concertsystème avec l'option <code>--pep517</code> et <code>--no-pep517</code> deselon pip,qu'on parveuille exempleles forcerfonctionnalités unede installationsetuptools ancienneou pourbien utilisercelles <code>setup.pydes develop</code>nouveaux systèmes, aisnipar exemple <code>pip install --editable --no-pep517</code>.
 
== Terminologie ==
* '''Projet''', '''paquetage''', '''distribution''' : dans le vocabulaire il s'agit d'un module tiers, on l'utilise dans ce livre dans le sens de linux, comme produit fini, archive installable qui contient éventuellement des sous paquetages en python (appellés simplement paquetage pour python).
* '''Sous-paquetage''', '''sous-module''' : dans le vocabulaire python il s'agit d'un paquetage ou ''package''.
* Distribution source : Une archive contenant le nécessaire au développement, il s'agit de l'équivalent d'un git clone, contenu dans une archive.
* Distribution binaire : Un fichier utilisable par des installeurs finaux, contient le minimum nécessaire à l'utilisation de son projet.
* Commande : option passée à un outil, par exemple <code>pip install</code>, <code>install</code> est la commande.
 
== Références ==