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

Contenu supprimé Contenu ajouté
Aucun résumé des modifications
Ligne 34 :
setup()
 
Cet exemple introduit '''setuptools''', qui est un module tiers à python que vous devez installer au préalable via <code>python -m pip install setuptools</code> et qui est un sur-ensemble de setuptools; c'est à dire qu'il permet de faire tout ce que distutils peut faire, mais en y ajoutant des fonctionnalités propres. Distutils existe depuis 1998Python 1.5, Setuptoolsrétro depuiscompatible 2005<ref>https://githubjusque Python 1.com/pypa/setuptools/commit/e9230cf512726e5ea428028c564a5b6a36b91095</ref>6, Setuptools a étévu construitle commejour uneau extensiondébut àdes celui-ciannées 2000, dans la même idée et de manière concurrente à zc.buildout<ref>httpshttp://www.pythonbuildout.org/communityen/sigslatest/currenttopics/distutils-sighistory.html/</ref>, au final setuptools a été retenu comme outil officiel. IlsCes extensions ont la particularité d'importer la fonction setup de distutils et de l'appeler avec les même arguments qu'ils ont reçu en entrée, on peut résumer le fonctionnement de ces outils ainsi.
 
# fichier setuptools/setup.py ou zc.buildout
Ligne 43 :
distutils_setup(**args)
 
De la même manière les fichiers qui exécutent les commandes (setup, build, sdist...) importent le fichiers de distutils et l'appellent tout en y ajoutant leurs propres fonctionnalités. ''Tout ce que l'on peut faire avec distutils, on peut le faire avec setuptools'' (ou presque<ref>https://setuptools.readthedocs.io/en/latest/deprecated/distutils-legacy.html</ref>). Si l'objectif de distutils est de produire des bundles installables à telle ou telle plateforme, celui de setuptools est assez multiple, mais principalement de produire des eggs spécifiques à python, et couplé avec l'utilitaire easy_install, de simplifier l'installation et la désinstallation (ce que distutils ne permet pas de faire) de paquetages en python, et distribuables sans necessiténécessité de build. Mais pour bien les comprendre il faut comprendre comment distutils fonctionne et comment il est possible de l'étendre.
 
== Distutils ==
 
L'arborescence du projet doit contenir un fichier setup.py, qui contient le code python nécessaire à l'installation de votre module. Cette installation, doit aboutir à la copie des fichiers nécessaires dans le répertoire site-packages le plus pertinent. Ce procédé se fait automatiquement si le fichier setup.py contient un appel à la fonction <code>setup()</code> de <code>distutils.core</code>. Cette fonction lirelit la ligne de commandes utilisée pour invoquer le script puis exécuteraexécute la commande appelléeappelée. <code>python ./setup.py toto --arg1=2</code> appelle la commande toto avec l'argument bar. Un fichier <code>'''setup.cfg'''</code> peut contenir des valeurs d'arguments par defautdéfaut pour chaque commande. Si il est ainsi
# setup.cfg
[toto]
Ligne 54 :
La commande toto s'exécutera avec arg1=2 at arg2=1, la ligne de commande prévaut sur le fichier de configuration.
 
Ne reconnaissant pas la commande toto un message d'erreur s'affiche avec la liste des commandes acceptées. Leur code source peut se trouver dans <code>Libs/distutils/commands</code>. Si vous lisez ces fichiers, et notamment la méthode <code>run</code>, vous remarquerez que beaucoup de commandes en appellent d'autres. Par exemple <code>install</code> appelle <code>build</code>, <code>build</code> peut appeller <code>build_ext</code>, <code>build_py</code>..., que chaque commande a des arguments différents qui peut altérer la course de l'installation, et des valeurs par défaut parfois non désirables.
 
Les principales commandes centrales 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 repertoirerépertoire, par defautdé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>setupinstall</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 build et sdist, mais les commandes build et sdist ne peuvent pas fonctionner à partir d'une archive faite par bdist. 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.
 
Qu'on utilise la commande <code>install</code>, <code>sdist</code> ou <code>bdist</code>, la commande <code>build</code> sera exécutée dans tous les cas, pour qu'elle sache quoi mettre dans la librairie, on utilise l'argument <code>py_modules</code> et <code>package</code> de <code>setup()</code>. Ceux-ci ont d'abord été mutuellement exclusifs, avant qu'il devienne possible de les utiliser en même temps. <code>py_modules</code> accepte une liste de chaines de caracteres, qui doivent correspondre à la manière dont les fichiers seront importés, par exemple un fichier <code>toto.py</code> sera importé ainsi
setup(py_modules=['toto'])
Si vous avez un autre fichier dans un sous-répertoire <code>a > b</code>, celui-ci doit contenir un fichier <code>__init__.py</code>, et vous le déclarez ainsi
setup(py_modules=['toto', 'a.b.toto'])
Cela inclura automatiquement le fichier __init__, pas besoin d'écrire <code>setup(py_modules=['toto', 'a.b.toto', 'a.b.__init__])</code>. Ces noms d'import doivent obligatoirement référencer des fichiers qui existent et se terminent par .py, se trouvent dans un répertoire contenant un fichier <code>__init__</code>. <code>py_modules</code> n'est pas compatible avec des fichiers non python (readme, ...) ou compressés (pyo, pyc) ou des paquetages PEP420.
 
L'option <code>package</code> est plus complexe à écrire, mais permet d'inclure tout type de fichier et d'utiliser une syntaxe de <code>glob</code><ref name="">https://docs.python.org/3/library/glob.html#glob.glob</ref>ing pour inclure rapidement des répertoires entiers de fichiers dans la librairie. Si on veut inclure les mêmes fichiers qu'avec <code>py_modules</code>, mais en plus un README, et des fichier en python compilés et de typage dans le répertoire a > b, on écrit
<nowiki>setup(package=['a.b'], package_data={'': ['README'], 'a.b': ['*.pyi', '*.pyc']}</nowiki>
<code>package</code> prend une liste de répertoires à chercher pour y trouver des fichiers en python, Si on veut ajouter d'autres fichiers que des scripts en python, on doit utiliser <code>package_data</code> qui prend un dictionnaire de champs nom de package -> liste de globs à appliquer. Le champ au nom vide <code><nowiki>''</nowiki></code> s'applique à tous les packages et à la racine du projet.
 
== Références ==