tag:linuxfr.org,2005:/sections/pythonLinuxFr.org : les dépêches de Python2026-01-30T14:55:36+01:00/favicon.pngtag:linuxfr.org,2005:News/428652026-01-30T14:55:36+01:002026-01-30T14:55:36+01:00Profileur mémoire MALT 1.6.0 et support de PythonLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><h2 id="toc-malt-cest-quoi">MALT c'est quoi ?</h2>
<p>L'optimisation est une activité connue de bien des développeurs, fouiller, chercher dans son code, pour, peut être, gagner un peu de performance (beaucoup, parfois). Dans cette tâche ardue, face à des codes grandissants, massifs, utilisant de nombreuses bibliothèques externes, un outil central dans ce travail de fourmi : <strong>le profileur</strong>.</p>
<p>Le profileur fait partie des outils importants pour le développeur qui se soucie des ressources qu'il utilise. Il lui permet de chercher les points chauds dans son code, comprendre le cout de chaque fonction, chaque ligne implémentée. Bien des outils s'intéressent à une chose : bien utiliser votre processeur.</p>
<p>Mais que se passe-t-il si vous rencontrez des difficultés avec l'utilisation de votre <strong>mémoire</strong> ? Si vous pensez mal utiliser la fonction <code>malloc()</code> du C ou l'opérateur <code>new</code> du C++ ? Si vous avez oublié qu'une de vos variables globales occupe <strong>10 Go</strong> (surtout en Fortran) ?</p>
<p>Publié il y a quelques années <a href="https://memtt.github.io/malt/">MALT</a> (licence CeCILL-C) permet de répondre à la question en cherchant toutes les <strong>allocations mémoires</strong> faites par votre code. Cet outil avait fait l'objet d'un article sur LinuxFR en 2018 lors de sa mise en open source : <a href="//linuxfr.org/news/profileurs-memoire-malt-et-numaprof">Profileurs mémoire MALT et NUMAPROF</a>.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://memtt.github.io/" hreflang="en" href="https://linuxfr.org/redirect/116923">Site officiel de MALT</a></li><li>lien nᵒ 2 : <a title="https://github.com/memtt/malt" hreflang="en" href="https://linuxfr.org/redirect/116924">Dépôt github</a></li><li>lien nᵒ 3 : <a title="https://linuxfr.org/news/profileurs-memoire-malt-et-numaprof" hreflang="fr" href="https://linuxfr.org/redirect/116925">Premier article LinuxFR sur MALT</a></li></ul><div><h2 id="toc-versions-160">Versions 1.6.0</h2>
<h3 id="toc-avancées">Avancées</h3>
<p>Depuis, cet outil fait son chemin avec l'ajout du support de <strong>Rust</strong> ainsi que nombreuses corrections et ajouts tels que la possibilité de dumper le profile mémoire juste avant que le système ne soit plein.</p>
<pre><code class="sh">malt -o dump:on-sys-full-at<span class="o">=</span><span class="m">80</span>% -o watch-dog<span class="o">=</span><span class="nb">true</span> ./mon_programme
<span class="c1"># également :</span>
<span class="c1"># -o dump:on-app-using-rss=10G</span>
<span class="c1"># -o dump:on-app-using-virt=80%</span>
<span class="c1"># -o dump:on-app-using-req=500M</span></code></pre>
<p>La possibilité de suivre les appels à <strong>mmap</strong>, <strong>mremap</strong>, <strong>munmap</strong> si vous les appelez directement dans votre code au lieu de laisser <strong>malloc</strong> faire son travail.</p>
<h3 id="toc-support-de-python">Support de python</h3>
<p>MALT a initialement été développé dans le contexte du <a href="https://fr.wikipedia.org/wiki/calcul%20%C3%A0%20haute%20performance">calcul à haute performance</a> — HPC (High Performance Computing) donc surtout pour le C / C++ / Fortran / Rust.</p>
<p>Dans la communauté scientifique, nous voyons dans les laboratoires de plus en plus fleurir l'usage d'un langage à priori bien éloigné de nos prérogatives de performances : <a href="https://www.python.org/">Python</a>.</p>
<p>Dans un cadre de calcul scientifique, il est souvent utilisé plutôt comme un wrapper permettant d'appeler et d'orchestrer des bibliothèques en C /C++ / Fortran, elles, performantes. Quoi que l'usage amène aussi à l'écrire de certaines parties du code en Python grâce à <a href="https://numpy.org/">Numpy</a> ou <a href="https://numba.pydata.org/">Numba</a> pour accéléré la partie calcule sur des tableaux ou autres frameworks d'exploitation des GPU.</p>
<p>La version <strong>1.6.0 de MALT</strong> vient d'ajouter le support (encore quelque peu <strong>expérimental</strong>) natif de Python permettant d'analyser un code <strong>pur python</strong> ou <strong>mix Python / C / C++…</strong>. Il s'agit pour l'instant de la <strong>première version</strong> avec ce support, il reste donc du travail.</p>
<p>Sont supportés les versions Python <strong>supérieures à 3.11</strong> ainsi que les environnements <strong>Conda</strong> / <strong>Anaconda</strong> / <strong>Venv</strong>. À l'heure de rédaction de cet article, cela comprend les versions <strong>3.11</strong> à <strong>3.14</strong>.</p>
<p>MALT étant orienté C / C++, il ne supporte que <strong>Linux</strong> comme système d'exploitation. (<em>NdM:</em> la causalité évoquée ici devrait faire réagir des développeurs d'autres systèmes libres notamment :))</p>
<h3 id="toc-utilisation-sur-python">Utilisation sur Python</h3>
<p>Si la commande <code>malt</code> fonctionne parfaitement, il est recommandé d'utiliser le wrapper <code>malt-python</code> qui adapte quelques configurations spécifiques à Python non encore automatiques.</p>
<pre><code class="sh">malt-python ./script.py
<span class="c1"># équivalent à </span>
malt --profile python-default ./script.py
<span class="c1"># liste des profiles</span>
malt --profile <span class="nb">help</span>
<span class="c1"># Afficher le profile</span>
malt-webview ./malt-script-py-6889.json</code></pre>
<h3 id="toc-profilage-rapide">Profilage rapide</h3>
<p>Notons que l'<strong>overhead</strong> de MALT est important en Python du fait du large nombre d'allocations générées par ce langage et de la méthode de résolution des piles d'appels pour retrouver les lignes dans votre code. Ces détails d'analyse peuvent être désactivés via :</p>
<pre><code class="sh"><span class="c1"># Désactivé complète de l'analyse des piles</span>
malt-python -p python-no-stack ./my_script.py
<span class="c1"># Analyse des piles par "sampling"</span>
malt-python -p python-sampling ./my_script.py</code></pre>
<h3 id="toc-nouvelle-interface">Nouvelle interface</h3>
<p>La version <strong>1.6.0</strong> arrive également avec une nouvelle interface graphique avec un code remis à jour<br>
par rapport à sa version originale vieillissante.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6d656d74742f6d616c742f7261772f6d61737465722f73637265656e73686f742e706e67/screenshot.png" alt="Capture annotation python" title="Source : https://github.com/memtt/malt/raw/master/screenshot.png"></p>
<h3 id="toc-profil-statique">Profil statique</h3>
<p>Pour ceux qui voudraient échanger les profils avec d'autres sur d'autres OS, il est possible depuis la <strong>1.6.0</strong> de générer une version statique des pages de l'interface (hors annotation des sources et arbre d'appel navigable) via :</p>
<pre><code class="sh"><span class="c1"># Toues les pages possible en statique</span>
malt-webview -static ./report malt-progr-123456.json
<span class="c1"># Seulement la page de résumé.</span>
malt-webview -static-summary ./report malt-progr-123456.json</code></pre>
<h2 id="toc-installation">Installation</h2>
<p>MALT n'est pas encore disponible dans les distributions classiques, vous devez donc le compiler et l'installer à la main. Le nombre réduit de dépendances obligatoires en fait un outil relativement facile à installer.</p>
<p>On trouvera la procédure dans la <a href="https://memtt.github.io/malt/doc/1.6.0/start/installation.html">documentation</a> du projet.</p>
<h2 id="toc-documentation">Documentation</h2>
<p>La documentation a été complètement ré-écrite et disponible sur le site officiel : <a href="https://memtt.github.io/malt/doc/v1.6.0">documentation</a>.</p>
<h2 id="toc-outils-similaires-pour-python">Outils similaires pour Python</h2>
<p>Découvrir un outil est aussi l'occasion d'en découvrir d'autres. Restons dans le monde du <strong>Python</strong>, si MALT ne vous convient pas vous trouverez peut-être votre bonheur avec les <strong>outils suivants</strong> et <strong>complémentaires</strong> à MALT et eux totalement dédiés au Python toujours pour la <strong>mémoire</strong> :</p>
<ul>
<li>
<a href="https://bloomberg.github.io/memray/">Memray</a> (Apache Public License 2)</li>
<li>
<a href="https://github.com/plasma-umass/scalene">Scalene</a> (Apache Public License 2)</li>
</ul>
<p><strong>MALT</strong> se positionne par rapport aux deux présentés en apportant une analyse fine en <strong>annotant</strong> tout <strong>le source code</strong> de l'application. Il offre également une analyse des <strong>variables globales</strong> et <strong>TLS</strong> coté C/C++/Fortran/Rust.</p>
</div><div><a href="https://linuxfr.org/news/profileur-memoire-malt-1-6-0-et-support-de-python.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/142101/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/profileur-memoire-malt-1-6-0-et-support-de-python#comments">ouvrir dans le navigateur</a>
</p>
Sébastien ValatBenoît SibaudPierre Jarillonhttps://linuxfr.org/nodes/142101/comments.atomtag:linuxfr.org,2005:News/426852025-09-24T08:57:44+02:002025-09-24T08:57:44+02:00PyConFR 2025, planning et inscriptionsLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>La PyConFR 2025 a lieu du jeudi 30 octobre au dimanche 2 novembre au Campus René Cassin à Lyon. Le planning est en ligne et les inscriptions sont ouvertes !</p>
<p>Comme toujours, l’évènement est gratuit et l’inscription est obligatoire.</p>
<p>Les deux premiers jours de la conférence seront occupés par les sprints. Et les deux jours suivants seront dédiés aux conférences (longues et courtes) et ateliers.</p>
<p>Trois keynotes sont au programme :</p>
<ul>
<li>Embracing Weird Code, d’Ivana Kellyer</li>
<li>Le rêve de tout enfant - devenir DBA ?, de Karen Jex</li>
<li>Être un·e allié·e du numérique pour tou·te·s en environnement hostile, de Morgane Rozenn Hauguel</li>
</ul>
</div><ul><li>lien nᵒ 1 : <a title="https://www.pycon.fr/2025/" hreflang="fr" href="https://linuxfr.org/redirect/116233">PyConFR 2025</a></li><li>lien nᵒ 2 : <a title="https://www.pycon.fr/2025/fr/schedule.html" hreflang="fr" href="https://linuxfr.org/redirect/116234">Programme de la PyConFR 2025</a></li><li>lien nᵒ 3 : <a title="https://www.pycon.fr/2025/fr/schedule.html#register" hreflang="fr" href="https://linuxfr.org/redirect/116235">Inscriptions</a></li><li>lien nᵒ 4 : <a title="https://www.pycon.fr/2025/fr/conduct.html" hreflang="fr" href="https://linuxfr.org/redirect/116236">Code de conduite</a></li><li>lien nᵒ 5 : <a title="https://www.pycon.fr/2025/fr/support.html" hreflang="fr" href="https://linuxfr.org/redirect/116237">Soutenir l’évènement</a></li><li>lien nᵒ 6 : <a title="https://www.afpy.org/" hreflang="fr" href="https://linuxfr.org/redirect/116238">Association Francophone Python</a></li></ul><div><p>Un atelier de programmation pour les enfants (à partir d’environ 7 ans) a lieu le samedi après-midi.</p>
<p>Un espace enfants (de 3 ans à 12 ans) est aussi mis à disposition le samedi et dimanche gratuitement et sur inscription.</p>
<p>Un déjeuner PyLadies a également lieu durant la conférence. Un des objectifs est de tisser des liens entre la communauté PyLadies et le reste de la communauté Python francophone.</p>
<p>En plus du traditionnel repas du samedi soir, des visites guidées de Lyon sont aussi possibles les jeudi et vendredi soir, toujours sur inscription.</p>
<p>Enfin, le dimanche matin, l’AFPy tient son assemblée générale. Si vous souhaitez y voter, assurez-vous d’être à jour de cotisation. </p>
</div><div><a href="https://linuxfr.org/news/pyconfr-2025-planning-et-inscriptions.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/140348/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/pyconfr-2025-planning-et-inscriptions#comments">ouvrir dans le navigateur</a>
</p>
grewn0uilleYsabeau 🧶https://linuxfr.org/nodes/140348/comments.atomtag:linuxfr.org,2005:News/388302025-05-06T17:13:18+02:002025-05-06T17:13:17+02:00Kivy : un cadriciel graphique unique en PythonLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Kivy est un cadriciel (framework) graphique, permettant de développer des interfaces tactiles (ou utilisable à la souris) sur toutes les plateformes, y compris mobiles. Il s'accompagne d'une suite de logiciels très pratiques que nous allons présenter ici.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f617661746172732e67697468756275736572636f6e74656e742e636f6d2f752f343039373237333f733d32303026763d34/4097273?s=200&v=4" alt="logo Kivy" title="Source : https://avatars.githubusercontent.com/u/4097273?s=200&v=4"></p>
</div><ul><li>lien nᵒ 1 : <a title="https://kivy.org/" hreflang="en" href="https://linuxfr.org/redirect/102818">site officiel</a></li><li>lien nᵒ 2 : <a title="https://github.com/kivy-garden" hreflang="en" href="https://linuxfr.org/redirect/112078">Kivy Garden (extensions)</a></li><li>lien nᵒ 3 : <a title="https://github.com/kivymd" hreflang="en" href="https://linuxfr.org/redirect/112079">KivyMD</a></li><li>lien nᵒ 4 : <a title="https://ressources.labomedia.org/les_pages_kivy_en_details" hreflang="fr" href="https://linuxfr.org/redirect/115594">Un très bon point d'entrée en français (merci chyek)</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-kivy-bo%C3%AEte-%C3%A0-outils-graphique-multi-plateformes">Kivy : boîte à outils graphique multi-plateformes</a></li>
<li>
<a href="#toc-kv-interface-graphique-en-d%C3%A9claratif">kv : interface graphique en déclaratif</a><ul>
<li><a href="#toc-les-propri%C3%A9t%C3%A9s">Les propriétés</a></li>
</ul>
</li>
<li><a href="#toc-plyer-acc%C3%A9der-facilement-aux-fonctionnalit%C3%A9s-mat%C3%A9rielles-de-votre-appareil">Plyer : accéder facilement aux fonctionnalités matérielles de votre appareil</a></li>
<li><a href="#toc-python-for-android-utiliser-python-sur-android">Python For Android : utiliser Python… sur Android</a></li>
<li><a href="#toc-kivy-for-ios-d%C3%A9ployez-sur-les-appareils-apple">Kivy for iOS : déployez sur les appareils Apple</a></li>
<li><a href="#toc-pyjnius--utiliser-lapi-java-android-depuis-python">Pyjnius : utiliser l’API Java Android depuis Python</a></li>
<li><a href="#toc-kivymd-des-widgets-material-design">KivyMD, des widgets Material Design</a></li>
<li><a href="#toc-quelques-limitations">Quelques limitations</a></li>
<li><a href="#toc-conclusion">Conclusion</a></li>
<li><a href="#toc-une-note-dhistoire">Une note d’histoire</a></li>
</ul>
<h2 id="toc-kivy-boîte-à-outils-graphique-multi-plateformes">Kivy : boîte à outils graphique multi-plateformes</h2>
<p>Kivy permet de créer des interfaces graphiques naturellement adaptées aux écrans tactiles (mais qui restent utilisables sur un environnement de bureau traditionnel, à la souris et sans écran tactile). Il est écrit principalement en Python mais les parties critiques sont écrites en <a href="https://cython.org/">Cython</a>, ce qui lui permet d’avoir de bonnes performances.</p>
<p>Une interface Kivy peut s’écrire de deux façons (ou, plus couramment, par une combinaison de ces deux façons) : en Python directement, ou via <code>kv</code>, une syntaxe déclarative dont nous parlons plus bas.</p>
<p>Pour vous donner une idée, voici un exemple de <code>hello world</code> repris du site officiel de Kivy :</p>
<pre><code class="python"><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.button</span> <span class="kn">import</span> <span class="n">Button</span>
<span class="k">class</span> <span class="nc">TestApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">Button</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s1">'Hello World'</span><span class="p">)</span>
<span class="n">TestApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span></code></pre>
<p>Ce qui donnera :<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f6b6976792e6f72672f696d616765732f68656c6c6f776f726c642e706e67/helloworld.png" alt="capture d’écran du hello world" title="Source : https://kivy.org/images/helloworld.png"></p>
<p>Une interface est composée de « widgets » agencés via des « <a href="https://kivy.org/doc/stable/gettingstarted/layouts.html">layouts</a> ». Certains widgets permettent de facilement mettre en place des composants communs de nos jours, comme par exemple le widget <a href="https://kivy.org/doc/stable/api-kivy.uix.carousel.html">Carousel</a> :<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f6b6976792e6f72672f646f632f737461626c652f5f696d616765732f6361726f7573656c2e676966/carousel.gif" alt="capture d’un widget Carousel" title="Source : https://kivy.org/doc/stable/_images/carousel.gif"></p>
<p>D’autre part, Kivy fournit des outils qui simplifient la vie du développeur ou de la développeuse, en particulier <a href="https://kivy.org/doc/stable/api-kivy.properties.html">un système de propriétés</a> (à ne pas confondre avec les propriétés Python) qui permet de mettre automatiquement l’interface à jour quand elles sont modifiées, ou de facilement attacher une fonction de rappel (« callback »), voir plus bas pour un exemple simple. On peut aussi citer <a href="https://kivy.org/doc/stable/api-kivy.animation.html">un mécanisme d’animations</a> très pratique et efficace.</p>
<h2 id="toc-kv-interface-graphique-en-déclaratif">kv : interface graphique en déclaratif</h2>
<p>kv est un langage permettant de décrire des interfaces, il met à profit les propriétés mentionnées plus haut et l’interface va se mettre à jour automatiquement quand des propriétés utilisées dans kv sont modifiées.</p>
<p>Reprenons le hello world vu plus haut, et faisons-le avec kv:</p>
<pre><code>Widget:
Button:
text: "Hello World"
</code></pre>
<p>Plutôt simple non ?<br>
Le code kv est généralement mis dans des fichiers séparés avec l’extension <code>.kv</code>.</p>
<h3 id="toc-les-propriétés">Les propriétés</h3>
<p>Kivy a donc un concept de propriété, qui permettent la liaison de données bidirectionnelles (two-way data binding), ou en d’autres termes de facilement avoir l’état mis à jour entre le code python et l’interface décrite dans le fichier <code>kv</code>. C’est à l’usage très pratique et facile.</p>
<p>Un exemple va sans doute aider à comprendre.</p>
<p>Commençons par faire un environnement virtuel Python avec Kivy:</p>
<pre><code class="sh">$ mkdir demo
$ <span class="nb">cd</span> demo
$ python -m venv env
$ <span class="nb">source</span> env/bin/activate
$ pip install kivy</code></pre>
<p>Ensuite créez le fichier <code>demo.py</code> suivant :</p>
<pre><code class="python"><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.boxlayout</span> <span class="kn">import</span> <span class="n">BoxLayout</span>
<span class="kn">from</span> <span class="nn">kivy.properties</span> <span class="kn">import</span> <span class="n">NumericProperty</span>
<span class="k">class</span> <span class="nc">CompteurWidget</span><span class="p">(</span><span class="n">BoxLayout</span><span class="p">):</span>
<span class="n">compteur</span> <span class="o">=</span> <span class="n">NumericProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">increment</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">compteur</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">class</span> <span class="nc">DemoApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">CompteurWidget</span><span class="p">()</span>
<span class="n">DemoApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span></code></pre>
<p>Et maintenant, dans le même répertoire, ajoutez le fichier <code>demo.kv</code> suivant, ce dernier sera automatiquement découvert par Kivy parce qu’il s’appelle comme notre application (<code>DemoApp</code>) sans le suffixe <code>App</code> et en minuscule :</p>
<pre><code>
<CompteurWidget>:
orientation: 'vertical'
padding: 20
Label:
text: str(root.compteur)
font_size: 30
Button:
text: "Incrémenter"
on_press: root.increment()
</code></pre>
<p>Il ne nous reste plus qu’à lancer le programme :</p>
<pre><code class="sh">python demo.py</code></pre>
<p>Et à admirer :</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f706172746167652e6a616262657266722e6f72672f30363831333639362d333162652d376461382d613334302d3262343965386133653166342f636170747572655f64656d6f2e706e67/capture_demo.png" alt="capture d’écran du programme de démo" title="Source : https://partage.jabberfr.org/06813696-31be-7da8-a340-2b49e8a3e1f4/capture_demo.png"></p>
<p>Le label se met automatiquement à jour quand le compteur <code>compteur</code> est incrémenté.</p>
<p>La lectrice ou le lecteur assidu de DLFP pourra faire un <a href="//linuxfr.org/wiki/taptempo">TapTempo</a> en exercice.</p>
<p><strong>Note</strong>: Ne vous arrêtez pas au fait que l’UI est un peu "moche" par défaut, il y a des extensions beaucoup plus attractives (cf. <code>KivyMD</code> plus bas), et il est très facile de personnaliser l’interface et d’en faire une très belle en modifiant un peu ses fichiers <code>.kv</code>.</p>
<h2 id="toc-plyer-accéder-facilement-aux-fonctionnalités-matérielles-de-votre-appareil">Plyer : accéder facilement aux fonctionnalités matérielles de votre appareil</h2>
<p>Plyer est une bibliothèque permettant d’accéder à nombre de fonctions utiles de manière indépendante de la plateforme. Ainsi vous avez une API commune pour afficher une notification, demander l’état de la batterie, faire vibrer l’appareil, faire de la synthèse vocale de texte (« text-to-speech »), ouvrir un fichier avec le dialogue natif de la plateforme, etc.</p>
<p>Bien que développé par la même équipe que Kivy, ce paquet est utilisable indépendamment et est donc particulièrement utile pour n’importe quel projet multi-plateformes. Référez-vous <a href="https://github.com/kivy/plyer">à la page du projet</a> pour avoir un tableau des fonctionnalités supportées selon les plateformes, et à <a href="https://plyer.readthedocs.io/en/latest/">la documentation</a> pour plus de détails.</p>
<h2 id="toc-python-for-android-utiliser-python-sur-android">Python For Android : utiliser Python… sur Android</h2>
<p>Si vous souhaitez distribuer votre application sur Android, vous allez devoir avoir une version de Python compilée pour cette plateforme, ainsi que de tous les paquets nécessitant une compilation.</p>
<p>C’est le rôle de Python pour Android, qui est une collection d’outils qui s’appuient sur l’Android SDK pour permettre d’utiliser Kivy ou d’autre paquets Python sur Android, et ainsi faire votre propre distribution Python.</p>
<p>Si vous utilisez un paquet qui n’est pas en pur Python (c’est-à-dire qu’il y a des parties à compiler) et qui n’est pas encore disponible ou qui l’est mais dans une version trop ancienne, vous pouvez écrire ou mettre à jour des « recettes » (<em>recipes</em> en anglais) qui indiquent comment compiler. Référez-vous à <a href="https://python-for-android.readthedocs.io/en/latest">la documentation</a> pour voir comment faire.</p>
<p>Notez toutefois qu’il y a une pénalité à utiliser du Python par rapport à une application dans un langage compilé : il faut charger l’interprète au démarrage, et au tout premier lancement de votre application (et uniquement au premier) il y a aura une phase de décompression de vos fichiers. En dehors de ces petits inconvénients, ça fonctionne très bien.</p>
<p>Là encore, le projet est indépendant de Kivy et peut être utilisé avec un autre cadriciel.</p>
<h2 id="toc-kivy-for-ios-déployez-sur-les-appareils-apple">Kivy for iOS : déployez sur les appareils Apple</h2>
<p>À l’instar d’Android, l’équipe de Kivy fournit les outils pour faire votre distribution Python pour iOS.</p>
<p>Il vous faudra posséder une machine Apple pour les utiliser, à cause des restrictions imposées par cette plateforme (ceci n’est pas spécifique à Kivy pour iOS, mais s’applique à tous les outils pour développer sur iOS).</p>
<p>Ainsi avec une seule base de code (et après avoir passé un peu de temps pour configurer correctement les outils de compilation), vous pouvez déployer votre application Kivy/Python sur toutes les plateformes majeures (hormis le web, pour lequel il existe d’autres options comme <a href="https://brython.info/">Brython</a>, <a href="https://pyodide.org/en/stable/">Pyodide</a> ou <a href="https://pyscript.net/">PyScript</a>, mais c’est un sujet pour une autre dépêche).</p>
<h2 id="toc-pyjnius--utiliser-lapi-java-android-depuis-python">Pyjnius : utiliser l’API Java Android depuis Python</h2>
<p>Parfois l’utilisation de Plyer mentionné plus haut et de la bibliothèque standard Python ne suffisent pas, et vous avez besoin d’accéder à l’API d’Android, mais cette dernière est faite pour être utilisée avec Java ou Kotlin.</p>
<p>Pour pouvoir utiliser l’API d’Android depuis Python, <a href="https://github.com/kivy/pyjnius">PyJNIus</a> est disponible. Ce paquet permet d’accéder aux classes Java comme à des classes Python en utilisant JNI (« Java Native Interface » ou « Interface Native de Java »).</p>
<p>Ce paquet fournit une méthode <code>autoclass</code> qui convertit automatiquement une classe Java en Python, c’est très facile à utiliser et souvent suffisant pour les cas simples.</p>
<h2 id="toc-kivymd-des-widgets-material-design">KivyMD, des widgets Material Design</h2>
<p>KivyMD n’est pas un projet officiel de l’équipe Kivy, mais <a href="https://github.com/kivymd/KivyMD">un projet tiers</a> dédié à la création d’un ensemble de Widgets thémables adoptant Material Design.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f486561545468656174522f4b6976794d442d646174612f6d61737465722f67616c6c6572792f70726576696f75732e706e67/previous.png" alt="Image d’exemple de KivyMD issue de la documentation" title="Source : https://raw.githubusercontent.com/HeaTTheatR/KivyMD-data/master/gallery/previous.png"></p>
<p><em>L'image ci-dessus est issue de <a href="https://kivymd.readthedocs.io/en/latest/">la documentation de KivyMD</a>, vous trouverez également des démos vidéo sur le <a href="https://github.com/kivymd/KivyMD">dépôt du projet</a>.</em></p>
<h2 id="toc-quelques-limitations">Quelques limitations</h2>
<p>Une petite note sur mon expérience personnelle (<a href="//linuxfr.org/users/goffi">Goffi</a>). Kivy est un excellent cadriciel, et l’équipe est accueillante et réactive : c’est un projet que je recommanderais en général. Ceci dit, dans mon cas particulier (un client XMPP ayant de nombreuses fonctionnalités), j’ai quelques limitations qui me poussent actuellement à chercher des alternatives, notamment basées sur Qt (PyQt et PySide) :</p>
<ul>
<li>l’accessibilité est un problème majeur. <a href="https://github.com/kivy/kivy/issues/8596">L’équipe y travaille</a>, mais aujourd’hui les applications Kivy ne sont pas accessibles. C’est notamment dû au fait que l’interface utilisateur est entièrement gérée par Kivy, au lieu d’utiliser des composants natifs, et c’est un point complètement bloquant pour moi.</li>
<li>il n’y a pas de webview ou de rendu HTML natif, ce qui est bloquant parce que je fais du rendu de blogs.</li>
<li>le rendu de texte est incomplet, notamment sur les plateformes mobiles. C’est compliqué de faire un rendu riche avec des émojis, par exemple, un gros problème pour une application de messagerie de nos jours.</li>
<li>le support des portails Freedesktop est inexistant : il faut faire l’implémentation à la main, alors qu’il est natif sur des cadriciels majeurs comme GTK ou Qt. Je pense par exemple à l’ouverture ou l’enregistrement de fichier. Les portails sont particulièrement nécessaires si on veut voir son application sur <a href="https://flatpak.org/">Flatpak</a>. Plus de détails sur <a href="https://github.com/kivy/kivy/issues/7695">ce ticket</a>.</li>
<li>le support Wayland existe, mais lors de mes tests sur des distributions GNU/Linux sur Pinephone, il n’a pas fonctionné et je n’ai pas pu afficher l’application.</li>
<li>le chargement de l’interprète Python prend un certain temps, ce qui rend le lancement de l’application sur plateformes mobiles telle qu’Android et probablement iOS (que je n’ai pas testé) un peu lent. Sachant que sur ces plateformes l’application peut être tuée à n’importe quel moment auquel cas il faut tout relancer, cela peut mener à une mauvaise expérience utilisateur. Ceci dit, avec une distribution optimisée (en enlevant les paquets non utilisés), et sur les téléphones actuels qui sont très puissants, le problème est sans doute moindre.</li>
</ul>
<p>Notez que je n’ai pas eu l’occasion de travailler avec Kivy récemment, peut-être que ces remarques ne sont plus à jour. Merci d’indiquer en commentaires s’il y a eu de l’évolution sur ces points.</p>
<p>Aussi, mon cas d’utilisation est très demandant (rendu HTML, affichage de texte performant, etc). Dans la plupart des cas, Kivy remplit sans problème toutes les cases (sauf l’accessibilité, qui reste son problème le plus important à mon sens).</p>
<h2 id="toc-conclusion">Conclusion</h2>
<p>Comme vous pouvez le voir, un travail considérable a été effectué sur Kivy et son écosystème, et il en sort un cadriciel performant, pratique, et qui peut potentiellement fonctionner sur la plupart des plateformes. Kivy est agréable à utiliser, et dispose d’une communauté très active et disponible.</p>
<p>Ce cadriciel mérite de s’y attarder, et est une option sérieuse si vous voulez développer un projet rapidement pour une ou plusieurs plateformes.</p>
<h2 id="toc-une-note-dhistoire">Une note d’histoire</h2>
<p>Cette dépêche a été commencée le 04 octobre 2018 à 14:19 (au premier jour de la Pycon FR 2018, à Lille, où une partie de l’équipe de Kivy était présente). Je crois que c’est haut la main la dépêche qui est restée le plus longtemps dans l’espace de rédaction. Comme quoi, tout vient à point à qui sait attendre, comme dirait l’autre.</p>
<p>Merci à toutes les personnes qui ont participé à la correction de la dépêche.</p>
</div><div><a href="https://linuxfr.org/news/kivy-un-cadriciel-graphique-unique-en-python.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/115418/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/kivy-un-cadriciel-graphique-unique-en-python#comments">ouvrir dans le navigateur</a>
</p>
GoffivmagninBAudJulien Jorgeorfenorpalm123tisaacdovikhttps://linuxfr.org/nodes/115418/comments.atomtag:linuxfr.org,2005:News/425022025-05-05T11:42:51+02:002025-05-05T11:42:51+02:00PyConFR 2025, à Lyon du 30 octobre au 2 novembreLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>L’Association Francophone Python (AFPy) organise la PyConFR 2025 du jeudi 30 octobre au dimanche 2 novembre. Pour cette 16e édition, nous sommes accueillis par le Campus René Cassin de Lyon !</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f7777772e7079636f6e2e66722f323032352f7374617469632f696d616765732f6c6f676f2d66756c6c2e737667/logo-full.svg" alt="Logo de la PyConFR 2025" title="Source : https://www.pycon.fr/2025/static/images/logo-full.svg"></p>
</div><ul><li>lien nᵒ 1 : <a title="https://www.pycon.fr/2025/fr/" hreflang="fr" href="https://linuxfr.org/redirect/115621">PyConFR 2025</a></li><li>lien nᵒ 2 : <a title="https://cfp.pycon.fr/pyconfr-2025/cfp" hreflang="fr" href="https://linuxfr.org/redirect/115622">Proposer un sujet à la PyConFR 2025</a></li><li>lien nᵒ 3 : <a title="https://www.pycon.fr/2025/fr/support.html" hreflang="fr" href="https://linuxfr.org/redirect/115623">Supporter l’évènement</a></li><li>lien nᵒ 4 : <a title="https://www.afpy.org/" hreflang="fr" href="https://linuxfr.org/redirect/115624">Association Francophone Python (AFPy)</a></li><li>lien nᵒ 5 : <a title="https://www.pycon.fr/2025/fr/conduct.html" hreflang="fr" href="https://linuxfr.org/redirect/115625">Code de conduite de la PyConFR</a></li></ul><div><p>La PyConFR, c’est un évènement gratuit sur 4 jours autour du langage de programmation Python. Elle est composée deux jours de développements participatifs (sprints), puis de deux jours de conférences et ateliers.</p>
<p>L’appel à propositions est ouvert jusqu’au 20 juillet. Peu importe votre niveau en Python, vous pouvez proposer un sujet de sprint, de conférence ou d’atelier ! Venez parler de développement logiciel, de diversité, de communauté, faire un retour d’expérience sur un outil, présenter votre projet, un domaine d’activité…</p>
<p>Comme tous les ans, nous proposons aux personnes habituellement peu représentées en conférence de l’aide pour trouver un sujet, rédiger la proposition de conférence, rédiger le support de conférence et pour répéter. Vous pouvez nous contacter à l’adresse <a href="mailto:[email protected]">[email protected]</a> si vous pensez en avoir besoin.</p>
<p>Enfin, la PyConFR est entièrement financée par les sponsors. Si vous connaissez des sponsors potentiels, n’hésitez pas à leur parler de l’évènement !</p>
</div><div><a href="https://linuxfr.org/news/pyconfr-2025-a-lyon-du-30-octobre-au-2-novembre.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/139108/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/pyconfr-2025-a-lyon-du-30-octobre-au-2-novembre#comments">ouvrir dans le navigateur</a>
</p>
grewn0uilleBenoît Sibaudhttps://linuxfr.org/nodes/139108/comments.atomtag:linuxfr.org,2005:News/421592024-09-19T18:03:56+02:002024-09-19T18:03:56+02:00PyConFR 2024, planning et inscriptionsLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>La PyConFR 2024 a lieu du jeudi 31 octobre au dimanche 3 novembre à l’UFR Mathématique et d’Informatique de Strasbourg. Le planning est disponible et les inscriptions sont ouvertes !</p>
<p>Comme toujours, la PyConFR est un évènement gratuit et l’inscription est obligatoire.</p>
<p>Les deux premiers jours de la conférence seront occupés par les sprints. Et les deux jours suivants seront dédiés aux conférences (longues et courtes) et ateliers.</p>
<p>Trois <em>keynotes</em> sont également au programme :</p>
<ul>
<li>De villageoise à l’itWoman… Quelles actions pour faire de mon rêve TECH une réalité ?, par Houleymatou Baldé</li>
<li>Recherche des bonnes pratiques de packaging, par Françoise Conil</li>
<li>Reality is not an end-to-end prediction problem: Applied NLP in the age of Generative AI, par Ines Montani</li>
</ul>
<p>Cette année, un espace enfants (de 3 ans à 12 ans) est mis à disposition gratuitement sur inscription. Vous pouvez inscrire vos enfants jusqu’au 15 octobre.</p>
<p>Durant cette édition, c’est aussi le retour du déjeuner PyLadies. Un des objectifs est de tisser des liens entre la communauté PyLadies et le reste de la communauté Python francophone.<br>
Les inscriptions au déjeuner PyLadies sont ouvertes jusqu’au 27 octobre.</p>
<p>Le dimanche matin, l'AFP y tiendra son assemblée générale. Si vous souhaitez y voter, assurez vous d'être à jour de cotisation. </p>
</div><ul><li>lien nᵒ 1 : <a title="https://www.pycon.fr/2024" hreflang="fr" href="https://linuxfr.org/redirect/114417">PyConFR 2024</a></li><li>lien nᵒ 2 : <a title="https://www.pycon.fr/2024/fr/schedule.html" hreflang="fr" href="https://linuxfr.org/redirect/114418">Programme de l’évènement</a></li><li>lien nᵒ 3 : <a title="https://www.pycon.fr/2024/fr/schedule.html#register" hreflang="fr" href="https://linuxfr.org/redirect/114419">Inscriptions</a></li><li>lien nᵒ 4 : <a title="https://www.pycon.fr/2024/fr/conduct.html" hreflang="fr" href="https://linuxfr.org/redirect/114420">Code de conduite de la PyConFR</a></li><li>lien nᵒ 5 : <a title="https://www.afpy.org/" hreflang="fr" href="https://linuxfr.org/redirect/114421">Association Francophone Python (AFPy)</a></li><li>lien nᵒ 6 : <a title="https://www.pycon.fr/2024/fr/support.html" hreflang="fr" href="https://linuxfr.org/redirect/114422">Supporter l’évènement</a></li></ul><div></div><div><a href="https://linuxfr.org/news/pyconfr-2024-planning-et-inscriptions.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/136747/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/pyconfr-2024-planning-et-inscriptions#comments">ouvrir dans le navigateur</a>
</p>
grewn0uilleBenoît SibaudgUIhttps://linuxfr.org/nodes/136747/comments.atomtag:linuxfr.org,2005:News/420302024-06-09T10:18:30+02:002024-06-09T11:33:08+02:00Mise à jour de VenC en version 3.2.2Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p><img src="https://img.linuxfr.org/img/687474703a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f323031375f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e432d6c6f676f2e737667/2017_-_Denis_Salem_-_CC_By_SA_-_VenC-logo.svg" alt="VenC"></p>
<p><a href="https://venc.software">VenC</a>, un générateur libre (GPLv3) de site statique en python qui avait déjà été présenté <a href="//linuxfr.org/news/venc-3-1-1-un-nouveau-generateur-de-site-statique">dans une dépêche précédente</a>, est de retour en version 3.2.2 avec tout un tas de nouvelles fonctionnalités cool qu’on va voir ici.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://denissalem.tuxfamily.org/" hreflang="fr" href="https://linuxfr.org/redirect/113943">Site de l'auteur </a></li><li>lien nᵒ 2 : <a title="https://jeremyberry.org/" hreflang="fr" href="https://linuxfr.org/redirect/113944">Site de Jérémy Berry</a></li><li>lien nᵒ 3 : <a title="https://venc.software/" hreflang="fr" href="https://linuxfr.org/redirect/113945">Site du projet</a></li><li>lien nᵒ 4 : <a title="https://fr.tipeee.com/denissalem" hreflang="fr" href="https://linuxfr.org/redirect/113946">Tipeee </a></li><li>lien nᵒ 5 : <a title="https://liberapay.com/denissalem/" hreflang="fr" href="https://linuxfr.org/redirect/113947">Liberapay</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-auto-rafra%C3%AEchissement-lors-de-la-pr%C3%A9visualisation">Auto-rafraîchissement lors de la prévisualisation</a></li>
<li><a href="#toc-assignation-dun-th%C3%A8me-par-d%C3%A9faut">Assignation d’un thème par défaut</a></li>
<li>
<a href="#toc-am%C3%A9lioration-des-fonctionnalit%C3%A9s-ftp">Amélioration des fonctionnalités FTP</a><ul>
<li><a href="#toc-transfert-parall%C3%A8le">Transfert parallèle</a></li>
<li><a href="#toc-verbosit%C3%A9-accrue">Verbosité accrue</a></li>
<li><a href="#toc-transfert-intelligent">Transfert intelligent</a></li>
<li><a href="#toc-configuration-ftp-plus-fine">Configuration FTP plus fine</a></li>
</ul>
</li>
<li><a href="#toc-th%C3%A8me-par-d%C3%A9faut">Thème par défaut</a></li>
<li>
<a href="#toc-nouvelles-balises-venc">Nouvelles balises VenC</a><ul>
<li><a href="#toc-acc%C3%A8s-am%C3%A9lior%C3%A9-%C3%A0-des-m%C3%A9tadonn%C3%A9es-yaml">Accès amélioré à des métadonnées YAML</a></li>
<li><a href="#toc-acc%C3%A9der-%C3%A0-la-date-de-la-derni%C3%A8re-publication">Accéder à la date de la dernière publication</a></li>
<li><a href="#toc-taxonomie-avanc%C3%A9e">Taxonomie Avancée</a></li>
</ul>
</li>
<li><a href="#toc-le-mot-de-la-fin">Le mot de la fin</a></li>
</ul>
<h2 id="toc-auto-rafraîchissement-lors-de-la-prévisualisation">Auto-rafraîchissement lors de la prévisualisation</h2>
<p>Quand vous prévisualisez votre site avec <code>venc -s</code> vous n’avez plus besoin de tuer le processus du serveur HTTP local et de régénérer manuellement le site à chaque modification. Le mécanisme est à présent automatique et détecte lui-même un changement pour vous présenter la version à jour de votre contenu ! La cache du navigateur étant parfois capricieux, VenC vous notifie dans la sortie standard de ses actions si jamais vous avez un doute.</p>
<h2 id="toc-assignation-dun-thème-par-défaut">Assignation d’un thème par défaut</h2>
<p>Si vous utilisez le thème par défaut <code>concrete</code> ou tout autre thème installé en dehors de votre projet il est possible de l’indiquer dans votre <a href="https://venc.software/Fichier-de-configuration-principal/#default-theme">fichier de configuration</a>. Ainsi <code>venc -s</code> sait comment régénérer votre site si nécessaire, et vous n’avez plus besoin d’indiquer le nom du thème installé sur votre système lorsque vous utilisez la commande <code>venc -xb</code> ou <code>venc -xftp</code>.</p>
<h2 id="toc-amélioration-des-fonctionnalités-ftp">Amélioration des fonctionnalités FTP</h2>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032345f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e432d332e325f736d616c6c2e6a7067/2024_-_Denis_Salem_-_CC_By_SA_-_VenC-3.2_small.jpg" alt="Transfert en parallèle" title="Transfert en parallèle | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2024_-_Denis_Salem_-_CC_By_SA_-_VenC-3.2_small.jpg"></p>
<p>De nombreuses améliorations ont été apportées, en voici quelques-unes !</p>
<h3 id="toc-transfert-parallèle">Transfert parallèle</h3>
<p>Vous pouvez maintenant <a href="https://venc.software/Fichier-de-configuration-principal/#ftp-sessions">définir un nombre arbitraire de connexions FTP simultanées</a> pour le transfert de votre site en ligne. Pour un petit site, ça ne fait pas de grosse différence, mais quand comme moi votre site comporte plusieurs centaines de pages, ça fait gagner un temps fou !</p>
<h3 id="toc-verbosité-accrue">Verbosité accrue</h3>
<ul>
<li>Affichage du prompt FTP</li>
<li>Affichage des commandes FTP associées à leur numéro de session</li>
</ul>
<h3 id="toc-transfert-intelligent">Transfert intelligent</h3>
<p>Pour économiser de la bande passante, VenC compare les fichiers locaux avec ceux en ligne. Ce faisant, il ne réalise que les opérations d’I/O strictement nécessaires.</p>
<h3 id="toc-configuration-ftp-plus-fine">Configuration FTP plus fine</h3>
<p>Vous pouvez maintenant paramétrer les options suivantes :</p>
<ul>
<li>
<a href="https://venc.software/Fichier-de-configuration-principal/#ftp-encoding">ftp_encoding</a> : VenC affichant à présent le prompt FTP du serveur, il est maintenant possible d’avoir des informations sur celui-ci, et notamment l’encodage. Avec l’option <code>ftp_encoding</code>, il est donc possible de configurer l’encodage de la session pour être en phase avec le serveur distant et éviter de mauvaises surprises.</li>
<li>
<a href="https://venc.software/Fichier-de-configuration-principal/#ftp-sessions">ftp_sessions</a> : comme on l’a vu plus haut, c’est le nombre de connexions parallèles possibles.</li>
<li>
<a href="https://venc.software/Fichier-de-configuration-principal/#ftp-port">ftp_port</a> : avant, pour une raison obscure et injustifiable par le bon sens, le port de la connexion FTP était codé en dur… Maintenant c’est une valeur par défaut qu’il est possible de redéfinir.</li>
</ul>
<h2 id="toc-thème-par-défaut">Thème par défaut</h2>
<p>Le thème par défaut a été amélioré, notamment au niveau de l’ergonomie et de la qualité visuelle du thème. Ce nouveau thème intègre par ailleurs les nouvelles fonctionnalités de la version 3.2 !</p>
<h2 id="toc-nouvelles-balises-venc">Nouvelles balises VenC</h2>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032345f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e435f6d6963726f636f64655f736d616c6c2e6a7067/2024_-_Denis_Salem_-_CC_By_SA_-_VenC_microcode_small.jpg" alt="Microcode VenC" title="Microcode VenC | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2024_-_Denis_Salem_-_CC_By_SA_-_VenC_microcode_small.jpg"></p>
<h3 id="toc-accès-amélioré-à-des-métadonnées-yaml">Accès amélioré à des métadonnées YAML</h3>
<p>Une fonctionnalité ultérieurement manquante et utile dans certains cas d’usage est de pouvoir accéder à des valeurs qui sont imbriquées dans un ou plusieurs dictionnaires <a href="https://yaml.org/">YAML</a> dans le fichier de configuration principal ou dans les métadonnées d’une publication.</p>
<p>Pour ce faire, quatre fonctions VenC ont été ajoutés :</p>
<ul>
<li><a href="https://venc.software/Motifs-de-blog/#cherrypickblogmetadata">CherryPickBlogMetadata</a></li>
<li><a href="https://venc.software/Motifs-de-blog/#cherrypickblogmetadataifexists">CherryPickBlogMetadataIfExists</a></li>
<li><a href="https://venc.software/Motifs-de-publication/#cherrypickentrymetadata">CherryPickEntryMetadata</a></li>
<li><a href="https://venc.software/Motifs-de-publication/#cherrypickentrymetadataifexists">CherryPickEntryMetadataIfExists</a></li>
</ul>
<p>Pour les champs non obligatoires ou non prédéfinis par VenC, ça permet donc en conséquence une plus grande liberté dans l’organisation du contenu <a href="https://yaml.org/">YAML</a>.</p>
<h3 id="toc-accéder-à-la-date-de-la-dernière-publication">Accéder à la date de la dernière publication</h3>
<p>Utile pour la génération de flux Atom ou RSS, récupérer la date de la dernière publication d’un fil de publication permet d’indiquer la vraie date de mise à jour du flux :</p>
<p><a href="https://venc.software/Motifs-contextuels/#getlastentrytimestamp">GetLastEntryTimestamp</a></p>
<h3 id="toc-taxonomie-avancée">Taxonomie Avancée</h3>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032345f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e435f616476616e6365645f7461786f6e6f6d795f736d616c6c2e6a7067/2024_-_Denis_Salem_-_CC_By_SA_-_VenC_advanced_taxonomy_small.jpg" alt="Taxonomie Avancée" title="Taxonomie Avancée | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2024_-_Denis_Salem_-_CC_By_SA_-_VenC_advanced_taxonomy_small.jpg"></p>
<p>C’est la grosse fonctionnalité de cette mise à jour !</p>
<p>Avant la version 3.2 vous ne pouviez organiser vos publications “que” par catégories hiérarchisées. C’était déjà pas mal dans la mesure où ça permettait de créer des menus arborescents pour organiser votre contenu, mais parfois ça n’est pas suffisant. Par exemple, tout à fait au hasard, vous êtes un⋅e artiste et vous gérez votre portfolio avec VenC. Vous pourriez alors avoir un menu de la forme suivante pour organiser vos œuvres :</p>
<pre><code>- Peintures
- Huile
- Acrylique
- Aquarelle
- Dessins
- Encre
- Crayon
- Mediums Mixtes
</code></pre>
<p>Et comme vous êtes un⋅e bon⋅ne p'tit⋅e libriste auto-radicalisé⋅e sur <em>Framasoft</em> et <em>LinuxFr</em> vos œuvres sont en partie sous licence CC-By-SA. En partie seulement car oui, vous faites aussi des <em>fanarts</em>, des travaux dérivés d’œuvres sous licences propriétaires. Vous voulez donc séparer ce qui est véritablement libre de ce qui ne l’est pas. En générant par exemple ce type de menu :</p>
<pre><code># Mediums
- Peintures
- Huile
- Acrylique
- Aquarelle
- Dessins
- Encre
- Crayon
- Mixtes
# Licences
- CC
- By-NC-ND
- By-SA
- Copyright
- Licence Art Libre
</code></pre>
<p>Ça tombe bien, c’est précisément ce que permet maintenant de faire VenC 3.2.</p>
<p>L’idée est la suivante : puisque l’arbre de toutes les catégories construit à partir de toutes les publications est un arbre hiérarchisé, il suffit de sélectionner une ou plusieurs branches de cet arbre pour créer nos menus et présenter notre contenu de la façon qu’il nous plaira.</p>
<p>Dans notre exemple précédent l’arbre hierarchisé de toutes les catégories aurait donc la forme suivante :</p>
<pre><code>- Mediums:
- Peintures:
- Huile
- Acrylique
- Aquarelle
- Dessins:
- Encre
- Crayon
- Mixtes
- Licences:
- CC:
- By-NC-ND
- By-SA
- Copyright
- Licence Art Libre
</code></pre>
<p>Nous sélectionnons donc les branches <code>Mediums</code> et <code>Licences</code>. Avec les nouvelles fonctions VenC qui vont bien :</p>
<ul>
<li><a href="https://venc.software/Motifs-de-blog/#getblogcategoriestreefrombranches">GetBlogCategoriesTreeFromBranches</a></li>
<li><a href="https://venc.software/Motifs-de-blog/#getflattenedblogcategoriesfrombranches">GetFlattenedBlogCategoriesFromBranches</a></li>
<li><a href="https://venc.software/Motifs-de-publication/#getentrycategoriestreefrombranches">GetEntryCategoriesTreeFromBranches</a></li>
<li><a href="https://venc.software/Motifs-de-publication/#getflattenedentrycategoriesfrombranches">GetFlattenedEntryCategoriesFromBranches</a></li>
</ul>
<p>Sur le thème par défaut <code>concrete</code>, vous pouvez ainsi avoir un nombre arbitraire de menu de catégories en configurant comme indiqué dans la documentation votre fichier de configuration principal.</p>
<p>À noter que même si ça n’est pas encore idéal, ce système ouvre la voie pour créer des blogs multilingues. Le brainstorming sur le sujet se passe <a href="https://github.com/DenisSalem/VenC/issues/23">ici</a>. Pour l’heure, il est donc possible de tirer avantage de la taxonomie avancée pour créer des branches pour chaque langue que vous souhaitez utiliser. Avec un peu de JS vous pouvez aller plus loin pour rendre ça plus dynamique à votre convenance.</p>
<h2 id="toc-le-mot-de-la-fin">Le mot de la fin</h2>
<p>Voilà pour cette nouvelle version !</p>
<p>La version 3.3 est déjà en cours de développement et vous pouvez suivre l’aventure <a href="https://framagit.org/denissalem/VenC/-/issues/?label_name%5B%5D=Version%203.3">ici</a>. Un grand merci à <a href="">Sidoine</a>, Rollniak et Jérémy pour leur aide, leur confiance et leurs feedbacks !</p>
<p>Les sources <code>GIMP</code> des illustrations sont accessibles ici sous licences <a href="https://creativecommons.org/licenses/by-sa/4.0/deed.fr">CC-By-SA</a></p>
<ul>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2024_-_Denis_Salem_-_CC_By_SA_-_VenC-3.2.xcf">Enhanced FTP Transfert</a></li>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2024_-_Denis_Salem_-_CC_By_SA_-_VenC_advanced_taxonomy.xcf">Advanced Taxonomy</a></li>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2024_-_Denis_Salem_-_CC_By_SA_-_VenC_microcode.xcf">Microcode</a></li>
</ul>
<p>Toutes les contributions sont évidemment les bienvenues :</p>
<ul>
<li>Corriger, relire ou améliorer la documentation.</li>
<li>Signaler des bugs ou faire des suggestions.</li>
<li>Écrire du code.</li>
<li>Me faire aumône pour me payer un p'tit shot de Baileys Irish Cream !</li>
</ul>
</div><div><a href="https://linuxfr.org/news/mise-a-jour-de-venc-en-version-3-2-2.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/135869/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/mise-a-jour-de-venc-en-version-3-2-2#comments">ouvrir dans le navigateur</a>
</p>
denissalemBenoît Sibaudhttps://linuxfr.org/nodes/135869/comments.atomtag:linuxfr.org,2005:News/420022024-05-13T17:03:16+02:002024-05-21T18:24:57+02:00PyConFR 2024, du 31 octobre au 3 novembre à StrasbourgLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>L’Association Francophone Python (AFPy) organise la PyConFR 2024 du jeudi 31 octobre au dimanche 3 novembre. Pour cette 15<sup>e</sup> édition, nous sommes accueillis par l’UFR Mathématique et d’Informatique de Strasbourg (<a href="https://mathinfo.unistra.fr/">https://mathinfo.unistra.fr/</a>) !</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f7777772e7079636f6e2e66722f323032342f7374617469632f696d616765732f7079636f6e66722e737667/pyconfr.svg" alt="Logo PyConFr 2024" title="Source : https://www.pycon.fr/2024/static/images/pyconfr.svg"></p>
<p>Si vous ne connaissez pas la PyConFR, c’est un évènement gratuit sur 4 jours autour du langage de programmation Python. Tout d’abord, il y a deux jours de développements participatifs (sprints), puis deux jours de conférences et ateliers.</p>
<p>Vous pouvez proposer un sujet de sprint, de conférence ou d’atelier <strong>jusqu’au 21 juillet</strong>. Peu importe votre niveau en Python, n’hésitez pas à proposer un sujet ! Venez parler de développement logiciel, de diversité, de communauté, faire un retour d’expérience sur un outil, présenter votre projet, un domaine d’activité…</p>
<p>Dans un secteur aussi masculin que le développement informatique, nous proposons aux personnes habituellement peu représentées en conférence de l’aide pour trouver un sujet, rédiger la proposition de conférence, rédiger le support de conférence et pour répéter. Vous pouvez nous contacter à l’adresse <a href="mailto:[email protected]">[email protected]</a> si vous pensez en avoir besoin.</p>
<p>Enfin, la PyConFR est entièrement financée par les sponsors. Si vous connaissez des sponsors potentiels, n’hésitez pas à leur parler de l’évènement !</p>
</div><ul><li>lien nᵒ 1 : <a title="https://www.pycon.fr/" hreflang="fr" href="https://linuxfr.org/redirect/113820">PyConFR 2024</a></li><li>lien nᵒ 2 : <a title="https://www.afpy.org/" hreflang="fr" href="https://linuxfr.org/redirect/113821">Association Francophone Python (AFPy)</a></li><li>lien nᵒ 3 : <a title="https://cfp.pycon.fr/pyconfr-2024/cfp" hreflang="fr" href="https://linuxfr.org/redirect/113822">Proposer un sujet à la PyConFR 2024</a></li><li>lien nᵒ 4 : <a title="https://www.pycon.fr/2024/fr/conduct.html" hreflang="fr" href="https://linuxfr.org/redirect/113823">Code de conduite de la PyConFR</a></li><li>lien nᵒ 5 : <a title="https://www.pycon.fr/2024/fr/support.html" hreflang="fr" href="https://linuxfr.org/redirect/113824">Supporter l’évènement</a></li></ul><div></div><div><a href="https://linuxfr.org/news/pyconfr-2024-du-31-octobre-au-3-novembre-a-strasbourg.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/135659/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/pyconfr-2024-du-31-octobre-au-3-novembre-a-strasbourg#comments">ouvrir dans le navigateur</a>
</p>
grewn0uilleBenoît Sibaudhttps://linuxfr.org/nodes/135659/comments.atomtag:linuxfr.org,2005:News/418652024-02-01T10:21:43+01:002024-02-02T11:19:38+01:00VenC 3.1.1 | Un nouveau générateur de site statiqueLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p><img src="//img.linuxfr.org/img/687474703a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f323031375f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e432d6c6f676f2e737667/2017_-_Denis_Salem_-_CC_By_SA_-_VenC-logo.svg" alt="VenC" title="VenC | Source : http://download.tuxfamily.org/dsalem/img/2017_-_Denis_Salem_-_CC_By_SA_-_VenC-logo.svg"></p>
<p>On va présenter ici un <em>nouveau</em> générateur de site statique, écrit en python. Il n’est pas exactement nouveau au sens où nous en sommes à la version 3, mais jusqu’ici le projet est resté relativement confidentiel. Nous parlerons également des perspectives d’évolution de ce projet.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://venc.software/" hreflang="fr" href="https://linuxfr.org/redirect/113337">Site et documentation du projet VenC</a></li><li>lien nᵒ 2 : <a title="https://github.com/DenisSalem/VenC" hreflang="en" href="https://linuxfr.org/redirect/113338">Code source de VenC sur Github</a></li><li>lien nᵒ 3 : <a title="https://framagit.org/denissalem/VenC/" hreflang="en" href="https://linuxfr.org/redirect/113339">Code source de VenC sur Framagit</a></li><li>lien nᵒ 4 : <a title="https://github.com/miyuchina/mistletoe" hreflang="en" href="https://linuxfr.org/redirect/113340">Mistletoe</a></li><li>lien nᵒ 5 : <a title="http://docutils.sourceforge.net/" hreflang="en" href="https://linuxfr.org/redirect/113341">Docutils</a></li><li>lien nᵒ 6 : <a title="https://github.com/roniemartinez/latex2mathml" hreflang="en" href="https://linuxfr.org/redirect/113342">Latex2MathML</a></li><li>lien nᵒ 7 : <a title="https://pygments.org/" hreflang="en" href="https://linuxfr.org/redirect/113343">Pygments</a></li><li>lien nᵒ 8 : <a title="https://asciidoc3.org/" hreflang="en" href="https://linuxfr.org/redirect/113344">AsciiDoc3</a></li><li>lien nᵒ 9 : <a title="https://denissalem.tuxfamily.org/" hreflang="fr" href="https://linuxfr.org/redirect/113345">Site de l'auteur</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-introduction-et-petit-historique">Introduction et petit historique</a></li>
<li><a href="#toc-que-permet-de-faire-venc">Que permet de faire VenC ?</a></li>
<li><a href="#toc-faster-smaller-stronger">Faster, smaller, stronger</a></li>
<li>
<a href="#toc-travaux-et-r%C3%A9flexion-en-cours-le-futur-de-lapplication">Travaux et réflexion en cours, le futur de l’application</a><ul>
<li><a href="#toc-le-moteur-de-recherche-client-side">Le moteur de recherche client-side.</a></li>
<li><a href="#toc-int%C3%A9gration-avec-le-fediverse">Intégration avec le Fediverse</a></li>
<li><a href="#toc-sites-statiques-deepweb-et-smolweb">Sites statiques, deepweb et smolweb</a></li>
<li><a href="#toc-et-le-format-epub">Et le format EPUB ?</a></li>
<li><a href="#toc-internationalisation-des-articles">Internationalisation des articles</a></li>
<li>
<a href="#toc-fonctionnalit%C3%A9s-de-base-manquantes">Fonctionnalités de base manquantes</a><ul>
<li><a href="#toc-g%C3%A9n%C3%A9ration-incr%C3%A9mentale-et-mise-en-cache">Génération incrémentale et mise en cache.</a></li>
<li><a href="#toc-auto-rafra%C3%AEchissement-lors-de-la-pr%C3%A9visualisation">Auto-rafraîchissement lors de la prévisualisation</a></li>
<li><a href="#toc-ajout-de-modules-tiers-%C3%A9crit-par-les-utilisateurs-eux-m%C3%AAmes">Ajout de modules tiers écrit par les utilisateurs eux-mêmes</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#toc-le-mot-de-la-fin">Le mot de la fin</a></li>
<li>
<a href="#toc-ressources-du-projet">Ressources du projet</a><ul>
<li><a href="#toc-illustrations">Illustrations</a></li>
<li><a href="#toc-modules-optionnels">Modules optionnels</a></li>
</ul>
</li>
</ul>
<h2 id="toc-introduction-et-petit-historique">Introduction et petit historique</h2>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032315f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e435f696e74726f647563696e675f736d616c6c2e6a7067/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_introducing_small.jpg" alt="Introducing VenC" title="Introducing VenC | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_introducing_small.jpg"></p>
<p>VenC est un <s>énième</s> générateur de site statique (libre, évidemment !) dont le nom étrange provient d’un rêve dans lequel je naviguais sur un réseau social appelé « V en C ». Ce rêve, ainsi que la forme et le contenu de ce réseau, m’avaient marqué. Rétrospectivement, ça ressemblait un peu aux premières heures glorieuses de Tumblr / Skyblog avec un petit côté web oldschool.</p>
<p>De gros projets historiques sont déjà bien installés dans le game. Par ailleurs, si j’en crois le nombre d’annonces de logiciels de ce type ici, ça ressemble un peu à un running gag d’arriver avec ma proposition !</p>
<p>Je tenais quand même à présenter l’ouvrage, car il s’agit d’un projet qui me tient toujours beaucoup à cœur et avec lequel j’ai notamment appris le langage Python et pour lequel je continue de progresser. Je l’ai commencé au début des années 2010 précisément quand on voyait émerger ce type d’outil, pour une approche plus “lightweight” du web. Ça n’est que récemment que j’ai ambitionné de rendre ce projet public.</p>
<p>La version 2 est la première que j’ai partagée sur mes réseaux sociaux (<a href="https://diasp.eu/u/denissalem">Diaspora*</a> et <a href="https://mamot.fr/@denissalem">Mastodon</a>). C’était un peu la bêta de la version actuelle, où de grandes restructurations de code avaient été réalisées, fort d’une plus grande expérience en Python. Ça ne m’a pas empêché d’introduire de la dette technique corrigée plus tard, et dans sa plus grande partie je l’espère, dans la version actuelle. J’avais également ajouté une fonctionnalité un peu expérimentale, dont on reparlera plus bas.</p>
<p>Globalement la v1 et la v2 sont restées assez confidentielles. Trop de bugs, pas correctement documentées et pas assez adaptées aux multiples et imprévisibles usages que peuvent avoir les utilisateurs finaux. Certaines questions de choix technico-philosophiques restées en suspens sont encore d’actualité et doivent être décidées par ceux qui l’utilisent. En d’autres termes, je souhaite élargir les possibilités qu’offre VenC en l’adaptant aux besoins des autres, et non plus seulement aux miens.</p>
<p>La version 3.1.0 a été mise en ligne le 30 décembre 2023 et j’estime le projet suffisamment mature (en toute modestie) pour être partagé avec vous. Mon idée étant de proposer l’existant, et de réfléchir avec vous avec les perspectives que j’ai en tête ou que vous pourriez vouloir voir implémentées ! C’est également l’occasion pour moi de bénéficier d’un éventuel retour d’expérience, d’une relecture de code ou de la documentation, et améliorer l’existant.</p>
<h2 id="toc-que-permet-de-faire-venc">Que permet de faire VenC ?</h2>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032315f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e435f437265617465426c6f67496e466976654d696e757465735f736d616c6c2e6a7067/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_CreateBlogInFiveMinutes_small.jpg" alt="Create blog in five minutes !" title="Create blog in five minute! | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_CreateBlogInFiveMinutes_small.jpg"></p>
<p>Il s’agit donc d’un générateur de site statique orienté blog proposant les fonctionnalités suivantes :</p>
<ul>
<li>Des balises VenC permettant une mise en page et une intégration de contenus avancés.</li>
<li>La possibilité de créer un agencement de publication en nombre arbitraire de colonnes.</li>
<li>Un module et une API JavaScript de défilement infini.</li>
<li>Les publications peuvent être organisées par catégories, sous-catégories, par période de dates et par chapitres.</li>
<li>La possibilité de désactiver des fils de publications spécifiques.</li>
<li>La possibilité de configurer des sous-répertoires pour chaque type de publications.</li>
<li>La possibilité d’ajouter des métadonnées au blog et aux publications.</li>
<li>Les publications sont triées par ordre chronologique par défaut, mais peuvent l’être aussi en fonction de métadonnées. L’ordre peut être ascendant ou descendant.</li>
<li>La génération de flux Atom et/ou RSS pour chaque fils de publications.</li>
<li>Des permaliens.</li>
<li>Un serveur HTTP simple pour effectuer des tests et prévisualiser le site.</li>
<li>La gestion et l’édition du blog peuvent être faites entièrement en ligne de commande, dans un environnement non graphique.</li>
<li>Le support de Markdown, reStructuredText, AsciiDoc.</li>
<li>La mise en ligne du site peut se faire en FTP via VenC.</li>
<li>Le support de pygmentize pour la coloration syntaxique.</li>
<li>Le support de l’API oEmbed.</li>
<li>Le support de l’API Kroki.</li>
<li>Le support de contenus audio et vidéo via les balises VenC.</li>
<li>Installation facilitée via pip et pipx.</li>
</ul>
<h2 id="toc-faster-smaller-stronger">Faster, smaller, stronger</h2>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032315f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e435f496e7374616c6c5f50617274795f736d616c6c2e6a7067/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_Install_Party_small.jpg" alt="VenC Install Party" title="VenC Install Party | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_Install_Party_small.jpg"></p>
<p>À noter qu’ayant à cœur l’optimisation et la performance, l’essentiel du travail réalisé sur cette version 3.1.0 à été l’intégration d’un mode de génération parallèle pour accélérer l’exportation de votre projet. En fait sur ce dernier point, l’ambition est de faire en sorte que VenC puisse passer à l’échelle en tirant parti <s>des threads Python</s> du module multiprocessing de Python. Nous en reparlerons plus bas.</p>
<p>J’ai passé de longues heures à optimiser l’ensemble du code comme je l’avais déjà fait entre la v1 et la v2. Mon propre site contenant énormément de pages, j’ai besoin d’une application rapide et simple pour générer celui-ci. Il y a quelque chose de très satisfaisant à travailler en ce sens. Tout en mesurant les limites de la chose et en découvrant qu’un excès de zèle rend le code incompréhensible bien sûr… Avec cette v3 je pense avoir atteint un équilibre. En termes de rapidité, la force de VenC réside aussi dans le fait qu’il possède sa propre syntaxe de balisage pour la mise en forme (pas de Jinja donc). Le moteur de template étant ici taillé sur mesure et fortement couplé à l’application, ça tourne très très vite ! Voir la <a href="https://venc.software/Pattern-Processor/">documentation de la syntaxe VenC</a>.</p>
<p>Si jamais ça vous intéresse, il est envisageable de découpler cette partie spécifique du code pour être utilisé ailleurs que dans VenC.</p>
<h2 id="toc-travaux-et-réflexion-en-cours-le-futur-de-lapplication">Travaux et réflexion en cours, le futur de l’application</h2>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032315f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e435f5475746f7269616c735f736d616c6c2e6a7067/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_Tutorials_small.jpg" alt="Learning VenC" title="Learning VenC | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_Tutorials_small.jpg"></p>
<h3 id="toc-le-moteur-de-recherche-client-side">Le moteur de recherche client-side.</h3>
<p>Dans la v2 j’avais introduit une fonctionnalité expérimentale permettant de générer des documents JSON-LD au format JSON ou <a href="https://www.ibm.com/docs/fr/integration-bus/10.1?topic=domain-jsonp-support-in-json">JSONP</a>. Cette fonctionnalité a été retirée depuis, parce que je ne suis plus sûr de la pertinence de cette technique pour le projet initial : celui d’implémenter un moteur de recherche client-side décentralisé. Ce moteur de recherche devait permettre de chercher du contenu sur un site, mais aussi sur celui des amis, pour lesquels les <em>end-points</em> seraient manuellement ajoutés par le propriétaire du site. Ce faisant, cela permettrait de créer un réseau de site qu’il est possible de <em>crawler</em> pour trouver du contenu.</p>
<p>Il se pourrait que JSON-LD ne soit en fait pas le meilleur moyen pour ce genre d’usage. Mais je n’en sais trop rien, qu’en pensez-vous ?</p>
<h3 id="toc-intégration-avec-le-fediverse">Intégration avec le Fediverse</h3>
<p>Un truc que j’ai toujours trouvé chouette avec Tumblr, c’est le fait d’avoir une page hautement personnalisable qui fait néanmoins partie d’un tout qu’il est possible d’explorer avec le moteur de recherche interne du service. En ce sens, cette idée rejoint celle concernant le moteur de recherche client-side décentralisé.</p>
<p>Avec l’émergence de Fediverse, ça serait en fait intéressant de voir comment il est possible d’intégrer VenC à ce type de réseau. Mon idée initiale était de créer une surcouche de VenC, une sorte de serveur, avec un frontend web, gérant plusieurs sites et capable de répondre dynamiquement à des requêtes des APIs du Fediverse. On comprend ici qu’un tel serveur pourrait avoir à gérer des dizaines, voire des centaines de sites, le parallélisme fraîchement implémenté dans la v3 prend tout son sens.</p>
<p>La question de comment explorer du contenu gérer par VenC de façon décentralisé, modulaire, et interopérable étant le grand axe d’évolution de VenC qui me tient à cœur. Je souhaite recréer ce réseau dont j’avais rêvé il y a longtemps.</p>
<h3 id="toc-sites-statiques-deepweb-et-smolweb">Sites statiques, deepweb et smolweb</h3>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032305f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e435f69735f636f6d706163745f736d616c6c2e706e67/2020_-_Denis_Salem_-_CC_By_SA_-_VenC_is_compact_small.png" alt="VenC Is Small" title="VenC Is Small | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2020_-_Denis_Salem_-_CC_By_SA_-_VenC_is_compact_small.png"></p>
<p>Le smolweb, c’est vraiment cool. Par certains aspects il ressemble aussi un peu au deepweb, lui aussi très intéressant pour d’autres raisons. Les propositions de ces réseaux étant toujours plus d’actualités à mesure que les années passent…</p>
<p>De ce que j’ai pu voir du deepweb, beaucoup de sites sont en fait statiques et d’apparence très sobre, ce qui ne manquera pas de faire vibrer la fibre nostalgique de ceux qui ont connu le web des années 1990 et 2000. Quelle époque ! Dans les deux types de réseau on retrouve deux aspects qui me tiennent à cœur, le green IT pour l’un et la vie privée pour l’autre. Des valeurs dans lesquelles je me retrouve évidemment, et pour lesquelles je souhaiterais que VenC apporte sa pierre à l’édifice.</p>
<p>Une des perspectives d’évolution de VenC serait de pouvoir faire en sorte qu’il puisse générer des sites spécifiquement pour les différents types de smolweb : Gopher, Gemini… Mais je ne connais pas bien le fonctionnement de ceux-là, c’est donc encore à l’étude.</p>
<h3 id="toc-et-le-format-epub">Et le format EPUB ?</h3>
<p>Puisque VenC gère le chapitrage de son contenu, ça pourrait avoir du sens de pouvoir générer des documents EPUB. Et comme ce format est en fait du contenu HTML embarqué dans un zip… Il n’y a plus qu’à étudier les spécifications du format, et au boulot. Mais peut-être qu’il existe déjà un module ou package Python pour ce genre de chose ? Tout ça est aussi à l’étude.</p>
<h3 id="toc-internationalisation-des-articles">Internationalisation des articles</h3>
<p>Le besoin étant d’avoir plusieurs versions d’un même article dans des langues différentes, il y a plusieurs façons de réaliser ça… Ça n’est donc pas une feature triviale.</p>
<p>Ça pourrait m’aider de me parler un peu de la façon dont vous voyez la chose pour un blog statique.</p>
<h3 id="toc-fonctionnalités-de-base-manquantes">Fonctionnalités de base manquantes</h3>
<p>La plupart des générateurs de sites statiques ont cette fonctionnalité, mais pas VenC. Pas encore en tout cas, mais c’est dans les tuyaux !</p>
<h4 id="toc-génération-incrémentale-et-mise-en-cache">Génération incrémentale et mise en cache.</h4>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032305f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e435f69735f666173745f736d616c6c2e706e67/2020_-_Denis_Salem_-_CC_By_SA_-_VenC_is_fast_small.png" alt="VenC Is Fast" title="VenC Is Fast | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2020_-_Denis_Salem_-_CC_By_SA_-_VenC_is_fast_small.png"></p>
<p>Au lieu de régénérer tout le site à chaque modification, VenC ne devrait modifier que le strict nécessaire. Une mise en cache des pages à certaines étapes de la génération du site pourrait être mise en œuvre pour accélérer le traitement.</p>
<h4 id="toc-auto-rafraîchissement-lors-de-la-prévisualisation">Auto-rafraîchissement lors de la prévisualisation</h4>
<p>Cette fonctionnalité va de pair avec la précédente, lorsque le site est prévisualisé il devrait s’auto-régénérer quand une modification est faite.</p>
<h4 id="toc-ajout-de-modules-tiers-écrit-par-les-utilisateurs-eux-mêmes">Ajout de modules tiers écrit par les utilisateurs eux-mêmes</h4>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646f776e6c6f61642e74757866616d696c792e6f72672f6473616c656d2f696d672f56656e435f496c6c757374726174696f6e735f736d616c6c2f323032315f2d5f44656e69735f53616c656d5f2d5f43435f42795f53415f2d5f56656e435f69735f7772697474656e5f696e5f707974686f6e5f736d616c6c2e706e67/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_is_written_in_python_small.png" alt="VenC Is Written In Python" title="VenC Is Written In Python | Source : https://download.tuxfamily.org/dsalem/img/VenC_Illustrations_small/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_is_written_in_python_small.png"></p>
<p>Pour le moment VenC n’a pas de greffons autres que ceux déjà prévus par l’application, mais les futures versions bénéficieront d’une API permettant d’écrire vos propres fonctionnalités.</p>
<h2 id="toc-le-mot-de-la-fin">Le mot de la fin</h2>
<p>J’ai à cœur de rendre ce logiciel accessible dans d’autres langues, pour la traduction de la documentation et du logiciel lui-même, je cherche des personnes pouvant justifier d’une capacité professionnelle de traduction dans une langue ou une ou autre. L’anglais et l’allemand seraient par exemple un bon début. Ce travail serait naturellement rémunéré. N’hésitez pas à me contacter si vous pensez avoir la compétence requise pour que nous en discutions.</p>
<p>Cette v3 a demandé beaucoup d’efforts, j’espère que ce logiciel trouvera ses utilisateurs ! J’en profite pour remercier les contributeurs qui croient en ce projet et notamment Jérémy Berry pour ses conseils et sa précieuse relecture, Sidoine Baratte qui suit le projet depuis le début et avec qui j’ai affronté les bugs ainsi que benben962 pour sa traduction en anglais de l’ancienne documentation !</p>
<h2 id="toc-ressources-du-projet">Ressources du projet</h2>
<h3 id="toc-illustrations">Illustrations</h3>
<p>Les sources des illustrations :</p>
<ul>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2021_-_Denis_Salem_-_CC_By_SA_-_venc-install-process.xcf">VenC Install Process</a></li>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_tutorials.xcf">VenC Tutorials</a></li>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_CreateBlogInFiveMinutes.xcf">Create Blog In Five Minutes</a></li>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_introducing.xcf">VenC Introducing</a></li>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2020_-_Denis_Salem_-_CC_By_SA_-_VenC_is_fast.xcf">VenC Is Fast</a></li>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2020_-_Denis_Salem_-_CC_By_SA_-_VenC_is_compact.xcf">VenC Is Compact</a></li>
<li><a href="https://download.tuxfamily.org/dsalem/xcf/2021_-_Denis_Salem_-_CC_By_SA_-_VenC_is_written_in_python.xcf">VenC Is Written In Python</a></li>
</ul>
<h3 id="toc-modules-optionnels">Modules optionnels</h3>
<ul>
<li>
<a href="https://github.com/miyuchina/mistletoe">Mistletoe</a> : Pour l’utilisation de la syntaxe Markdown.</li>
<li>
<a href="http://docutils.sourceforge.net/">Docutils</a> : Pour utiliser la syntax reStructuredText.</li>
<li>
<a href="https://github.com/roniemartinez/latex2mathml">Latex2MathML</a> : Pour convertir du LaTeX en MathML.</li>
<li>
<a href="https://pygments.org/">Pygments</a> : Pour la coloration syntaxique.</li>
<li>
<a href="https://asciidoc3.org/">AsciiDoc3</a> : Pour utiliser la syntaxe Asciidoc</li>
</ul>
</div><div><a href="https://linuxfr.org/news/venc-3-1-1-un-nouveau-generateur-de-site-statique.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/134728/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/venc-3-1-1-un-nouveau-generateur-de-site-statique#comments">ouvrir dans le navigateur</a>
</p>
denissalemYsabeau 🧶orfenorBenoît Sibaudhttps://linuxfr.org/nodes/134728/comments.atomtag:linuxfr.org,2005:News/418122023-12-21T10:09:58+01:002023-12-22T08:44:25+01:00L'installation et la distribution de paquets Python (2/4)Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Cette dépêche est la deuxième d’une série de quatre sur le <em>packaging</em> en Python :</p>
<ol>
<li><a href="//linuxfr.org/news/l-installation-et-la-distribution-de-paquets-python-1-4">L’histoire du <em>packaging</em> Python</a></li>
<li><strong>Tour de l’écosystème actuel</strong></li>
<li>Le casse-tête du code compilé</li>
<li>La structure de la communauté en question</li>
</ol>
<p>Je vais donc proposer un aperçu plus ou moins complet des différents outils, et de ce qu’ils font ou ne font pas, en essayant de les comparer. Mais je parlerai aussi des fichiers de configuration, des dépôts où les paquets sont publiés, des manières d’installer Python lui-même, et de l’interaction de tout ceci avec les distributions Linux. En revanche, je laisse de côté pour l’instant les paquets écrits en C, C++ ou Rust et la complexité qu’ils apportent.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://packaging.python.org/" hreflang="en" href="https://linuxfr.org/redirect/113136">Guide officiel du packaging Python</a></li><li>lien nᵒ 2 : <a title="https://packaging.python.org/en/latest/key_projects/" hreflang="en" href="https://linuxfr.org/redirect/113137">Liste de projets (sur ce même guide)</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#toc-pip"></a><a href="https://pip.pypa.io">Pip</a><ul>
<li><a href="#toc-les-dangers-de-pip-sous-linux">Les dangers de pip sous Linux</a></li>
<li><a href="#toc-pip-est-un-outil-de-bas-niveau">Pip est un outil de bas niveau</a></li>
</ul>
</li>
<li>
<a href="#toc-les-environnements-virtuels-venv-virtualenv-pipx">Les environnements virtuels : </a><a href="https://docs.python.org/3/library/venv.html">venv</a>, <a href="https://virtualenv.pypa.io/">virtualenv</a>, <a href="https://pypa.github.io/pipx">pipx</a>
</li>
<li>
<a href="#toc-linvocation-dun-build-backend-build">L’invocation d’un build backend : </a><a href="https://pypa-build.readthedocs.io">build</a>
</li>
<li>
<a href="#toc-le-transfert-sur-pypi-twine">Le transfert sur PyPI : </a><a href="https://twine.readthedocs.io">twine</a>
</li>
<li><a href="#toc-la-configuration-dun-projet-le-fichier-pyprojecttoml">La configuration d’un projet : le fichier <code>pyproject.toml</code></a></li>
<li>
<a href="#toc-les-build-backends-pour-code-python-pur">Les build backends pour code Python pur</a><ul>
<li>
<a href="#toc-setuptools"></a><a href="https://setuptools.pypa.io">setuptools</a>
</li>
<li>
<a href="#toc-flit"></a><a href="https://flit.pypa.io">Flit</a>
</li>
<li>
<a href="#toc-hatchling"></a><a href="https://hatch.pypa.io">Hatchling</a>
</li>
<li>
<a href="#toc-pdm-backend"></a><a href="https://backend.pdm-project.org/">PDM-Backend</a>
</li>
<li>
<a href="#toc-poetry-core"></a><a href="https://python-poetry.org/docs/pyproject/">Poetry-core</a>
</li>
</ul>
</li>
<li>
<a href="#toc-la-gestion-des-versions-de-python-pyenv">La gestion des versions de Python : </a><a href="https://github.com/pyenv/pyenv">pyenv</a>
</li>
<li>
<a href="#toc-un-outil-de-test-et-dautomatisation-tox">Un outil de test et d’automatisation : </a><a href="https://tox.wiki">tox</a>
</li>
<li><a href="#toc-interlude-vous-avez-dit-lock-file">Interlude : vous avez dit lock file?</a></li>
<li>
<a href="#toc-un-gestionnaire-de-lock-file-pip-tools">Un gestionnaire de lock file : </a><a href="https://pip-tools.readthedocs.io">pip-tools</a>
</li>
<li>
<a href="#toc-les-outils-tout-en-un">Les outils « tout-en-un »</a><ul>
<li>
<a href="#toc-poetry"></a><a href="https://python-poetry.org">Poetry</a>
</li>
<li>
<a href="#toc-pdm"></a><a href="https://pdm-project.org">PDM</a>
</li>
<li>
<a href="#toc-hatch"></a><a href="https://hatch.pypa.io">Hatch</a>
</li>
</ul>
</li>
<li><a href="#toc-la-cr%C3%A9ation-dex%C3%A9cutables-ind%C3%A9pendants-et-installeurs-pyinstaller-cx_freeze-briefcase-pyoxidizer-etc">La création d’exécutables indépendants et installeurs : PyInstaller, cx_freeze, briefcase, PyOxidizer (etc.)</a></li>
<li><a href="#toc-conda-un-univers-parall%C3%A8le">Conda, un univers parallèle</a></li>
<li><a href="#toc-petite-comparaison-des-r%C3%A9solveurs-de-d%C3%A9pendances">Petite comparaison des résolveurs de dépendances</a></li>
<li><a href="#toc-conclusion-et-avis-personnels">Conclusion et avis personnels</a></li>
</ul>
<p>Commençons par un outil dont à peu près tout utilisateur de Python a entendu parler : pip.</p>
<h2 id="toc-pip"><a href="https://pip.pypa.io">Pip</a></h2>
<p>L’installeur de paquets pip est un outil fondamental et omniprésent. Son nom est un acronyme récursif signifiant « Pip Installs Packages ». Il permet d’installer un paquet depuis le PyPI, mais aussi depuis une copie locale du code source, ou encore depuis un dépôt Git. Il dispose, bien sûr, d’un résolveur de dépendances pour trouver des versions à installer qui soient compatibles entre elles. Il possède aussi un certain nombre de fonctionnalités standard pour un gestionnaire de paquets, comme désinstaller un paquet, lister les paquets installés, etc.</p>
<p>S’il y a un outil de packaging quasi universel, c’est bien pip. Par exemple, la page de chaque paquet sur PyPI (<a href="https://pypi.org/project/numpy">exemple</a>) affiche une commande pour l’installer, à savoir <code>pip install <paquet></code>. Quand la documentation d’un paquet donne des instructions d’installation, elles utilisent généralement pip.</p>
<p>De plus, la distribution officielle de Python permet de <em>boostraper</em> très simplement pip avec la commande <code>python -m ensurepip</code>. Cela rend pip très facile à installer, et lui donne un caractère officiel, soutenu par les développeurs de Python, caractère que n’ont pas la plupart des autres outils que je vais mentionner.</p>
<p>Même les autres outils qui installent aussi des paquets depuis le PyPI (comme pipx, Hatch, tox, etc.) le font presque tous en utilisant, en interne, pip (sauf Poetry qui est un peu à part).</p>
<p>Dans l’univers parallèle de Conda et Anaconda, les utilisateurs sont souvent obligés d’utiliser pip dans un environnement Conda parce qu’un paquet donné n’est pas disponible au format Conda (ce qui crée, d’ailleurs, des problèmes de compatibilité, mais c’est un autre sujet).</p>
<h3 id="toc-les-dangers-de-pip-sous-linux">Les dangers de pip sous Linux</h3>
<p>Malheureusement, sous Linux spécifiquement, l’interface en ligne de commande de pip a longtemps été un moyen très facile de se tirer une balle dans le pied. En effet, la commande simple</p>
<pre><code class="shell">pip install <paquet></code></pre>
<p>tentait d’installer le paquet au niveau du système, de manière visible pour tous les utilisateurs de la machine (typiquement dans <code>/usr/lib/pythonX.Y/site-packages/</code>). Bien sûr, il faut des permissions pour cela. Que fait Monsieur Toutlemonde quand il voit « permission denied error » ? Élémentaire, mon cher Watson :</p>
<pre><code class="shell">sudo pip install <paquet></code></pre>
<p>Or, sous Linux, installer des paquets avec pip au niveau du système, c’est mal. Je répète : c’est <strong>MAL</strong>. Ou plutôt, c’est valable dans 0,1% des cas et dangereux dans 99,9% des cas.</p>
<p>J’insiste : ne faites <strong>JAMAIS</strong> <code>sudo pip install</code> ou <code>sudo pip uninstall</code>. (Sauf si vous savez <em>parfaitement</em> ce que vous faites et que vous avez scrupuleusement vérifié qu’il n’y a aucun conflit.)</p>
<p>Le souci ? Les distributions Linux contiennent, elles aussi, des paquets écrits en Python, qui sont installés au même endroit que celui dans lequel installe la commande <code>sudo pip install</code>. Pip peut donc écraser un paquet installé par le système avec une version différente du même paquet, potentiellement incompatible avec le reste, ce qui peut avoir des conséquences catastrophiques. Il suffit de penser que DNF, le gestionnaire de paquets de Fedora, est écrit en Python, pour avoir une idée des dégâts potentiels !</p>
<p>Aujourd’hui, heureusement, la commande <code>pip install <paquet></code> (sans <code>sudo</code>), au lieu d’échouer avec une erreur de permissions, installe par défaut dans un emplacement spécifique à l’utilisateur, typiquement <code>~/.local/lib/pythonX.Y/site-packages/</code> (ce qui devait auparavant se faire avec <code>pip install --user <paquet></code>, l’option <code>--user</code> restant disponible si on veut être explicite). De plus, pip émet un avertissement sous Linux lorsqu’exécuté avec les droits root (<a href="https://github.com/pypa/pip/pull/9394">source</a>). Ainsi, <code>pip install <paquet></code> est devenu beaucoup moins dangereux.</p>
<p>Attention, j’ai bien dit <em>moins</em> dangereux… mais dangereux quand même ! Pourquoi, s’il n’efface plus les paquets du système ? Parce que si un paquet est installé à la fois par le système, et par pip au niveau de l’utilisateur, la version de pip va prendre le dessus, car le dossier utilisateur a priorité sur le dossier système. Le résultat est que le conflit, en réalité, persiste : il reste possible de casser un paquet système en installant une version incompatible avec pip au niveau utilisateur. Seulement, c’est beaucoup plus facile à corriger (il suffit d’un <code>rm -rf ~/.local/lib/pythonX.Y/site-packages/*</code>, alors qu’un conflit dans le dossier système peut être quasi irréparable).</p>
<p>La seule option qui soit sans danger est de ne jamais rien installer en dehors d’un environnement virtuel (voir plus bas pour les instructions).</p>
<p>Pour finir, la <a href="https://peps.python.org/668">PEP 668</a> a créé un mécanisme pour qu’une distribution Linux puisse marquer les dossiers de paquets Python qu’elle contrôle. Pip refuse (par défaut) de modifier ces dossiers et affiche un avertissement qui mentionne les environnements virtuels. Debian (à partir de Debian Bookworm), Ubuntu (à partir d’Ubuntu Lunar) et d’autres distributions Linux, ont choisi de mettre en place cette protection. Donc, désormais, <code>sudo</code> ou pas, <code>pip install</code> en dehors d’un environnement virtuel donne une erreur (on peut forcer l’opération avec l’option <code>--break-system-packages</code>).</p>
<p>En revanche, Fedora <a href="https://discussion.fedoraproject.org/t/status-of-marking-the-base-python-environment-as-externally-managed-pep-668">n’a pas implémenté la protection</a>, espérant réussir à créer un dossier pour pip qui soit au niveau système mais séparé du dossier de la distribution Linux, pour que <code>pip install</code> soit complètement sûr et qu’il n’y ait pas besoin de cette protection. Je recommande <a href="https://www.youtube.com/watch?v=7SBH4PkbMhw">la présentation de Miro Hrončok</a> à la conférence PyCon polonaise en janvier 2023, qui explique le casse-tête dans les menus détails. Petite citation en avant-goût : « The fix is quite trivial when you design it, and it only strikes back when you actually try to do it ».</p>
<h3 id="toc-pip-est-un-outil-de-bas-niveau">Pip est un outil de bas niveau</h3>
<p>Pip a une autre chausse-trappe qui est surprenant quand on est habitué au gestionnaire de paquets d’une distribution Linux. Petite illustration :</p>
<pre><code class="text">$ python -m venv my-venv/ # crée un environnement isolé vide pour la démonstration
$ source my-venv/bin/activate # active l’environnement
$ pip install myst-parser
[...]
Successfully installed MarkupSafe-2.1.3 Pygments-2.16.1 alabaster-0.7.13 [...]
[...]
$ pip install mdformat-deflist
[...]
Installing collected packages: markdown-it-py, mdit-py-plugins, mdformat, mdformat-deflist [...]
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
myst-parser 2.0.0 requires markdown-it-py~=3.0, but you have markdown-it-py 2.2.0 which is incompatible.
myst-parser 2.0.0 requires mdit-py-plugins~=0.4, but you have mdit-py-plugins 0.3.5 which is incompatible.
Successfully installed markdown-it-py-2.2.0 mdformat-0.7.17 mdformat-deflist-0.1.2 mdit-py-plugins-0.3.5 [...]
$ echo $?
0</code></pre>
<p>Comme on peut le voir, <em>la résolution des dépendances par pip ne prend pas en compte les paquets déjà installés dans l’environnement</em>. Autrement dit, pour installer un paquet X, pip va simplement regarder quelles sont les dépendances de X (y compris les dépendances transitives), trouver un ensemble de versions qui soient compatibles entre elles, et les installer. Pip ne vérifie pas que les versions des paquets sont aussi compatibles avec ceux qui sont déjà installés. Ou plutôt, il les vérifie, mais seulement <em>après</em> avoir fait l’installation, à un moment où le mal est déjà fait, et uniquement pour afficher un avertissement. Dans l’exemple ci-dessus, on installe d’abord <code>myst-parser</code>, dont la dernière version dépend de <code>markdown-it-py</code> version 3.x, puis on installe <code>mdformat-deflist</code>, qui dépend de <code>markdown-it-py</code> version 1.x ou 2.x. En installant <code>mdformat-deflist</code>, Pip installe aussi, comme dépendance, <code>markdown-it-py</code> 2.x, ce qui casse le <code>myst-parser</code> installé précédemment.</p>
<p>Ceci n’est naturellement pas du goût de tout le monde (je me rappelle d’ailleurs d’une enquête utilisateur faite par les développeurs de Pip il y a quelques années, où ils posaient la question de savoir ce que Pip devait faire dans cette situation). La morale est que pip est surtout un outil conçu pour créer un environnement virtuel où se trouvent toutes les dépendances dont on a besoin, pas pour s’en servir comme de <code>apt</code> ou <code>dnf</code>, en installant et désinstallant manuellement des dépendances. Et surtout, que <code>pip install X; pip install Y</code> <strong>n’est absolument pas équivalent</strong> à <code>pip install X Y</code>, et c’est la seconde forme qui est correcte.</p>
<h2 id="toc-les-environnements-virtuels-venv-virtualenv-pipx">Les environnements virtuels : <a href="https://docs.python.org/3/library/venv.html">venv</a>, <a href="https://virtualenv.pypa.io/">virtualenv</a>, <a href="https://pypa.github.io/pipx">pipx</a>
</h2>
<p>Les environnements virtuels permettent de travailler avec des ensembles de paquets différents, installés de façon indépendante entre eux. L’outil d’origine pour les créer est virtualenv. Néanmoins, le plus utilisé aujourd’hui est venv, qui est une version réduite de virtualenv intégrée à la bibliothèque standard. Malheureusement, venv est plus lent et n’a pas toutes les fonctionnalités de virtualenv, qui reste donc utilisé également…</p>
<p>Pour créer un environnement virtuel (avec venv), on exécute :</p>
<pre><code class="shell">python -m venv nom-de-l-environnement</code></pre>
<p>Cela crée un dossier <code>nom-de-l-environnement/</code>. Chaque environnement est donc stocké dans un dossier. À l’intérieur de ce dossier se trouve notamment un sous-dossier <code>bin/</code> avec des exécutables :</p>
<ul>
<li><p>un exécutable <code>python</code>, qui ouvre un interpréteur Python ayant accès aux paquets de l’environnement virtuel (et, par défaut, seulement eux),</p></li>
<li><p>un exécutable <code>pip</code>, qui installe les paquets à l’intérieur de l’environnement.</p></li>
</ul>
<p>De plus, pour simplifier l’utilisation dans un shell, on peut « activer » l’environnement, avec une commande qui dépend du shell. Par exemple, sous les shells UNIX classiques (<code>bash</code>, <code>zsh</code>), on exécute</p>
<pre><code class="shell"><span class="nb">source</span> nom-de-l-environnement/bin/activate</code></pre>
<p>Cette commande modifie la variable <code>PATH</code> pour y ajouter <code>nom-de-l-environnement/bin/</code> afin que (par exemple) la commande <code>python</code> invoque <code>nom-de-l-environnement/bin/python</code>.</p>
<p>Malgré cela, les environnements virtuels restent un niveau de confort en dessous du Python du système, puisqu’il faut activer un environnement avant de s’en servir, ou écrire à chaque fois le chemin <code>dossier-environnement/bin/</code>. Bien sûr, il faut aussi mémoriser les commandes, et puis c’est si facile de faire <code>pip install</code> dans l’environnement global (non virtuel). Donc, beaucoup n’y prêtent malheureusement pas attention et installent au niveau global, ce qui cause des conflits de dépendances (c’est maintenant refusé par défaut sous Debian et dérivés, comme je l’expliquais dans la section précédente, mais c’est toujours majoritaire sous macOS et Windows).</p>
<p>C’est aussi pour rendre plus pratiques les environnements virtuels qu’existent pléthore d’outils qui les créent et/ou activent pour vous. Je termine avec l’un de ces outils, lié à la fois à pip et aux environnements virtuels, j’ai nommé pipx. À première vue, pipx a une interface qui ressemble à celle de pip, avec par exemple des sous-commandes <code>pipx install</code>, <code>pipx uninstall</code> et <code>pipx list</code>. Mais, à la différence de pip, qui installe un paquet dans un environnement déjà créé, pipx va, pour chaque paquet installé, créer un nouvel environnement virtuel dédié. Pipx est principalement destiné à installer des outils dont l’interface est en ligne de commande, pas sous forme d’un module importable en Python. Pipx utilise pip, pour ne pas trop réinventer la roue quand même. Au final,</p>
<pre><code class="shell">$ pipx install pycowsay</code></pre>
<p>revient à quelque chose comme</p>
<pre><code class="shell">$ python -m venv ~/.local/pipx/pycowsay/
$ ~/.local/pipx/pycowsay/bin/pip install pycowsay
$ ln -s ~/.local/pipx/pycowsay/bin/pycowsay ~/.local/bin/pycowsay</code></pre>
<p>Pour résumer, pipx permet d’installer des outils en ligne de commande, de manière isolée, qui n’interfèrent pas avec le système ou entre eux, sans avoir à gérer les environnements virtuels soi-même.</p>
<h2 id="toc-linvocation-dun-build-backend-build">L’invocation d’un build backend : <a href="https://pypa-build.readthedocs.io">build</a>
</h2>
<p>Pour déposer son projet sur PyPI, il faut d’abord obtenir deux fichiers : une <em>sdist</em> (<em>source distribution</em>), qui est essentiellement une archive <code>.tar.gz</code> du code avec des métadonnées ajoutées, et un paquet installable au format <em>wheel</em>, d’extension <code>.whl</code>. L’outil build sert à générer ces deux fichiers. Il s’invoque comme ceci, dans le dossier du code source :</p>
<pre><code class="shell">python -m build</code></pre>
<p>Petit exemple dans le dépôt de Sphinx (l’outil de documentation le plus répandu dans le monde Python) :</p>
<pre><code class="text">$ python -m build
* Creating venv isolated environment...
* Installing packages in isolated environment... (flit_core>=3.7)
* Getting build dependencies for sdist...
* Building sdist...
* Building wheel from sdist
* Creating venv isolated environment...
* Installing packages in isolated environment... (flit_core>=3.7)
* Getting build dependencies for wheel...
* Building wheel...
Successfully built sphinx-7.3.0.tar.gz and sphinx-7.3.0-py3-none-any.whl
$ ls dist/
sphinx-7.3.0-py3-none-any.whl sphinx-7.3.0.tar.gz</code></pre>
<p>Comme on peut le comprendre, build est un outil très simple. L’essentiel de sa documentation tient en <a href="https://pypa-build.readthedocs.io">une courte page</a>. Il crée un environnement virtuel pour installer le build backend, en l’occurrence Flit, puis se contente d’invoquer celui-ci.</p>
<h2 id="toc-le-transfert-sur-pypi-twine">Le transfert sur PyPI : <a href="https://twine.readthedocs.io">twine</a>
</h2>
<p>À l’image de build, twine est un outil fort simple qui remplit une seule fonction et la remplit bien : déposer la <em>sdist</em> et le <em>wheel</em> sur PyPI (ou un autre dépôt de paquets). En continuant l’exemple précédent, on écrirait :</p>
<pre><code class="shell">twine upload dist/*</code></pre>
<p>Après avoir fourni un login et mot de passe, le projet est publié, il peut être installé avec pip, et possède sa page <code>https://pypi.org/project/nom-du-projet</code>.</p>
<h2 id="toc-la-configuration-dun-projet-le-fichier-pyprojecttoml">La configuration d’un projet : le fichier <code>pyproject.toml</code>
</h2>
<p><code>pyproject.toml</code> est le fichier de configuration adopté par à peu près tous les outils de packaging, ainsi que de nombreux outils qui ne sont pas liés au packaging (par exemple les linters comme Ruff, les auto-formateurs comme Black ou le même Ruff, etc.). Il est écrit dans le langage de configuration <a href="https://toml.io">TOML</a>. On a besoin d’un <code>pyproject.toml</code> pour n’importe quel projet publié sur PyPI, et même, souvent, pour les projets qui ne sont pas distribués sur PyPI (comme pour configurer Ruff).</p>
<p>Dans ce fichier se trouvent trois sections possibles — des « tables », en jargon TOML. La table <code>[build-system]</code> détermine le build backend du projet (je reviens plus bas sur le choix du build backend). La table <code>[project]</code> contient les informations de base, comme le nom du projet, la version, les dépendances, etc. Quant à la table <code>[tool]</code>, elle est utilisée via des sous-tables <code>[tool.<nom de l'outil>]</code> : tout outil peut lire de la configuration dans sa sous-table dédiée. Rien n’est standardisé par des spécifications PyPA dans la table <code>[tool]</code>, chaque outil y fait ce qu’il veut.</p>
<p>Avant qu’on me <a href="//linuxfr.org/news/l-installation-et-la-distribution-de-paquets-python-1-4#comment-1940643">dise</a> que <code>pyproject.toml</code> est mal documenté, ce qui a pu être vrai, je précise que des efforts ont été faits à ce niveau dans les dernières semaines, par moi et d’autres, ce qui donne un <a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/">guide du pyproject.toml</a> normalement complet et compréhensible, ainsi qu’une <a href="https://packaging.python.org/en/latest/discussions/setup-py-deprecated/">explication</a> sur ce qui est déprécié ou non concernant <code>setup.py</code> et un <a href="https://packaging.python.org/en/latest/guides/modernize-setup-py-project/">guide</a> sur la migration de <code>setup.py</code> vers <code>pyproject.toml</code>. Tout ceci réside sur <a href="https://packaging.python.org">packaging.python.org</a>, qui est un site officiel de la PyPA rassemblant des tutoriels, guides et spécifications techniques.</p>
<h2 id="toc-les-build-backends-pour-code-python-pur">Les build backends pour code Python pur</h2>
<p>Le build backend est chargé de générer les sdists et les wheels que l’on peut ensuite mettre sur PyPI avec twine ou autre. Il est spécifié dans le fichier <code>pyproject.toml</code>. Par exemple, pour utiliser Flit, la configuration est :</p>
<pre><code class="toml"><span class="p">[</span><span class="n">build-system</span><span class="p">]</span>
<span class="n">requires</span> <span class="o">=</span> <span class="p">[</span><span class="s">"flit_core>=3.7"</span><span class="p">]</span>
<span class="n">build-backend</span> <span class="o">=</span> <span class="s">"flit_core.buildapi"</span></code></pre>
<p><code>requires</code> est la liste des dépendances (des paquets sur PyPI), et <code>build-backend</code> est le nom d’un module (qui doit suivre une interface standardisée).</p>
<p>Il peut sembler étrange qu’il faille, même pour un projet simple, choisir son build backend. Passons donc en revue les critères de choix : de quoi est responsable le build backend ?</p>
<p>D’abord, il doit traduire les métadonnées du projet. En effet, dans les sdists et wheels, les métadonnées sont encodées dans un format un peu étrange, à base de MIME, qui est conservé au lieu d’un format plus moderne comme TOML ou JSON, pour des raisons de compatibilité. La plupart des build backends se contentent de prendre les valeurs dans la table <code>[project]</code> du <code>pyproject.toml</code> et de les copier directement sous la bonne forme, mais setuptools permet aussi de configurer les métadonnées via <code>setup.py</code> ou <code>setup.cfg</code>, également pour préserver la compatibilité, et il y a aussi des build backends comme Poetry qui n’ont pas adopté la table <code>[project]</code> (j’y reviens dans la section sur Poetry).</p>
<p>De plus, les build backends ont souvent des façons de calculer dynamiquement certaines métadonnées, typiquement la version, qui peut être lue depuis un attribut <code>__version__</code>, ou déterminée à partir du dernier tag Git.</p>
<p>C’est aussi le build backend qui décide des fichiers du projet à inclure ou exclure dans la sdist et le wheel. En particulier, on trouve généralement des options qui permettent d’inclure des fichiers autres que <code>.py</code> dans le wheel (c’est le wheel qui détermine ce qui est installé au final, alors que la sdist peut aussi contenir les tests etc.). Cela peut servir, par exemple, aux paquets qui doivent être distribués avec des icônes, des données en JSON, des templates Django…</p>
<p>Enfin, s’il y a des extensions en C, C++, Rust ou autre, le build backend est chargé de les compiler.</p>
<p>Il existe aujourd’hui de nombreux build backends. Beaucoup sont spécifiques à un type d’extensions compilées, ils sont présentés dans la troisième dépêche. Voici les <em>build backends</em> principaux pour du code Python pur.</p>
<h3 id="toc-setuptools"><a href="https://setuptools.pypa.io">setuptools</a></h3>
<p>C’est le build backend historique. Il reste très largement utilisé.</p>
<p>Avant qu’arrive <code>pyproject.toml</code>, il n’y avait qu’un build backend, <code>setuptools</code>, et il était configuré soit par le <code>setup.py</code>, soit par un fichier en syntaxe INI, nommé <code>setup.cfg</code> (qui est l’ancêtre de <code>pyproject.toml</code>). Ainsi, il existe aujourd’hui trois manières différentes de configurer <code>setuptools</code>, à savoir <code>setup.py</code>, <code>setup.cfg</code> et <code>pyproject.toml</code>. On rencontre les trois dans les projets existants. La façon recommandée aujourd’hui est <code>pyproject.toml</code> pour tout ce qui est statique, sachant que <code>setup.py</code>, qui est écrit en Python, peut toujours servir s’il y a besoin de configuration programmable.</p>
<p>Aujourd’hui, setuptools ne se veut plus qu’un build backend, mais historiquement, en tant que descendant de distutils, il a <em>beaucoup</em> de fonctionnalités, désormais dépréciées, pour installer des paquets ou autres. On peut se faire une idée de l’ampleur des évolutions qui ont secoué le <em>packaging</em> au fil des années en parcourant l’abondante <a href="https://setuptools.pypa.io/en/latest/deprecated/index.html">documentation des fonctionnalités obsolètes</a>, notamment <a href="https://setuptools.pypa.io/en/latest/deprecated/easy_install.html">cette page</a>, <a href="https://setuptools.pypa.io/en/latest/deprecated/commands.html">celle-ci</a> ou encore <a href="https://setuptools.pypa.io/en/latest/deprecated/python_eggs.html">celle-là</a>.</p>
<h3 id="toc-flit"><a href="https://flit.pypa.io">Flit</a></h3>
<p>Flit est l’exact opposé de setuptools. C’est le build backend qui vise à être le plus simple et minimal possible. Il n’y a pratiquement pas de configuration autre que la configuration standardisée des métadonnées dans la table <code>[project]</code> du <code>pyproject.toml</code>.</p>
<p>Flit se veut volontairement inflexible (« opinionated »), pour qu’il n’y ait pas de choix à faire. Avec Flit, un projet appelé <code>nom-projet</code> doit obligatoirement fournir un module et un seul, soit <code>nom_projet.py</code>, soit <code>nom_project/</code>. De même, il est possible d’inclure des fichiers autres que <code>.py</code>, mais ils doivent obligatoirement se trouver tous dans un dossier dédié au même niveau que le <code>pyproject.toml</code>.</p>
<p>Flit dispose aussi d’une interface en ligne de commande minimale, avec des commandes <code>flit build</code> (équivalent de <code>python -m build</code>), <code>flit publish</code> (équivalent de <code>twine upload</code>), <code>flit install</code> (équivalent de <code>pip install .</code>), et <code>flit init</code> (qui initialise un projet).</p>
<h3 id="toc-hatchling"><a href="https://hatch.pypa.io">Hatchling</a></h3>
<p>Hatchling est le build backend associé à Hatch, un outil tout-en-un dont il sera question plus loin.</p>
<p>Contrairement à setuptools, il est plutôt facile d’utilisation, et il fait plus souvent ce qu’on veut par défaut. Contrairement à Flit, il offre aussi des options de configuration plus avancées (comme pour inclure plusieurs modules dans un paquet), ainsi que la possibilité d’écrire des plugins.</p>
<h3 id="toc-pdm-backend"><a href="https://backend.pdm-project.org/">PDM-Backend</a></h3>
<p>De même que hatchling est associé à Hatch, PDM-Backend est associé à PDM. Je n'en ai pas d'expérience, mais à lire sa documentation, il me semble plus ou moins équivalent en fonctionnalités à hatchling, avec des options un peu moins fines.</p>
<h3 id="toc-poetry-core"><a href="https://python-poetry.org/docs/pyproject/">Poetry-core</a></h3>
<p>Comme les deux précédents, Poetry-core est associé à un outil plus vaste, à savoir Poetry.</p>
<p>Par rapport à hatchling et PDM-backend, il est moins sophistiqué (il ne permet pas de lire la version depuis un attribut dans le code ou depuis un tag Git).</p>
<h2 id="toc-la-gestion-des-versions-de-python-pyenv">La gestion des versions de Python : <a href="https://github.com/pyenv/pyenv">pyenv</a>
</h2>
<p>L’une des difficultés du <em>packaging</em> Python est que l’interpréteur Python lui-même n’est généralement pas compilé <em>upstream</em> et téléchargé depuis le site officiel, du moins pas sous Linux (c’est davantage le cas sous Windows, et plus ou moins le cas sous macOS). L’interpréteur est plutôt fourni de l’extérieur, à savoir, sous Linux, par le gestionnaire de paquets de la distribution, ou bien, sous macOS, par XCode, Homebrew ou MacPorts. Cela peut aussi être un Python compilé à partir du code source sur la machine de l’utilisateur.</p>
<p>Ce modèle est différent d’autres langages comme Rust, par exemple. Pour installer Rust, la plupart des gens utilisent Rustup, un script qui télécharge des exécutables statiques compilés <em>upstream</em> (le fameux <code>curl | bash</code> tant décrié…).</p>
<p>Le but de <code>pyenv</code> est de simplifier la gestion des versions de Python. On exécute, par exemple, <code>pyenv install 3.10.2</code> pour installer Python 3.10.2. Comme <code>pyenv</code> va compiler le code source, il faut quand même installer soi-même les dépendances (avec leurs en-têtes C).</p>
<h2 id="toc-un-outil-de-test-et-dautomatisation-tox">Un outil de test et d’automatisation : <a href="https://tox.wiki">tox</a>
</h2>
<p>À partir du moment où un projet grossit, il devient souvent utile d’avoir de petits scripts qui automatisent des tâches courantes, comme exécuter les tests, mettre à jour tel fichier à partir de tel autre, ou encore compiler des catalogues de traduction en format MO à partir des fichiers PO. Il devient également nécessaire de tester le projet sur différentes versions de Python, ou encore avec différentes versions des dépendances.</p>
<p>Tout cela est le rôle de <code>tox</code>. Il se configure avec un fichier <code>tox.ini</code>. Voici un exemple <a href="https://github.com/pygments/pygments/blob/8f3bec7/tox.ini">tiré de Pygments</a>:</p>
<pre><code>[tox]
envlist = py
[testenv]
description =
run tests with pytest (you can pass extra arguments for pytest,
e.g., "tox -- --update-goldens")
deps =
pytest >= 7.0
pytest-cov
pytest-randomly
wcag-contrast-ratio
commands =
pytest {posargs}
use_develop = True
</code></pre>
<p>On peut avoir plusieurs sections <code>[testenv:xxx]</code> qui définissent des environnements virtuels. Chaque environnement est créé avec une version de Python ainsi qu’une certaine liste de dépendances, et peut déclarer des commandes à exécuter. Ces commandes ne passent pas par un shell, ce qui garantit que le <code>tox.ini</code> reste portable.</p>
<h2 id="toc-interlude-vous-avez-dit-lock-file">Interlude : vous avez dit lock file?</h2>
<p>Pour faire simple, un lock file est un fichier qui décrit de manière exacte un environnement de sorte qu’il puisse être reproduit. Prenons un exemple. Imaginons une application Web déployée sur plusieurs serveurs, qui a besoin de la bibliothèque <code>requests</code>. Elle va déclarer cette dépendance dans sa configuration. Éventuellement, elle fixera une borne sur la version (par exemple <code>requests>=2.31</code>), pour être sûre d’avoir une version compatible. Mais le paquet <code>requests</code> a lui-même des dépendances. On souhaiterait que l’environnement soit vraiment reproductible — que des serveurs différents n’aient pas des versions différentes des dépendances, même si les environnements sont installés à des moments différents, entre lesquels des dépendances publient des nouvelles versions. Sinon, on risque des bugs difficiles à comprendre qui ne se manifestent que sur l’un des serveurs.</p>
<p>La même problématique se pose pour développer une application à plusieurs. Sauf si l’application doit être distribuée dans des environnements variés (par exemple empaquetée par des distributions Linux), il ne vaut pas la peine de s’embarrasser de versions différentes des dépendances. Il est plus simple de fixer toutes les versions pour tout le monde.</p>
<p>Dans la vraie vie, une application peut avoir des centaines de dépendances, dont quelques-unes directes et les autres indirectes. Il devient très fastidieux de maintenir à la main une liste des versions exactes de chaque dépendance.</p>
<p>Avec un lock file, on s’assure de geler un ensemble de versions de tous les paquets qui sera le même pour tous les contributeurs, et pour tous les déploiements d’une application. On sépare, d’un côté, la déclaration des dépendances directes minimales supposées compatibles avec l’application, écrite à la main, et de l’autre côté, la déclaration des versions exactes de toutes les dépendances, générée automatiquement. Concrètement, à partir de la contrainte <code>requests>=2.31</code>, un générateur de lock file pourrait écrire un lock file qui fixe les versions <code>certifi==2023.11.17</code>, <code>charset-normalizer==3.3.2</code>, <code>idna==3.4</code>, <code>requests==2.31.0</code>, <code>urllib3==2.1.0</code>. À la prochaine mise à jour du lock file, certaines de ces versions pourraient passer à des versions plus récentes publiées entre-temps.</p>
<p>Le concept de lock file est en revanche beaucoup plus discutable pour une bibliothèque (par opposition à une application), étant donné qu’une bibliothèque est faite pour être utilisée dans d’autres projets, et que si un projet a besoin des bibliothèques A et B, où A demande <code>requests==2.31.0</code> alors que B demande <code>requests==2.30.0</code>, il n’y a aucun moyen de satisfaire les dépendances. Pour cette raison, une bibliothèque doit essayer de minimiser les contraintes sur ses dépendances, ce qui est fondamentalement opposé à l’idée d’un lock file.</p>
<p>Il existe plusieurs outils qui permettent de générer et d’utiliser un lock file. Malheureusement, l’un des plus gros problèmes actuels du packaging Python est le manque criant, sinon d’un outil, d’un <em>format</em> de lock file standardisé. Il y a eu une tentative avec la <a href="https://peps.python.org/665">PEP 665</a>, rejetée par manque de consensus (mais avant qu’on soupire qu’il suffisait de se mettre d’accord : il y a de vraies questions techniques qui se posent, notamment sur l’adoption d’un standard qui ne permet pas de faire tout ce que font certains outils existants, qui risquerait de fragmenter encore plus au lieu d’aider).</p>
<h2 id="toc-un-gestionnaire-de-lock-file-pip-tools">Un gestionnaire de lock file : <a href="https://pip-tools.readthedocs.io">pip-tools</a>
</h2>
<p>pip-tools est un outil assez simple pour générer et utiliser un lock file. Il se compose de deux parties, <code>pip-compile</code> et <code>pip-sync</code>.</p>
<p>La commande <code>pip-compile</code>, prend un ensemble de déclarations de dépendances, soit dans un <code>pyproject.toml</code>, soit dans un fichier spécial <code>requirements.in</code>. Elle génère un fichier <code>requirements.txt</code> qui peut être installé par <code>pip</code>.</p>
<p>Quant à la commande <code>pip-sync</code>, c’est simplement un raccourci pour installer les dépendances du <code>requirements.txt</code>.</p>
<p>Les locks files sont donc des fichiers <code>requirements.txt</code>, un format pas si bien défini puisqu’un <code>requirements.txt</code> est en fait essentiellement une série d’arguments et d’options en ligne de commande à passer à pip.</p>
<h2 id="toc-les-outils-tout-en-un">Les outils « tout-en-un »</h2>
<p>Face à la prolifération d’outils à installer et mémoriser, certains ont essayé de créer une expérience plus cohérente avec des outils unifiés. Malheureusement, ce serait trop simple s’ils s’étaient accordés sur un projet commun…</p>
<h3 id="toc-poetry"><a href="https://python-poetry.org">Poetry</a></h3>
<p>Poetry est un outil un peu à part qui fait à peu près tout par lui-même.</p>
<p>Poetry se destine aux développeurs de bibliothèques et d’applications. Toutefois, en pratique, il est plutôt orienté vers les applications.</p>
<p>La configuration se fait entièrement dans le fichier <code>pyproject.toml</code>. Poetry s’utilise toujours avec son build backend Poetry-core, donc la partie <code>[build-system]</code> du <code>pyproject.toml</code> est configurée comme ceci :</p>
<pre><code class="toml"><span class="p">[</span><span class="n">build-system</span><span class="p">]</span>
<span class="n">requires</span> <span class="o">=</span> <span class="p">[</span><span class="s">"poetry-core"</span><span class="p">]</span>
<span class="n">build-backend</span> <span class="o">=</span> <span class="s">"poetry.core.masonry.api"</span></code></pre>
<p>En revanche, contrairement à la plupart des autres build backends, Poetry n’accepte pas la configuration dans la table <code>[project]</code>. À la place, il faut écrire les métadonnées sur le projet (nom, version, mainteneurs, etc.) dans la table <code>[tool.poetry]</code>, dans un format différent du format standard.</p>
<p>La particularité de Poetry qui le rend à part est d’être centré profondément sur le concept de lock file, d’insister fortement sur le <a href="https://semver.org">Semantic Versioning</a>, et d’avoir son propre résolveur de dépendances. Poetry n’installe jamais rien dans l’environnement virtuel du projet avant d’avoir généré un lock file qui trace les versions installées. Sa configuration a aussi des raccourcis typiques du semantic versioning, comme la syntaxe <code>nom_du_paquet = "^3.4"</code>, qui dans la table <code>[project]</code> s’écrirait plutôt <code>"nom_du_paquet >= 3.4, < 4</code>.</p>
<p>(Ironiquement, les versions de Poetry lui-même <a href="https://python-poetry.org/docs/faq/#why-does-poetry-not-adhere-to-semantic-versioning">ne suivent pas le Semantic Versioning</a>.)</p>
<p>Je ne vais pas présenter les commandes de Poetry une par une car cette dépêche est déjà bien trop longue. Je renvoie à la <a href="https://python-poetry.org/docs/">documentation</a>. Disons simplement qu’un projet géré avec Poetry se passe de pip, de build, de twine, de venv et de pip-tools.</p>
<h3 id="toc-pdm"><a href="https://pdm-project.org">PDM</a></h3>
<p>Je l’avoue, je connais mal PDM. D’après ce que j’en ai compris, il est assez semblable à Poetry dans son interface, mais suit tout de même plus les standards, en mettant sa configuration dans la table <code>[project]</code>, et en utilisant le résolveur de dépendances de pip. (Je parlerai tout de même, dans la quatrième dépêche, de la motivation à l’origine du développement de PDM, qui cache toute une histoire. Pour ceux qui ont compris, oui, c’est bien résumé par un nombre qui est multiple de 97.)</p>
<h3 id="toc-hatch"><a href="https://hatch.pypa.io">Hatch</a></h3>
<p>Hatch est plus récent que Poetry et PDM. (Il n’est pas encore au niveau de Poetry en termes de popularité, mais loin devant PDM.) Il est encore en développement rapide.</p>
<p>Comparé à Poetry et PDM, il ne gère pas, pour l’instant, les lock files. (Ici aussi, il y a une histoire intéressante : l’auteur a d’abord voulu attendre que les discussions de standardisation aboutissent à un format commun, mais vu l’absence de progrès, il a fait savoir récemment qu’il allait implémenter un format non standardisé, comme Poetry et PDM le font déjà.)</p>
<p>En contrepartie, Hatch gère aussi les versions de Python. Il est capable d’installer ou de désinstaller une version très simplement, sachant que, contrairement à pyenv, il ne compile pas sur la machine de l’utilisateur mais télécharge des versions précompilées (beaucoup plus rapide, et aucune dépendance à installer soi-même). Il a aussi une commande <code>fmt</code> pour reformater le projet (plutôt que de définir soi-même un environnement pour cela dans Poetry ou PDM), et il est <a href="https://github.com/pypa/hatch/issues/1053">prévu</a> qu’il gagne bientôt des commandes comme <code>hatch test</code> et <code>hatch doc</code> également.</p>
<p>De plus, dans Poetry, lorsque l’on déclare, par exemple, un environnement pour compiler la documentation, avec une dépendance sur <code>sphinx >= 7</code>, cette dépendance est résolue <em>en même temps que les dépendances principales du projet</em>. Donc, si votre générateur de documentation demande une certaine version, mettons, de Jinja2 (ou n’importe quel autre paquet), vous êtes forcé d’utiliser la même version pour votre propre projet, même si l’environnement pour exécuter votre projet n’a rien à voir avec l’environnement pour générer sa documentation. C’est la même chose avec PDM. Je trouve cette limitation assez frustrante, et Hatch n’a pas ce défaut.</p>
<h2 id="toc-la-création-dexécutables-indépendants-et-installeurs-pyinstaller-cx_freeze-briefcase-pyoxidizer-etc">La création d’exécutables indépendants et installeurs : PyInstaller, cx_freeze, briefcase, PyOxidizer (etc.)</h2>
<p>Distribuer son projet sur PyPI est bien beau, mais pour installer un paquet du PyPI, il faut d’abord avoir Python et savoir se servir d’un terminal pour lancer pip. Quid de la distribution d’une application graphique à des gens qui n’ont aucune connaissance technique ?</p>
<p>Pour cela, il existe une pléthore d’outils qui créent des installeurs, contenant à la fois Python, une application, ses dépendances, une icône d’application, et en bref, tout ce qu’il faut pour satisfaire les utilisateurs qui n’y comprennent rien et réclament juste une « application normale » avec un « installeur normal », un <code>appli-setup.exe</code> ou <code>Appli.dmg</code>. Les plus connus sont <a href="https://pyinstaller.org">PyInstaller</a> et <a href="https://pypi.org/project/py2exe">py2exe</a>. Plus récemment est aussi apparu <a href="https://briefcase.readthedocs.io">briefcase</a>.</p>
<p>Il y a aussi d’autres outils qui ne vont pas jusqu’à créer un installeur graphique, mais se contentent d’un exécutable qui peut être lancé en ligne de commande. Ce sont notamment <a href="https://cx-freeze.readthedocs.io">cx_freeze</a> et <a href="https://pyoxidizer.readthedocs.io">PyOxidizer</a>, mais il y en a <a href="https://pyoxidizer.readthedocs.io/en/stable/pyoxidizer_comparisons.html">bien d’autres</a>.</p>
<p>Malheureusement, toute cette classe d’usages est l’un des gros points faibles de l’écosystème actuel. PyInstaller, par exemple, est fondé sur des principes assez douteux qui datent d’une époque où le packaging était beaucoup moins évolué qu’aujourd’hui (voir notamment <a href="https://discuss.python.org/t/installer-creation-based-on-distributions/34576/4">ce commentaire</a>). Pour faire simple, PyInstaller détecte les <code>import</code> dans le code pour trouver les fichiers à inclure dans l’application, au lieu d’inclure toutes les dépendances déclarées par les mainteneurs. Il semble que briefcase soit meilleur de ce point de vue.</p>
<p>De manière plus générale, embarquer un interpréteur Python est techniquement compliqué, notamment à cause de l’interaction avec des bibliothèques système (comme OpenSSL), et chacun de ces projets résout ces difficultés d’une manière différente qui a ses propres limitations.</p>
<h2 id="toc-conda-un-univers-parallèle">Conda, un univers parallèle</h2>
<p>Comme expliqué dans la première dépêche sur l’historique du packaging, Conda est un outil entièrement indépendant de tout le reste. Il ne peut pas installer de paquets du PyPI, son format de paquet est différent, ses environnements virtuels sont différents. Il est développé par une entreprise, Anaconda Inc (mais publié sous une licence libre). Et surtout, bien que chacun puisse publier des paquets sur anaconda.org, il reste principalement utilisé à travers des <em>dépôts de paquets</em> comprenant plusieurs milliers de paquets, qui sont gérés non pas par les auteurs du code concerné, mais par des mainteneurs propres au dépôt de paquets, à la manière d’une distribution Linux, ou de Homebrew et MacPorts sous macOS. En pratique, les deux dépôts principaux sont Anaconda, qui est maintenu par Anaconda Inc, et conda-forge, maintenu par une communauté de volontaires.</p>
<p>Quelques outils gravitent autour de Conda (mais beaucoup moins que les outils compatibles PyPI, car Conda est plus unifié). Je pense notamment à <a href="https://mariusvniekerk.github.io/condax">Condax</a>, qui est à Conda ce que pipx est à pip. Il y a aussi <a href="https://conda.github.io/conda-lock">conda-lock</a> pour les lock files.</p>
<p>Grâce à son modèle, Conda permet une distribution très fiable des extensions C et C++, ce qui constitue son atout principal. Un inconvénient majeur est le manque de compatibilité avec PyPI, qui reste la source unique pour la plupart des paquets, Conda n’ayant que les plus populaires.</p>
<h2 id="toc-petite-comparaison-des-résolveurs-de-dépendances">Petite comparaison des résolveurs de dépendances</h2>
<p>Les résolveurs de dépendances sont des composants invisibles, mais absolument cruciaux des systèmes de packaging. Un résolveur de dépendances prend un ensemble de paquets, et de contraintes sur ces paquets, de la forme « l’utilisateur demande le paquet A version X.Y au minimum et version Z.T au maximum », ou « le paquet A version X.Y dépend du paquet B version Z.T au minimum et U.V au maximum ». Il est chargé de déterminer un ensemble de versions compatibles, si possible récentes.</p>
<p>Cela paraît simple, et pourtant, le problème de la résolution de dépendances est NP-complet (c’est facile à démontrer), ce qui signifie que, sauf à prouver fausse l'<a href="https://en.wikipedia.org/wiki/Exponential_time_hypothesis">hypothèse du temps exponentiel</a> (et si vous le faites, vous deviendrez célèbre et couronné de gloire et du prix Turing), il n’existe pas d’algorithme pour le résoudre qui ait une complexité meilleure qu’exponentielle. Les algorithmes utilisés en pratique se fondent soit sur des heuristiques, soit sur une traduction en problème SAT et appel d’un SAT-solveur. Le bon résolveur est celui qui réussira à résoudre efficacement les cas rencontrés en pratique. Pour revenir à Python, il y a aujourd’hui trois résolveurs de dépendances principaux pour les paquets Python.</p>
<p>Le premier est celui de pip, qui est implémenté dans <a href="https://github.com/sarugaku/resolvelib">resolvelib</a>. Il utilise des heuristiques relativement simples. Historiquement, il s’est construit sur une contrainte forte : jusqu’à récemment (<a href="https://peps.python.org/658">PEP 658</a>), il n’y avait aucun moyen sur PyPI de télécharger seulement les métadonnées d’un paquet sans télécharger le paquet entier. Donc, il n’était pas possible d’obtenir tout le graphe de dépendances entier avant de commencer la résolution, car cela aurait nécessité de télécharger le code entier de <em>toutes</em> les versions de chaque dépendance. Or, il n’y a aucun solveur SAT existant (à ma connaissance) qui permette de modifier incrémentalement le problème. Par conséquent, pip était de toute façon forcé d’adopter une stratégie ad-hoc. La contrainte a été levée, mais l’algorithme est resté.</p>
<p>Le deuxième résolveur est celui de Conda. (En fait, le résolveur est en train de changer, mais l’ancien et le nouveau sont similaires sur le principe.) Contrairement à pip, Conda télécharge à l’avance un fichier qui donne les dépendances de chaque version de chaque paquet, ce qui lui permet de traduire le problème de résolution entier en problème SAT et d’appliquer un solveur SAT.</p>
<p>Enfin, le troisième résolveur fait partie de Poetry. Si j’ai bien compris <a href="https://github.com/pypa/pip/issues/7406">ceci</a>, il utilise l’algorithme PubGrub, qui ne traduit pas le problème en SAT, mais le résout plutôt avec une méthode inspirée de certains solveurs SAT.</p>
<p>En pratique, dans mon expérience, le solveur de pip se montre rapide la plupart du temps (sauf dans les cas vraiment complexes avec beaucoup de dépendances et de contraintes).</p>
<p>Toujours dans mon expérience, la résolution de dépendances dans Conda est en revanche franchement lente. À sa décharge, je soupçonne que le résolveur lui-même n’est pas spécialement lent (voire, plus rapide que celui de pip ? je ne sais pas), mais comme Conda a pour principe de ne prendre quasiment rien dans le système, en ayant des paquets comme <code>wget</code>, <code>freetype</code>, <code>libxcb</code>, <code>pcre2</code>, etc. etc. etc., certains paquets ont un nombre absolument effrayant de dépendances. Par exemple, il y a quelque temps, j’ai eu à demander à <code>conda-lock</code> un environnement satisfaisant les contraintes suivantes :</p>
<pre><code>- pyqt=5.15.9
- sip=6.7.11
- pyqt-builder=1.15.2
- cmake=3.26.4
- openjpeg=2.5.0
- jpeg=9e
- compilers=1.6.0
- boost-cpp
- setuptools=68.0.0
- wheel
</code></pre>
<p>Sur mon ordinateur, il faut environ 7 minutes pour que Conda calcule l’environnement résultant — j’insiste sur le fait que rien n’est installé, ce temps est passé uniquement dans le résolveur de dépendances. Le lock file créé contient environ 250 dépendances (!).</p>
<p>À titre illustratif : « Conda has gotten better by taking more shortcuts and guessing things (I haven't had a 25+ hour solve in a while) » — <a href="https://iscinumpy.dev/post/bound-version-constraints/#solver">Henry Schreiner</a></p>
<p>Quant au résolveur de Poetry, même si je n’ai jamais utilisé sérieusement Poetry, je crois savoir que sa lenteur est l’une des objections les plus fréquentes à cet outil. Voir par exemple <a href="https://github.com/python-poetry/poetry/issues/2094">ce bug</a> avec 335 👍. (Je trouve aussi révélateur que sur les premiers résultats renvoyés par une recherche Google de « poetry dependency resolver », une moitié environ se plaigne de la lenteur du résolveur.)</p>
<p>D’un autre côté, le solveur de Poetry n’est appelé que lorsque le lock file est mis à jour, donc beaucoup moins souvent que celui de pip ou même Conda. Il y a un vrai compromis à faire : le résolveur de Poetry se veut plus précis (il est censé trouver plus souvent une solution avec des versions récentes), mais en contrepartie, la mise à jour du lock file peut prendre, apparemment, une dizaine de minutes dans certains cas.</p>
<h2 id="toc-conclusion-et-avis-personnels">Conclusion et avis personnels</h2>
<p>Je termine en donnant mes choix très personnels et partiellement <strong>subjectifs</strong>, avec lesquels tout le monde ne sera pas forcément d’accord.</p>
<p>D’abord, il faut une manière d’installer des outils en ligne de commande distribués sous forme de paquets Python. Il est sage de donner à chacun son environnement virtuel pour éviter que leurs dépendances n’entrent en conflit, ce qui peut arriver très vite. Pour cela, on a essentiellement le choix entre pipx, ou créer à la main un environnement virtuel à chaque fois. Sans hésiter, je choisis pipx.</p>
<p>(Il y a un problème de <em>boostrap</em> parce que pipx est lui-même un outil du même genre. La solution consiste à l’installer avec un paquet système, que la plupart des distributions fournissent, normalement sous le nom <code>python3-pipx</code>.)</p>
<p>Ensuite, pour travailler sur un projet, on a le choix entre utiliser build et twine à la main pour générer la sdist et le wheel et les distribuer sur PyPI, ou bien utiliser un outil plus unifié, soit Flit, soit Poetry, soit PDM, soit Hatch. Dans le premier cas, on peut utiliser n’importe quel build backend, dans le deuxième, on est potentiellement restreint au build backend associé à l’outil unifié (c’est le cas avec Flit et Poetry, mais pas avec PDM, et plus avec Hatch depuis très récemment).</p>
<p>Parlons d’abord du build backend. À vrai dire, lorsque les builds backends ont été introduits (par la PEP 517, voir dépêche précédente), la motivation était largement de permettre l’émergence d’alternatives à setuptools au vu du triste état de setuptools. L’objectif est atteint, puisqu’il y a désormais des alternatives mille fois meilleures. L’ennui, c’est qu’il y a aussi un peu trop de choix. Donc, comparons.</p>
<p>D’abord, il y a setuptools. Sa configuration est franchement compliquée, par exemple je ne comprends pas précisément les douze mille <a href="https://setuptools.pypa.io/en/latest/userguide/package_discovery.html">options de configuration</a> qui contrôlent les modules qui sont inclus dans les wheels. De plus, setuptools est vraiment excessivement verbeux. Dans le dépôt de Pygments, un module dont je suis mainteneur, <code>python -m build | wc -l</code> comptait 4190 lignes de log avec setuptools, à comparer à 10 lignes depuis que nous sommes passés à hatchling. Mais surtout, le problème avec setuptools, c’est qu’il y a mille et une fonctionnalités mutantes dont on ne sait pas très bien si elles sont obsolètes, et la documentation est, pour moi, tout simplement incompréhensible. Entendons-nous bien : j’ai beaucoup de respect pour les gens qui maintiennent setuptools, c’est absolument essentiel vu le nombre de paquets qui utilisent setuptools parce que c’était historiquement le seul outil, mais aujourd’hui, peu contestent que setuptools est moins bon qu’à peu près n’importe quel build backend dans la concurrence, et on ne peut pas le simplifier, justement à cause de toute la compatibilité à garder.</p>
<p>Alors… Flit ? Pour les débutants, ce n’est pas mal. Mais la force de Flit, son inflexibilité, est aussi son défaut. Exemple <a href="//linuxfr.org/news/l-installation-et-la-distribution-de-paquets-python-1-4#comment-1940663">ici</a> et <a href="https://lists.sr.ht/%7Elioploum/offpunk-devel/%3C4bf5c83950eb5a7f81007b1c6cea15db3e7c0d60.camel%40abou-samra.fr%3E">là</a> où, suite à un commentaire de Ploum sur la dépêche précédente, je l’ai aidé à résoudre son problème de packaging, dont la racine était que Flit ne permet tout simplement pas d’avoir plusieurs modules Python dans un même paquet.</p>
<p>Alors… Poetry-core ? PDM-backend ? Hatchling ? Les différences sont moins marquées, donc parlons un peu des outils unifiés qui leur sont associés.</p>
<p>D’abord, que penser de Poetry ? Je précise que ne l’ai jamais utilisé moi-même sur un vrai projet. À ce que j’en ai entendu, la plupart de ceux qui l’utilisent l’apprécient et le trouvent plutôt intuitif. Par ailleurs, comme décrit plus haut, il a son propre résolveur de dépendances, et celui-ci est particulièrement lent au point que la génération du lock file peut prendre du temps. Soit. Mais je suis un peu sceptique à cause de points plus fondamentaux, notamment le fait que les dépendances de votre générateur de documentation contraignent celles de votre projet, ce que je trouve assez idiot. Je recommande aussi ces deux posts très détaillés sur le blog d’Henry Schreiner : <a href="https://iscinumpy.dev/post/bound-version-constraints">Should You Use Upper Bound Version Constraints?</a> et <a href="https://iscinumpy.dev/post/poetry-versions">Poetry versions</a>. Pour faire court, Poetry incite fortement à mettre des contraintes de version maximum (comme <code>jinja2 < 3</code>), ce qui est problématique quand les utilisateurs de Poetry se mettent à en abuser sans s’en rendre compte. Et il a aussi des opinions assez spéciales sur la résolution de dépendances, par exemple il vous force à mettre une contrainte <code>< 4</code> sur Python lui-même dès qu’une de vos dépendances le fait, alors que tout projet Poetry le fait par défaut. J’ajoute le fait qu’on ne peut pas utiliser un autre build backend avec Poetry que Poetry-core. En corollaire, on ne peut pas utiliser Poetry sur un projet si tout le projet n’utilise pas Poetry, ce qui implique de changer des choses pour tous les contributeurs (alors que PDM et Hatch peuvent fonctionner avec un build backend différent). C’est pour moi un gros point noir.</p>
<p>Alors… PDM ? Honnêtement, je n’en ai pas assez d’expérience pour juger vraiment. Je sais qu’il corrige la plupart des défauts de Poetry, mais garde le « défaut du générateur de documentation ».</p>
<p>Alors… Hatch ? C’est celui que j’utilise depuis quelques mois, et jusqu’ici, j’en suis plutôt satisfait. C’est un peu dommage qu’il n’ait pas encore les lock files, mais je n’en ai pas besoin pour mes projets.</p>
<p>Je n’utilise pas pyenv. Déjà avant Hatch, je trouvais qu’il représentait trop de travail à configurer par rapport au service rendu, que je peux facilement faire à la main avec <code>./configure && make</code> dans le dépôt de Python. Et depuis que j’utilise Hatch, il le fait pour moi, sans avoir à compiler Python.</p>
<p>De même, je n’utilise plus tox, je fais la même chose avec Hatch (avec la nuance que si j’ai déjà tanné mes co-mainteneurs pour remplacer <code>make</code> par tox, j’hésite à re-changer pour Hatch…). J’ai fini par me méfier du format INI, qui est piégeux (c’est subjectif) et mal spécifié (il y en a mille variantes incompatibles).</p>
<p>Donc, en résumé, j’utilise seulement deux outils, qui sont pipx, et Hatch. (Et <a href="https://github.com/pypa/hatch/issues/1105">j’espère</a> n’avoir bientôt plus besoin de pipx non plus.) Mais si vous avez besoin de lock files, vous pourriez remplacer Hatch par PDM.</p>
<p>Je termine la comparaison avec un mot sur Conda. À mon avis, entre écosystème Conda et écosystème PyPI, le choix est surtout pragmatique. Qu’on le veuille ou non, l’écosystème Python s’est historiquement construit autour de PyPI, qui reste prédominant. Malheureusement, la réponse de Conda à « Comment installer dans un environnement Conda un paquet PyPI qui n’est pas disponible au format Conda ? » est « Utilisez pip, mais à vos risques et périls, ce n’est pas supporté », ce qui n’est pas fantastique lorsque l’on a besoin d’un tel paquet (cela arrive très vite). D’un autre côté, pour le calcul scientifique, Conda peut être plus adapté, et il y a des pans entiers de ce domaine, comme <a href="https://pypackaging-native.github.io/key-issues/native-dependencies/geospatial_stack/">le géospatial</a>, qui fonctionnent avec Conda et ne fonctionnent pas du tout avec PyPI.</p>
<p>J’espère que ce très long panorama était instructif et aidait à y voir plus clair dans l’écosystème. Pour la troisième dépêche, je vous proposerai un focus sur la distribution des modules d’extension écrits dans des langages compilés, sur ses difficultés inextricables, et sur les raisons pour lesquelles le modèle de Conda en fait une meilleure plateforme que PyPI pour ces extensions.</p>
</div><div><a href="https://linuxfr.org/news/l-installation-et-la-distribution-de-paquets-python-2-4.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/134246/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/l-installation-et-la-distribution-de-paquets-python-2-4#comments">ouvrir dans le navigateur</a>
</p>
jeanasBenoît SibaudNils RatusznikYsabeau 🧶https://linuxfr.org/nodes/134246/comments.atomtag:linuxfr.org,2005:News/417402023-11-06T09:10:39+01:002023-11-08T09:28:48+01:00L’installation et la distribution de paquets Python (1/4)Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Quelques dépêches précédentes ont parlé des outils de <em>packaging</em> Python, comme <a href="//linuxfr.org/news/environnement-moderne-de-travail-python">ici</a>, <a href="//linuxfr.org/news/python-partie-6-pip-et-pipx">là</a> ou encore <a href="//linuxfr.org/news/python-partie-8-pipenv">là</a>. Je vais chercher à faire un tour complet de la question, non seulement du point de vue de l’utilisateur qui cherche à comprendre quelle est « la bonne » solution (← ha ha ha <em>rire moqueur</em>…), mais aussi en expliquant les choix qui ont été faits, les évolutions, la structure de la communauté autour des outils, et les critiques qui leur sont souvent adressées, à tort ou à raison.</p>
</div><ul></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-les-outils-historiques-distutils-et-setuptools">Les outils historiques : distutils et setuptools</a></li>
<li><a href="#toc-la-mont%C3%A9e-en-puissance-de-pip-et-pypi">La montée en puissance de <code>pip</code> et PyPI</a></li>
<li><a href="#toc-lintroduction-du-format-wheel">L’introduction du format « wheel »</a></li>
<li><a href="#toc-les-d%C3%A9buts-des-environnements-virtuels">Les débuts des environnements virtuels</a></li>
<li><a href="#toc-la-cr%C3%A9ation-de-la-python-packaging-authority-pypa">La création de la <em>Python Packaging Authority</em> (PyPA)</a></li>
<li><a href="#toc-le-d%C3%A9veloppement-dun-%C3%A9cosyst%C3%A8me-alternatif-conda-et-anaconda">Le développement d’un écosystème alternatif, <code>conda</code> et Anaconda</a></li>
<li><a href="#toc-les-pep-517-et-518-une-petite-r%C3%A9volution">Les PEP 517 et 518, une petite révolution</a></li>
<li><a href="#toc-la-pep-621-un-peu-de-standardisation">La PEP 621, un peu de standardisation</a></li>
<li><a href="#toc-l%C3%A9mergence-doutils-tout-en-un-alternatifs">L’émergence d’outils tout-en-un alternatifs</a></li>
<li><a href="#toc-conclusion">Conclusion</a></li>
</ul>
<p>Il est question ici de <em>packaging</em>, terme pris dans un sens très large :</p>
<ul>
<li>L’installation de paquets,</li>
<li>L’installation de Python lui-même,</li>
<li>L’administration d’environnements isolés,</li>
<li>La gestion de dépendances (<em>lock files</em> notamment),</li>
<li>La distribution de son code, en tant qu’auteur d’un paquet Python,</li>
<li>La compilation de code natif (écrit en C, C++, Rust…) pour être utilisé depuis Python.</li>
</ul>
<p>Le langage Python, avec son écrasante popularité (premier au classement TIOBE), est vanté pour sa simplicité. Hélas, c’est devenu un lieu commun que cette simplicité ne s’étend pas aux outils de <em>packaging</em>, qui sont tout sauf faciles à maîtriser. Personnellement, j’y ai été confronté en voulant soulager la situation de <a href="https://frescobaldi.org">Frescobaldi</a>, une application écrite avec Python et PyQt. Frescobaldi possède une dépendance qui <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/51">se</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/2">trouve</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/58">concentrer</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/57">quelques</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/56">difficultés</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/53">de</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/52">distribution</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/48">et</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/46">d’installation</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/29">coriaces</a>, <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/27">en</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/20">raison</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/15">de</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/14">code</a> <a href="https://github.com/frescobaldi/python-poppler-qt5/issues/13">C++</a>.</p>
<p>Ce problème d’ensemble a conduit à des évolutions rapides au cours des dernières années, qui tentent de garder un équilibre fragile entre l’introduction de nouvelles méthodes et la compatibilité avec l’existant. Parmi les utilisateurs, une confusion certaine a émergé avec cette cadence des changements, qui fait que des tutoriels écrits il y a quelques années voire quelques mois à peine sont complètement obsolètes, de même que des outils encore récemment recommandés.</p>
<p>Bien que de nombreux progrès soient indéniables, il existe un scepticisme très répandu concernant le degré de fragmentation déconcertant de l’écosystème, qui met l’utilisateur face à un labyrinthe d’outils qui se ressemblent.</p>
<p>Pour illustrer ce labyrinthe, voici une liste des outils que, personnellement, j’utilise ou j’ai utilisé, ou dont j’ai au moins lu sérieusement une partie de la documentation :</p>
<p>pip, pipx, virtualenv, venv, ensurepip, conda, condax, conda-lock, tox, build, twine, setuptools, setuptools-scm, flit, hatch, poetry, pdm, rye, pip-tools, maturin, setuptools-rust, meson-python, scikit-build, sip, pyinstaller, py2app, py2exe, cx_freeze, pyoxidizer, pynsist, briefcase, wheel, repairwheel, auditwheel, delocate, delvewheel, cibuildwheel</p>
<p>Encore une fois, je n’ai mis ceux que je connais. On en trouve encore d’autres <a href="https://packaging.python.org/en/latest/key_projects">ici</a>.</p>
<p>Sans compter quelques outils dont j’ai connaissance mais qui sont aujourd’hui obsolètes ou non-maintenus :</p>
<p>distutils, distutils2, distribute, pyflow, bento, pipenv</p>
<p>Et quelques librairies de plus bas niveau : importlib.metadata, packaging, distlib, installer</p>
<p>Face à cette profusion, le classique aujourd’hui est de citer <a href="https://xkcd.com/927">le XKCD qui va bien</a>, et de renchérir en comparant au langage Rust, connu pour la simplicité de ses outils, à savoir :</p>
<p>cargo, rustup</p>
<p><em>Et c’est tout.</em> Alors, pourquoi a-t-on besoin de plusieurs douzaines d’outils différents pour Python ?</p>
<p>Cela s’explique largement par des facteurs techniques, que j’expliquerai, qui font à la fois que le <em>packaging</em> Python est intrinsèquement plus compliqué, et qu’on lui demande beaucoup plus. En vérité, dans ces outils, on trouve des projets qui ont des cas d’utilisation complètement différents et ne s’adressent pas au même public (comme <code>pip</code> et <code>repairwheel</code>). Le hic, c’est qu’il y a, aussi, beaucoup de projets dont les fonctionnalités se recouvrent partiellement, voire totalement, comme entre <code>pip</code> et <code>conda</code>, entre <code>venv</code> et <code>virtualenv</code>, entre <code>hatch</code> et <code>poetry</code>, entre <code>pyinstaller</code> et <code>briefcase</code>, etc. Ces projets sont en concurrence et ont chacun leurs adeptes religieux et leurs détracteurs, à la manière des vieilles querelles Vim/Emacs et compagnie.</p>
<p>Cette dépêche est la première d’une série de quatre, qui ont pour but de décortiquer comment tout cela fonctionne, de retracer comment on en est arrivés là, et de parler des perspectives actuelles :</p>
<ol>
<li><strong>L’histoire du <em>packaging</em> Python</strong></li>
<li>Tour de l’écosystème actuel</li>
<li>Le casse-tête du code compilé</li>
<li>La structure de la communauté en question</li>
</ol>
<h2 id="toc-les-outils-historiques-distutils-et-setuptools">Les outils historiques : distutils et setuptools</h2>
<p>Guido van Rossum a créé le langage Python en 1989. Rappelons qu’à l’époque, le World Wide Web n’existait pas encore, ni d’ailleurs le noyau Linux, les deux ayant été créés en 1991. D’après Wikipédia (<a href="https://en.wikipedia.org/wiki/Package_manager">source</a>), le premier logiciel comparable aux gestionnaires de paquets actuels a été CPAN — destiné au langage Perl — qui ne date que de 1995. Python a donc grandi en même temps que l’idée des paquets, des gestionnaires de paquets et des dépôts sur Internet prenait racine.</p>
<p>C’est en 1998 (<a href="https://gerg.ca/blog/post/2013/distutils-history">source</a>) que le module <strong>distutils</strong> est né pour répondre au même besoin que CPAN pour Perl, dans le monde Python encore balbutiant. Ce module servait à compiler et installer des paquets, y compris des paquets écrits en C. Historiquement, il permettait aussi de générer des paquets au format RPM de Red Hat, et d’autres formats pour des machines de l’époque comme Solaris et HP-UX (<a href="https://docs.python.org/3.0/distutils/introduction.html">source</a>).</p>
<p><code>distutils</code> faisait partie de la bibliothèque standard du langage. Il était configuré avec un fichier écrit en Python, nommé conventionnellement <code>setup.py</code>, qui prenait généralement cette forme :</p>
<pre><code class="python"><span class="kn">from</span> <span class="nn">distutils.core</span> <span class="kn">import</span> <span class="n">setup</span>
<span class="n">setup</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'nom-paquet'</span><span class="p">,</span>
<span class="n">version</span><span class="o">=</span><span class="s1">'x.y.z'</span><span class="p">,</span>
<span class="o">...</span><span class="p">)</span></code></pre>
<p>On pouvait alors exécuter le <code>setup.py</code> comme un script, en lui passant le nom d’une commande :</p>
<pre><code class="shell">$ python setup.py install <span class="c1"># installe le paquet</span>
$ python setup.py bdist_rpm <span class="c1"># génère un RPM</span></code></pre>
<p>Le téléchargement de paquets depuis un dépôt partagé ou la résolution des dépendances n’ont jamais fait partie des fonctions de <code>distutils</code>. De façon peut-être plus surprenante, <code>distutils</code> n’offre pas de moyen simple ou fiable pour désinstaller un paquet.</p>
<p>Le projet <strong>setuptools</strong> est arrivé en 2004 (<a href="https://mail.python.org/pipermail/distutils-sig/2004-March/003758.html">source</a>) pour pallier les limitations de <code>distutils</code>. Contrairement à <code>distutils</code>, <code>setuptools</code> a toujours été développé en dehors de la bibliothèque standard. Cependant, il était fortement couplé à <code>distutils</code>, le modifiant par des sous-classes… et par une bonne dose <em>monkey-patching</em> (<a href="https://github.com/pypa/setuptools/blob/main/setuptools/monkey.py">source</a>).</p>
<p>Lesdites limitations ont également conduit à un fork de <code>distutils</code>, nommé <strong>distutils2</strong>, démarré en 2010. Parmi les raisons citées par son initiateur figure le désir de faire évoluer <code>distutils</code> à un rythme plus soutenu que ce qui était raisonnable pour un module de la bibliothèque standard, surtout un module largement utilisé au travers de <code>setuptools</code> et son monkey-patching (<a href="https://tarekziade.wordpress.com/2010/03/03/the-fate-of-distutils-pycon-summit-packaging-sprint-detailed-report">source</a>). Mais <code>distutils2</code> a cessé d’être développé en 2012 (<a href="https://pypi.org/project/Distutils2/#history">source</a>).</p>
<p>De même, entre 2011 et 2013 (<a href="https://pypi.org/project/distribute/#history">source</a>), il a existé un fork de <code>setuptools</code> appelé <strong>distribute</strong>. Ses changements ont fini par être fusionnés dans <code>setuptools</code>.</p>
<p>Pour finir, <code>distutils</code> a été supprimé de la bibliothèque standard dans la version 3.12 de Python, sortie en octobre 2023, au profit de <code>setuptools</code>. Mais cela ne signifie pas que <code>distutils</code> n’est plus utilisé du tout : <code>setuptools</code> continue d’en contenir une copie qui peut être utilisée (bien que ce ne soit pas recommandé).</p>
<p>Il est aussi à noter que NumPy, la bibliothèque Python quasi universelle de calcul scientifique, maintient elle aussi son propre fork de <code>distutils</code>, <code>numpy.distutils</code>, qui est en train d’être remplacé (<a href="https://numpy.org/doc/stable/reference/distutils.html">source</a>). Cela montre à quel point le code de <code>distutils</code> s’est révélé à la fois essentiel, omniprésent à travers divers avatars ou forks, et difficile à faire évoluer.</p>
<h2 id="toc-la-montée-en-puissance-de-pip-et-pypi">La montée en puissance de <code>pip</code> et PyPI</h2>
<p>Avec le développement d’Internet et la croissance de la communauté Python, il devenait nécessaire d’avoir un registre centralisé où pouvaient être déposés et téléchargés les paquets. C’est dans ce but qu’a été créé <strong>PyPI</strong>, le <strong>Py</strong>thon <strong>P</strong>ackage <strong>I</strong>ndex, en 2002 (<a href="https://peps.python.org/pep-0301">source</a>). (Ne pas confondre PyPI avec PyPy, une implémentation alternative de Python. PyPy se prononce « paille paille » alors que PyPI se prononce « paille pie aïe »).</p>
<p><code>distutils</code> pouvait installer un paquet une fois le code source téléchargé, mais pas installer le paquet depuis PyPI directement, en résolvant les dépendances. Le premier outil à en être capable a été <code>setuptools</code> avec la commande <strong>easy_install</strong> (<a href="https://packaging.python.org/en/latest/discussions/pip-vs-easy-install/#pip-vs-easy-install">source</a>), apparue en 2004.</p>
<p>Puis est arrivé <strong>pip</strong> en 2008. Entre autres, <code>pip</code> était capable, contrairement à tout ce qui existait jusque là, de désinstaller un paquet ! En quelques années, <code>easy_install</code> est devenu obsolète et <code>pip</code> s’est imposé comme l’outil d’installation standard. En 2013, la <a href="https://peps.python.org/pep-0453">PEP 453</a> a ajouté à la bibliothèque standard un module <code>ensurepip</code>, qui <em>bootstrape</em> <code>pip</code> en une simple commande : <code>python -m ensurepip</code>. De cette manière, <code>pip</code> a pu continuer d’être développé indépendamment de Python tout en bénéficiant d’une installation facile (puisqu’on ne peut évidemment pas installer <code>pip</code> avec <code>pip</code>) et d’une reconnaissance officielle.</p>
<h2 id="toc-lintroduction-du-format-wheel">L’introduction du format « wheel »</h2>
<p>Sans rentrer trop dans les détails à ce stade, le format principal pour distribuer un paquet jusqu’en 2012 était le code source dans une archive <code>.tar.gz</code>. Pour installer un paquet, <code>pip</code> devait décompresser l’archive et exécuter le script <code>setup.py</code>, utilisant <code>setuptools</code>. Bien sûr, cette exécution de code arbitraire posait un problème de fiabilité, car il était critique de contrôler l’environnement, par exemple la version de <code>setuptools</code>, au moment d’exécuter le <code>setup.py</code>.</p>
<p><code>setuptools</code> avait bien un format de distribution précompilé, le format <em>egg</em> (le nom est une référence aux Monty Python). Un paquet en <code>.egg</code> pouvait être installé sans exécuter de <code>setup.py</code>. Malheureusement, ce format était mal standardisé et spécifique à <code>setuptools</code>. Il était conçu comme un format importable, c’est à dire que l’interpréteur pouvait directement lire l’archive (sans qu’elle soit décompressée) et exécuter le code dedans. C’était essentiellement une façon de regrouper tout le code en un seul fichier, mais il n’y avait pas vraiment d’intention de distribuer les <code>.egg</code>.</p>
<p>La <a href="https://peps.python.org/pep-0427">PEP 427</a> a défini le format <em>wheel</em> pour que les paquets puissent être distribués sous forme précompilée sur PyPI, évitant l’exécution du <code>setup.py</code> durant l’installation.</p>
<p>Parmi les innovations, les fichiers <em>wheels</em> sont nommés d’après une convention qui indique avec quelle plateforme ils sont compatibles. Ceci a grandement facilité la distribution de modules Python codés en C ou C++. Jusqu’ici, ils étaient toujours compilés sur la machine de l’utilisateur, durant l’exécution du <code>setup.py</code>. Avec les wheels, il est devenu possible de distribuer le code déjà compilé, avec un wheel par système d’exploitation et par version de Python (voire moins, mais c’est pour la troisième dépêche).</p>
<h2 id="toc-les-débuts-des-environnements-virtuels">Les débuts des environnements virtuels</h2>
<p>Voici un problème très banal : vous voulez utiliser sur la même machine les projets <code>foo</code> et <code>bar</code>, or <code>foo</code> nécessite <code>numpy</code> version 3.14 alors que <code>bar</code> nécessite <code>numpy</code> version 2.71. Malheureusement, en Python, il n’est pas possible d’installer deux versions du même paquet à la fois dans un même environnement, alors que d’autres langages le permettent. (Plus précisément, il est possible d’installer deux versions en parallèle si elles ont des noms de module différents, le nom de module Python n’étant pas forcément le même que le nom de paquet, cf. Pillow qui s’installe avec <code>pip install pillow</code> mais s’importe avec <code>import PIL</code>. Mais tant que le module Python n’est pas renommé, les deux sont en conflit.)</p>
<p>La solution est alors de se servir d’un <strong>environnement virtuel</strong>, qui est un espace isolé où on peut installer des paquets indépendamment du reste du système. Cette technique a été développée dans le projet <code>virtualenv</code>, créé par Ian Bicking, qui est aussi l’auteur originel de <code>pip</code>. La première version date de 2007 (<a href="https://pypi.org/project/virtualenv/0.8">source</a>).</p>
<p>Plus tard, en 2012 (<a href="https://peps.python.org/pep-0405">source</a>), un module <code>venv</code> a été ajouté à la bibliothèque standard. C’est une version réduite de <code>virtualenv</code>. On peut toutefois regretter que <code>virtualenv</code> soit encore nécessaire dans des cas avancés, ce qui fait que les deux sont utilisés aujourd’hui, même si <code>venv</code> est largement prédominant.</p>
<h2 id="toc-la-création-de-la-python-packaging-authority-pypa">La création de la <em>Python Packaging Authority</em> (PyPA)</h2>
<p>La première réaction naturelle en entendant le nom « Python Packaging Authority » est bien sûr de penser qu’à partir de 2012, date de sa création, ce groupe a été l’acteur d’une unification, d’une standardisation, d’une mise en commun des efforts, etc. par contraste avec la multiplication des <em>forks</em> de <code>distutils</code>.</p>
<p>Sauf que… <strong>le mot « Authority » était au départ une blague</strong> (sérieusement !). La preuve sur <a href="https://gist.github.com/jezdez/6222d1ba8b10d734d003492e58041687">l’échange de mails</a> où le nom a été choisi, les alternatives proposées allant de « ianb-ng » à « Ministry of Installation » (référence aux Monty Python), en passant par « Politburo ».</p>
<p>La Python Packaging Authority a démarré comme un groupe informel de successeurs à Ian Bicking (d’où le « ianb ») pour maintenir les outils qu’il avait créés, <code>pip</code> et <code>virtualenv</code>. Elle est un ensemble de projets, reconnus comme importants et maintenus.</p>
<p>Au fil du temps, la partie « Authority » son nom a évolué progressivement de la blague vers une autorité semi-sérieuse. La PyPA d’aujourd’hui développe des <strong>standards d’interopérabilité</strong>, proposés avec le même processus que les changements au langage Python lui-même, sous forme de PEP (Python Enhancement Proposals), des propositions qui ressemblent aux RFC, JEP et autres SRFI. La PyPA est également une <a href="https://github.com/pypa">organisation GitHub</a>, sous l’égide de laquelle vivent les dépôts de divers projets liés au <em>packaging</em>, dont la plupart des outils que je mentionne dans cette dépêche, aux exceptions notables de <code>conda</code>, <code>poetry</code> et <code>rye</code>.</p>
<p>La PyPA <em>n’est pas</em> :</p>
<ul>
<li><p>Un ensemble <em>cohérent</em> d’outils. Il y a <em>beaucoup</em> d’outils redondants (mais chacun ayant ses adeptes) dans la PyPA. Pour ne donner qu’un exemple, tout ce que peut faire <code>flit</code>, <code>hatch</code> peut le faire aussi.</p></li>
<li><p>Une véritable autorité. Elle reste composée de projets indépendants, avec chacun ses mainteneurs. Le fait qu’un standard soit accepté ne garantit pas formellement qu’il sera implémenté. J’ai lu quelque part une allusion à un exemple récent de « transgression » dans <code>setuptools</code>, mais je n’ai pas retrouvé de quel standard il s’agissait. Concrètement, cela ne veut pas dire que les transgressions sont fréquentes, mais plutôt qu’une PEP risque de ne pas être acceptée s’il n’est pas clair que les mainteneurs des outils concernés sont d’accord.</p></li>
</ul>
<p>La décision finale sur une PEP est prise par un « délégué », et normalement, cette personne ne fait que formaliser le consensus atteint (même s’il peut y avoir des <a href="https://discuss.python.org/t/pronouncement-on-peps-660-and-662-editable-installs/9450">exceptions</a>).</p>
<p>Il n’y a donc pas de place dans la PyPA actuelle pour des décisions du type de « <code>flit</code> devient officiellement déprécié au profit de <code>hatch</code> ».</p>
<h2 id="toc-le-développement-dun-écosystème-alternatif-conda-et-anaconda">Le développement d’un écosystème alternatif, <code>conda</code> et Anaconda</h2>
<p>Pour compléter la liste de tout ce qui s’est passé en 2012, c’est aussi l’année de la première version de <code>conda</code>.</p>
<p>Cet outil a été créé pour pallier les graves lacunes des outils classiques concernant la distribution de paquets écrits en C ou C++ (Rust était un langage confidentiel à l’époque). Jusque là, on ne pouvait redistribuer que le code source, et il fallait que chaque paquet soit compilé sur la machine où il était installé.</p>
<p>Le format <em>wheel</em> introduit plus haut a commencé à résoudre ce problème, mais toutes ces nouveautés sont concomitantes.</p>
<p>Contrairement à tous les autres outils mentionnés dans cette dépêche, Conda, bien qu’<em>open source</em>, est développé par une entreprise (d’abord appelée Continuum Analytics, devenue Anaconda Inc.). C’est un univers parallèle à l’univers PyPA : un installeur de paquets différent (<code>conda</code> plutôt que <code>pip</code>), un format de paquet différent, un gestionnaire d’environnements virtuel différent (<code>conda</code> plutôt que <code>virtualenv</code> ou <code>venv</code>), une communauté largement séparée. Il est aussi beaucoup plus unifié.</p>
<p><code>conda</code> adopte un modèle qui se rapproche davantage de celui d’une distribution Linux que de PyPI, en mettant le <em>packaging</em> dans les mains de mainteneurs séparés des auteurs des paquets. Il est possible de publier ses propres paquets indépendamment d’une organisation, mais ce n’est pas le cas le plus fréquent. On pourrait comparer cela à ArchLinux, avec son dépôt principal coordonné auquel s’ajoute un dépôt non coordonné, l’AUR.</p>
<p>Cette organisation permet à <code>conda</code> de faciliter énormément la distribution de modules C ou C++. En pratique, même avec les <em>wheels</em>, cela reste une source infernale de casse-têtes avec les outils PyPA (ce sera l’objet de la troisième dépêche), alors que tout devient beaucoup plus simple avec <code>conda</code>. Voilà son gros point fort et sa raison d’être.</p>
<p>Il faut bien distinguer <code>conda</code>, l’outil, d’Anaconda, qui est une <em>distribution</em> de Python contenant, bien sûr, <code>conda</code>, mais aussi une flopée de paquets scientifiques ultra-populaires comme NumPy, SciPy, matplotlib, etc. Anaconda est très largement utilisée dans le monde scientifique (analyse numérique, <em>data science</em>, intelligence artificielle, etc.). Il existe aussi Miniconda3, qui est une distribution plus minimale avec seulement <code>conda</code> et quelques autres paquets essentiels. Enfin, <code>conda-forge</code> est un projet communautaire (contrairement à Anaconda, développé au sein de l’entreprise Anaconda Inc.) qui distribue des milliers de paquets. On peut, dans une installation d’Anaconda, installer un paquet depuis <code>conda-forge</code> avec <code>conda - c conda-forge</code> (le plus courant), ou bien (moins courant) installer une distribution nommée « Miniforge », qui est un équivalent de Miniconda3 fondé entièrement sur <code>conda-forge</code>.</p>
<h2 id="toc-les-pep-517-et-518-une-petite-révolution">Les PEP 517 et 518, une petite révolution</h2>
<p>S’il y a deux PEP qui ont vraiment changé le <em>packaging</em>, ce sont les PEP 517 et 518.</p>
<p>La <a href="https://peps.python.org/pep-0518">PEP 518</a> constate le problème que le fichier de configuration de <code>setuptools</code>, le <code>setup.py</code>, est écrit en Python. Comme il faut <code>setuptools</code> pour exécuter ce fichier, il n’est pas possible pour lui, par exemple, de spécifier la version de <code>setuptools</code> dont il a besoin. Il n’est pas possible non plus d’avoir des dépendances dans le <code>setup.py</code> autres que <code>setuptools</code> (sauf avec un hack nommé <code>setup_requires</code> proposé par <code>setuptools</code>, qui joue alors un rôle d’installeur de paquets comme <code>pip</code> en plus de son rôle normal… ce pour quoi <code>setuptools</code> n’excellait pas). <strong>Elle décrit aussi explicitement comme un problème le fait qu’il n’y ait pas de moyen pour une alternative à setuptools de s’imposer.</strong> En effet, le projet <code>setuptools</code> souffrait, et souffre toujours gravement, d’une surcomplication liée à toutes ses fonctionnalités dépréciées.</p>
<p>Pour remédier à cela, la PEP 518 a introduit un nouveau fichier de configuration nommé <strong>pyproject.toml</strong>. Ce fichier est écrit en <a href="https://toml.io">TOML</a>, un format de fichier de configuration (comparable au YAML). C’est l’embryon d’un mouvement pour adopter une configuration qui soit <strong>déclarative</strong> plutôt qu’exécutable. Le TOML avait déjà été adopté par l’outil Cargo de Rust.</p>
<p>Le <code>pyproject.toml</code> contient une table <code>build-system</code> qui déclare quels paquets doivent être installés pour exécuter le <code>setup.py</code>. Elle se présente ainsi :</p>
<pre><code class="toml"><span class="p">[</span><span class="n">build-system</span><span class="p">]</span>
<span class="n">requires</span> <span class="o">=</span> <span class="p">[</span><span class="s">"setuptools>=61"</span><span class="p">]</span></code></pre>
<p>Cet exemple pourrait être utilisé dans un projet dont le <code>setup.py</code> a besoin de <code>setuptools</code> version 61 au moins.</p>
<p>Mais ce n’est pas tout. La PEP 518 définit aussi une table <code>tool</code> avec des sous-tables arbitraires <code>tool.nom-d-outil</code>, qui permet à des outils arbitraires de lire des options de configuration, qu’ils soient des outils de <em>packaging</em>, des reformateurs de code, des linters, des générateurs de documentation,… <strong>pyproject.toml est donc devenu un fichier de configuration universel pour les projets Python.</strong> (Anecdote : au départ, il n’était pas prévu pour cela, mais il a commencé à être utilisé comme tel, si bien que la PEP 518 a été modifiée a posteriori pour structurer cela dans une table <code>tool</code>.)</p>
<p>La deuxième étape a été la <a href="https://peps.python.org/pep-0517">PEP 517</a>. Elle va plus loin, en standardisant une interface entre deux outils appelés <strong>build frontend</strong> et <strong>build backend</strong>. Le <em>build frontend</em> est l’outil appelé par l’utilisateur, par exemple <code>pip</code>, qui a besoin de compiler le paquet pour en faire un wheel installable (ou une <em>sdist</em>, soit une archive du code source avec quelques métadonnées, mais c’est un détail). Le <em>build backend</em> est un outil comme <code>setuptools</code> qui va créer le wheel.</p>
<p>Le rôle du <em>build frontend</em> est assez simple. Pour schématiser, il lit la valeur <code>build-system.requires</code> du <code>pyproject.toml</code>, installe cette liste de dépendances, puis lit la valeur <code>build-system.build-backend</code>, importe le <em>build backend</em> en Python, et appelle la fonction <code>backend.build_wheel()</code>.</p>
<p>Voici un exemple de <code>pyproject.toml</code> utilisant les PEP 517 et 518 avec <code>setuptools</code> comme <em>build backend</em> :</p>
<pre><code class="toml"><span class="p">[</span><span class="n">build-system</span><span class="p">]</span>
<span class="n">requires</span> <span class="o">=</span> <span class="p">[</span><span class="s">"setuptools"</span><span class="p">]</span>
<span class="n">build-backend</span> <span class="o">=</span> <span class="s">"setuptools.build_meta"</span></code></pre>
<p>Ainsi, il est devenu possible d’écrire et utiliser des <em>build backends</em> complètement différents de <code>setuptools</code>, sans même de fichier <code>setup.py</code>. L’un des premiers <em>build backends</em> alternatifs a été <strong>flit</strong>, qui cherche à être l’opposé de <code>setuptools</code> : le plus simple possible, avec le moins de configuration possible.</p>
<p>La PEP 517 décrit bien son objectif :</p>
<blockquote>
<p>While distutils / setuptools have taken us a long way, they suffer from three serious problems : (a) they’re missing important features like usable build-time dependency declaration, autoconfiguration, and even basic ergonomic niceties like DRY-compliant version number management, and (b) extending them is difficult, so while there do exist various solutions to the above problems, they’re often quirky, fragile, and expensive to maintain, and yet (c) it’s very difficult to use anything else, because distutils/setuptools provide the standard interface for installing packages expected by both users and installation tools like pip.</p>
<p>Previous efforts (e.g. distutils2 or setuptools itself) have attempted to solve problems (a) and/or (b). This proposal aims to solve (c).</p>
<p>The goal of this PEP is get distutils-sig out of the business of being a gatekeeper for Python build systems. If you want to use distutils, great ; if you want to use something else, then that should be easy to do using standardized methods.</p>
</blockquote>
<h2 id="toc-la-pep-621-un-peu-de-standardisation">La PEP 621, un peu de standardisation</h2>
<p>Suite aux PEP 518 et 517, plusieurs <em>build backends</em> alternatifs ont émergé. Bien sûr, tous les <em>build backends</em> avaient des manières différentes de spécifier les métadonnées du projet comme le nom, la version, la description, etc.</p>
<p>La <a href="https://peps.python.org/pep-0621">PEP 621</a> a standardisé cette partie de la configuration, en définissant une nouvelle section du <code>pyproject.toml</code>, la table <code>project</code>. Concrètement, elle peut ressembler à :</p>
<pre><code class="toml"><span class="p">[</span><span class="n">project</span><span class="p">]</span>
<span class="n">name</span> <span class="o">=</span> <span class="s">"my-project"</span>
<span class="n">version</span> <span class="o">=</span> <span class="s">"0.1"</span>
<span class="n">description</span> <span class="o">=</span> <span class="s">"My project does awesome things."</span>
<span class="n">requires-python</span> <span class="o">=</span> <span class="s">">=3.8"</span>
<span class="n">authors</span> <span class="o">=</span> <span class="p">[{</span><span class="n">name</span> <span class="o">=</span> <span class="s">"Me"</span><span class="p">,</span> <span class="n">email</span> <span class="o">=</span> <span class="s">"[email protected]"</span><span class="p">}]</span>
<span class="p">...</span></code></pre>
<p>En effet, écrire les métadonnées n’est pas la partie la plus intéressante d’un <em>build backend</em>. Les vraies différences se trouvent, par exemple, dans la prise en charge des extensions C ou C++, ou bien dans des options de configuration plus avancées. La PEP 621 permet de changer plus facilement de <em>build backend</em> en gardant l’essentiel de la configuration de base.</p>
<p>De plus, elle encourage la configuration statique, par opposition au <code>setup.py</code> de <code>setuptools</code>. L’avantage d’une configuration statique est sa fiabilité : aucune question ne se pose sur l’environnement d’exécution du <code>setup.py</code>, sa portabilité, etc.</p>
<p>Pour donner un exemple concret, on apprend dans <a href="https://peps.python.org/pep-0597/#using-the-default-encoding-is-a-common-mistake">cette section</a> de la PEP 597 que les développeurs écrivaient souvent dans le <code>setup.py</code> un code qui lit le README avec l’encodage système au lieu de l’UTF-8, ce qui peut rendre le paquet impossible à installer sous Windows. C’est le genre de problèmes systémiques qui sont éliminés par la configuration statique.</p>
<p>Malgré tout, la configuration dynamique reste utile. C’est typiquement le cas pour la valeur de <code>version</code>, qui est avantageusement calculée en consultant le système de contrôle de version (par exemple avec <code>git describe</code> pour Git). Dans ces situations, on peut marquer la valeur comme étant calculée dynamiquement avec</p>
<pre><code class="toml"><span class="p">[</span><span class="n">project</span><span class="p">]</span>
<span class="n">dynamic</span> <span class="o">=</span> <span class="p">[</span><span class="s">"version"</span><span class="p">]</span></code></pre>
<p>C’est alors au <em>build backend</em> de déterminer la valeur par tout moyen approprié (éventuellement configuré dans la table <code>tool</code>).</p>
<h2 id="toc-lémergence-doutils-tout-en-un-alternatifs">L’émergence d’outils tout-en-un alternatifs</h2>
<p>Cet historique est très loin d’être exhaustif, et pourtant on sent déjà la prolifération d’outils différents. Face à la confusion qui en résulte, des développeurs ont tenté d’écrire des outils « tout-en-un » qui rassemblent à peu près toutes les fonctionnalités en une seule interface cohérente : installation, <em>build frontend</em>, <em>build backend</em>, gestion des environnements virtuels, installation d’une nouvelle version de Python, mise à jour d’un <em>lock file</em>, etc. Parmi eux, on peut notamment citer <a href="https://python-poetry.org">poetry</a>, développé depuis 2018 (<a href="https://python-poetry.org/history">source</a>), qui se distingue en ne participant pas à la PyPA et en réimplémentant bien plus de choses que d’autres (notamment en ayant son propre résolveur de dépendances distinct de celui de <code>pip</code>). On peut penser aussi à <a href="https://hatch.pypa.io">hatch</a>, qui, lui, fait partie de la PyPA et ne fait pas autant de choses, mais s’intègre mieux à l’existant. Et pour mentionner le dernier-né, il y a également <a href="https://rye-up.com">rye</a>, qui cherche à modifier la façon dont Python est <em>boostrapé</em>, en utilisant exclusivement des Pythons gérés par lui-même, qui ne viennent pas du système, et en étant écrit lui-même en Rust plutôt qu’en Python.</p>
<h2 id="toc-conclusion">Conclusion</h2>
<p>J’espère que cet historique permet de mieux comprendre pourquoi le <em>packaging</em> est tel qu’il est aujourd’hui.</p>
<p>L’un des facteurs majeurs est l’omniprésence des extensions C, C++ ou maintenant Rust qui doivent être précompilées. C’est la raison essentielle pour laquelle <code>conda</code> et tout son écosystème existent et sont séparés du monde de la PyPA.</p>
<p>Un autre facteur est à chercher dans les problèmes de conception de <code>distutils</code>, un code qui date de l’époque des premiers gestionnaires de paquets et qui n’était pas prêt à accompagner Python pour vingt ans. C’est pour cela qu’il a été forké si souvent, et c’est pour en finir avec l’hégémonie de son fork <code>setuptools</code> que les PEP 518 et 517 ont volontairement ouvert le jeu aux outils alternatifs.</p>
<p>Il faut enfin voir que la PyPA n’a jamais été un groupe unifié autour d’un outil, et qu’il est difficile de changer de modèle social.</p>
<p>Dans la deuxième dépêche, je ferai un tour complet de l’état actuel, en présentant tous les outils et les liens entre eux.</p>
</div><div><a href="https://linuxfr.org/news/l-installation-et-la-distribution-de-paquets-python-1-4.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/133784/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/l-installation-et-la-distribution-de-paquets-python-1-4#comments">ouvrir dans le navigateur</a>
</p>
jeanasBenoît Sibaudalberic89 🐧gUIL'intendant zonardnonaspalm123https://linuxfr.org/nodes/133784/comments.atomtag:linuxfr.org,2005:News/413802023-02-03T10:35:21+01:002023-02-05T17:34:07+01:00Programme de la PyConFR 23Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>La PyConFR, l'évènement de la communauté francophone du langage de programmation python, aura lieu du 16 au 19 février 2023 à Bordeaux. L'évènement est gratuit mais l'inscription au préalable est obligatoire.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646973637573732e616670792e6f72672f75706c6f6164732f64656661756c742f6f726967696e616c2f31582f373161376134333266316366643836656234323364333163633230356434346164353238323965382e6a706567/71a7a432f1cfd86eb423d31cc205d44ad52829e8.jpeg" alt="PyConFR du 16 au 19 février à Bordeaux" title="Source : https://discuss.afpy.org/uploads/default/original/1X/71a7a432f1cfd86eb423d31cc205d44ad52829e8.jpeg"></p>
<p>Le programme vient de paraître, le sommaire des conférences, des ateliers et des sprints vous attend dans la suite de cette dépêche.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://www.pycon.fr/2023/fr/schedule.html" hreflang="fr" href="https://linuxfr.org/redirect/111694">Programme</a></li><li>lien nᵒ 2 : <a title="https://www.pycon.fr/2023/fr/index.html" hreflang="fr" href="https://linuxfr.org/redirect/111695">PyConFR</a></li></ul><div><p>Voici le programme qui vient de paraître : (plus d'infos sur le site)</p>
<p><strong>Conférences</strong> (Samedi et Dimanche)</p>
<ul>
<li> CoWorks, a compositionnal microservices framework using Flask/AWS Lambda and Airflow</li>
<li> Développement VRAIMENT cross-platform avec Python</li>
<li> Portage Python sur Webassembly</li>
<li> Faire du Python professionnel</li>
<li> Python moderne et fonctionnel pour des logiciels robustes</li>
<li> Accessibilité numérique : faire sa part quand on est développeur·euse backend</li>
<li> Devenir incollable sur les callables !</li>
<li> Déployer du backend en 2023</li>
<li> « Fixed bugs » n’est peut-être pas le meilleur message de commit</li>
<li> Apport du langage Python dans un service de recherche hospitalière pour mener des analyses de deep learning</li>
<li> Documentation, logiciel libre et pérennité en arts numériques</li>
<li> Où il est question de gaufre et d’Internet</li>
<li> Garder le contrôle de ses données géolocalisées avant de les partager</li>
<li> Lutter contre le dérèglement climatique avec Django</li>
<li> sphinx-lint : un linter pour ta doc</li>
<li> Continuous performance analysis for Python</li>
<li> NucliaDB, une base de données pour le machine learning et les données non-structurées</li>
<li> Driving down the Memray lane - Profiling your data science work</li>
<li> Trying no GIL on scientific programming</li>
<li> Contribuer à l’open source sur des projets Python… sans coder</li>
<li> Fear the mutants. Love the mutants.</li>
<li> Running real-time machine learning analytics on traces</li>
<li> Python Côte d'Ivoire : Défis et Perspectives</li>
<li> Domain-driven design: what can Python do for you?</li>
<li> Supercharging Jupyter notebooks for effective storytelling</li>
<li> Rejoignez le Fediverse, ajoutez ActivityPub à votre site !</li>
<li> À la découverte de Polars (ou pourquoi vous pourriez quitter Pandas)</li>
<li> Introduction to Sigstore: cryptographic signatures made easier</li>
<li> Python for microcontrollers</li>
<li> Monorepo Python avec environnements de développement reproductibles et CI scalable</li>
<li> The power of AWS Chalice for quick serverless API development in Python</li>
<li> Giving and receiving great feedback through PRs</li>
<li> Uncovering Python’s surprises: a deep dive into gotchas</li>
<li> Python web performance 101: uncovering the root causes</li>
<li> From a Python script to an open-source project</li>
<li> Une bonne quantité de Python peut-elle rendre Firefox moins vulnérable aux supply chain attacks ?</li>
<li> Apprendre Python, c’est pour tout le monde en 2023 ! 💞</li>
<li> Cerveau, Biomarqueurs et Deep Learning</li>
<li> Apprentissage statistique adapté aux données sales avec dirty-cat</li>
<li> Let’s exploit pickle, and skops to the rescue!</li>
<li> Interactive HoloViz Visualization and Dashboards</li>
<li> Cloud infrastructure from Python code: how far could we go?</li>
<li> Traitement de données géographiques avec Rasterio, NumPy, Fiona et Shapely</li>
<li> REX analyse antivirus des fichiers de la plateforme emplois de l’inclusion</li>
<li> J'ai hacké ma chaudière pour avoir un thermostat !</li>
<li> Psycopg, troisième du nom</li>
<li> OCR : apprenez à extraire la substantifique moelle de vos documents scannés</li>
<li> Réinventer le tour du monde en (beaucoup) moins de 80 jours</li>
<li> Django Admin comme framework pour développer des outils internes</li>
<li> Geographic visualization using Streamlit</li>
<li> Transformez vos algorithmes de données/IA en applications web complètes en un rien de temps avec Taipy</li>
<li> Un membre très discret de la famille Jupyter mais pourtant si utile !</li>
<li> Interactive web pages with Django or Flask, without writing JavaScript</li>
<li> Je suis nul·le !</li>
<li> Monitorez vos applications Python (et pas uniquement votre infra)</li>
<li> Nua, un PaaS open source en Python pour l'auto-hébergement de vos applications</li>
<li> GEMSEO : une bibliothèque pour l’optimisation multi-disciplinaire</li>
<li> Save Sheldon: Sarcasm Detection for the Uninitiated!</li>
</ul>
<p><strong>Ateliers</strong> (Samedi et Dimanche)</p>
<ul>
<li> Le Zen de Python appliqué à la production de jeux vidéo</li>
<li> Initiation à Django à travers la création d'un blog</li>
<li> Faire un module Tryton</li>
<li> Le réseau de neurones qui écrivait des romans</li>
<li> Comment créer des applications web de data science époustouflantes en Python - Tutoriel Taipy</li>
<li> Meme Saheb: using Dank Learning to generate original meme captions</li>
<li> Mettre le web en page(s) : générer un document PDF avec HTML et CSS</li>
</ul>
<p><strong>Sprints</strong> (Jeudi et Vendredi)</p>
<ul>
<li> Traduction de la doc’ de Python en Français</li>
<li> Sprint sur le projet « Witness Angel »</li>
<li> Complete the work on Modoboa v2</li>
<li> Améliorer l'algorithme de détection de proximité de polygone pour l'étude du potentiel géothermique individuel (transition EnR)</li>
<li> Amélioration de ReservoirPy, un outil simple de Reservoir Computing</li>
<li> Developing community plugins for Argilla: an open-source platform for data-centric NLP</li>
<li> Improve tests for Zou/Kitsu API (Flask)</li>
<li> Sardine : improvisation musicale avec Python 3.10+</li>
<li> Développements de correctifs et tests des modules Ansible</li>
<li> Release d'AnyBlok 2.0.0</li>
<li> Rajout de fonctionnalités et créations de démos complètes et interactives pour Taipy</li>
</ul>
</div><div><a href="https://linuxfr.org/news/programme-de-la-pyconfr-23.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/130201/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/programme-de-la-pyconfr-23#comments">ouvrir dans le navigateur</a>
</p>
MelcoreBenoît SibaudPierre Jarillontedhttps://linuxfr.org/nodes/130201/comments.atomtag:linuxfr.org,2005:News/412752022-11-24T16:23:16+01:002022-11-24T16:23:16+01:00Conférence & Atelier PyConFr - du 16 au 19 février 2023 à BordeauxLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Quatre journées consacrées au langage Python, la PyConFr revient après deux ans d’absence pour proposer des Sprints, où l’on se retrouve à l’Université de Bordeaux pour coder ensemble le jeudi et vendredi, et des conférences le week-end afin de découvrir les expériences de chacun avec le langage Python. </p>
<p>L’accès est gratuit, ouvert à toutes et tous, cependant l’inscription préalable est nécessaire.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f646973637573732e616670792e6f72672f75706c6f6164732f64656661756c742f6f726967696e616c2f31582f373161376134333266316366643836656234323364333163633230356434346164353238323965382e6a706567/71a7a432f1cfd86eb423d31cc205d44ad52829e8.jpeg" alt="PyConFr 2023 du 16 au 19 février à Bordeaux" title="Source : https://discuss.afpy.org/uploads/default/original/1X/71a7a432f1cfd86eb423d31cc205d44ad52829e8.jpeg"></p>
</div><ul><li>lien nᵒ 1 : <a title="https://www.pycon.fr/2023/fr/index.html" hreflang="fr" href="https://linuxfr.org/redirect/111337">Site de la PyConFr</a></li><li>lien nᵒ 2 : <a title="https://www.helloasso.com/associations/afpy/evenements/pyconfr-2023" hreflang="fr" href="https://linuxfr.org/redirect/111338">Inscription à la PyConFr</a></li><li>lien nᵒ 3 : <a title="https://cfp-2023.pycon.fr/" hreflang="fr" href="https://linuxfr.org/redirect/111339">L' Appel à Participation</a></li></ul><div><h2 id="toc-conférences-et-ateliers-samedi-et-dimanche">Conférences et ateliers (samedi et dimanche)</h2>
<p>Durant le week-end, vous aurez l’occasion de participer à des présentations sur des sujets variés, autour du langage Python, de ses usages, des bonnes pratiques, des retours d’expériences, des partages d’idées…</p>
<h2 id="toc-codage-participatif-sprints-jeudi-et-vendredi">Codage participatif « Sprints » (jeudi et vendredi)</h2>
<p>Les développeurs et développeuses de différents projets open source se rejoignent pour coder ensemble. Chaque personne est la bienvenue pour contribuer, et nous cherchons également à accompagner les débutant·e·s.</p>
<h3 id="toc-restauration-sur-place">Restauration sur place</h3>
<p>Des food trucks seront à disposition.</p>
<h3 id="toc-proposer-une-conférence-ou-un-atelier">Proposer une conférence ou un atelier</h3>
<p>Même un débutant ou une débutante a quelque chose à raconter, alors venez proposer votre conférence sur le site de l’appel à participation.</p>
</div><div><a href="https://linuxfr.org/news/conference-atelier-pyconfr-du-16-au-19-fevrier-2023-a-bordeaux.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/129377/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/conference-atelier-pyconfr-du-16-au-19-fevrier-2023-a-bordeaux#comments">ouvrir dans le navigateur</a>
</p>
MelcoreYsabeau 🧶Xavier Teyssierpalm123https://linuxfr.org/nodes/129377/comments.atomtag:linuxfr.org,2005:News/411992022-09-22T19:35:27+02:002022-09-24T09:11:45+02:00Des nouvelles de WeasyPrint, ou comment développer du libre à (presque) plein tempsLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p><a href="https://weasyprint.org">WeasyPrint</a> est un générateur de documents qui transforme du HTML/CSS en PDF. C’est écrit en Python, c’est libre (bah oui, sinon on n’en parlerait pas ici), et nous en avions déjà discuté ici il y a quelques années dans <a href="//linuxfr.org/news/premiere-version-stable-pour-weasyprint">un petit article</a>.</p>
<p>Avec le temps (plus de 11 ans depuis le premier commit, que le temps passe vite ma p’tite dame…), le logiciel a gagné une sacrée ribambelle d’utilisateurs avec plus de 750 000 téléchargements par mois. Parmi tous ces gens qui utilisent WeasyPrint, on a forcément rencontré plein de gens avec plein d’idées pour générer plein de drôles de trucs ! Nous avons croisé entre autres des rapports de sécurité informatique 🖥️, des livres de jeu de rôle 🎮️, des tickets 🎫️, des documents scientifiques 🧮️, des factures de sites de vente en ligne 📄️, des compte-rendus biologiques ⚛️, des modes d’emploi de fours 🧑🍳️, des lettres officielles 💌️, des étiquettes électroniques 🏷️, des affiches promotionnelles en pharmacies ⚕️, des diplômes universitaires 🎓️… </p>
<p>Forts de ce petit succès, Lucie Anglade et moi (Guillaume Ayoub) avons créé depuis deux ans une structure qui s’appelle <a href="https://courtbouillon.org">CourtBouillon</a> (oui, parce que notre autre passion est la bonne nourriture) dédiée au développement de WeasyPrint et de ses dépendances. Nous avons donc pu passer beaucoup de temps à travailler sur le logiciel et apporter plein de nouveautés, tout en nous posant beaucoup de questions pour assurer un modèle économique viable. Voilà ce que l’on aimerait partager avec vous.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://weasyprint.org/" hreflang="en" href="https://linuxfr.org/redirect/111073">Le site de WeasyPrint</a></li><li>lien nᵒ 2 : <a title="https://courtbouillon.org/" hreflang="en" href="https://linuxfr.org/redirect/111074">Le site de CourtBouillon</a></li><li>lien nᵒ 3 : <a title="https://github.com/Kozea/WeasyPrint/" hreflang="en" href="https://linuxfr.org/redirect/111075">Le dépôt de WeasyPrint</a></li><li>lien nᵒ 4 : <a title="https://thym.courtbouillon.org/1" hreflang="en" href="https://linuxfr.org/redirect/111076">Le sondage en cours</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-deux-ans-%C3%A0-toute-vitesse">Deux ans à toute vitesse</a></li>
<li><a href="#toc-le-temps-et-largent">Le temps et l’argent</a></li>
<li><a href="#toc-le-futur">Le futur</a></li>
<li><a href="#toc-en-attendant-la-suite">En attendant la suite</a></li>
</ul>
<h2 id="toc-deux-ans-à-toute-vitesse">Deux ans à toute vitesse</h2>
<p>Depuis le début de l’an dernier, nous avons publié <a href="https://github.com/Kozea/WeasyPrint/releases">4 versions majeures</a> qui englobent une bonne liste de fonctionnalités dont voici les plus importantes :</p>
<ul>
<li>les notes de bas de page,</li>
<li>les coupures de blocs après un certain nombre de lignes (pour que le texte ne dépasse pas de la page),</li>
<li>les coupures forcées ou interdites dans les colonnes,</li>
<li>le respect de la norme PDF/A (pour avoir des documents archivables),</li>
<li>les coupures de pages dans les cellules d’un tableau, les blocs flottants et les blocs absolus,</li>
<li>la gestion des polices bitmap (pratique pour faire des étiquettes électroniques parfaites au pixel près),</li>
<li>l’insertion de points de suite (mais si, vous savez ce que c’est, ce sont les petits points entre le nom d’un chapitre et le numéro de page dans une table des matières),</li>
<li>la génération reproductible de fichiers PDF,</li>
<li>le support des principaux sélecteurs CSS de niveau 4 (comme <code>:is()</code>, <code>:where()</code>, <code>:has()</code>),</li>
<li>sans oublier une génération bien plus rapide et des fichiers générés plus petits.</li>
</ul>
<p>Nous en avons également profité pour créer <a href="https://github.com/CourtBouillon/pydyf">pydyf</a>, une bibliothèque bas niveau de génération de PDF, histoire d’avoir les mains libres pour ajouter certaines fonctionnalités. C’était une volonté depuis de longues années (pour supporter le format PDF/A par exemple) mais la spécification PDF a nécessité un peu de temps avant d’être apprivoisée 😉️.</p>
<p>Pour parler de tout cela, nous avons écrit <a href="https://doc.courtbouillon.org/weasyprint/">une toute nouvelle documentation</a> que nous espérons mieux organisée et plus claire. Nous avons également rédigé <a href="https://www.stella.coop/blog/00026-des-documents-adaptes-a-vos-envies-et-vos-formats">une longue série d’articles</a> avec nos copains de <a href="https://www.madcats.fr/">Madcats</a> qui ont créé de très jolis documents dont vous pouvez vous inspirer pour créer les vôtres.</p>
<p>En bref, on n’a pas chômé. Mais où a-t-on trouvé tout ce temps ?</p>
<h2 id="toc-le-temps-et-largent">Le temps et l’argent</h2>
<p>La raison d’être de CourtBouillon est de créer, développer et maintenir des logiciels libres. Et pour cela, il faut avoir du temps, beaucoup de temps, <strong>vraiment</strong> beaucoup de temps.</p>
<p>Tout le monde veut toujours plein, plein, plein de fonctionnalités, et nous avons un avantage de ce côté-là : CSS en voit fleurir de nombreuses à un rythme soutenu. Comme nous nous appuyons rigoureusement sur ces spécifications, nous avons donc « juste » à les implémenter. Évidemment, à chaque fois qu’une nouvelle propriété est supportée par les navigateurs, les gens se ruent sur nous pour demander pourquoi WeasyPrint ne la supporte toujours pas alors que Chrome et Firefox la gèrent très bien depuis au moins 2 semaines (j’éxagère à peine 😁️). Pour la faire court : ça prend du temps.</p>
<p>Au-delà du code et de ses fonctionnalités, nous passons des jours entiers à trier les tickets, répondre aux questions, tweeter, écrire des articles, corriger des bugs et peaufiner la documentation. Ce travail est peu visible mais il prend bien plus de temps que ce que la plupart des utilisatrices et des utilisateurs imaginent. C’est pourtant un travail de fond nécessaire pour garder nos projets en bonne santé et ne pas crouler rapidement sous les demandes insatisfaites.</p>
<p>Encore au-delà ce travail peu valorisé se cache tout le travail de l’ombre que l’on ne voit pas du tout. Lire des spécifications, que ce soit pour CSS ou PDF, est devenu une seconde nature pour nous, et nous nous sommes habitués au langage étrange que l’on trouve dans ces documents. Certaines rumeurs disent même que nous en rêvons la nuit… Nous devons également faire particulièrement attention à la qualité du code. Nous sommes une toute petite équipe et nous avons, mine de rien, à maintenir un moteur de rendu HTML/CSS… Il est par conséquent très important de s’assurer au quotidien que la dette technique ne s’accumule pas et que l’architecture globale est toujours bien solide, sous peine de se retrouver sous l’eau dans le futur à l’ajout de la moindre fonctionnalité. Au-delà de l’interminable suite de tests (car oui, dans WeasyPrint nous avons plus de lignes de Python pour les tests que pour le code), il est nécessaire de retoucher l’architecture de nos bibliothèques de temps en temps, tout comme nous devons supporter les dernières versions de Python et des diverses dépendances que nous avons.</p>
<p>Pour avoir tout ce temps et en même temps gagner quelque argent pour manger (parce qu’on aime beaucoup ça, je vous le rappelle), nous fournissons divers services à des clients utilisateurs un peu partout dans le monde. Certaines fonctionnalités sont ainsi payées par des entreprises qui ont des besoins spécifiques et sont ensuite ravies d’avoir une belle version toute neuve qui répond parfaitement à leurs besoins. D’autres nous contactent pour avoir de l’aide à la création de documents, nous nous occupons alors de créer du HTML et du CSS aux petits oignons (miam) en accord avec leurs maquettes et leur charte graphique. Nous avons enfin un <a href="https://opencollective.com/courtbouillon">système de sponsoring et de dons</a> qui ouvre droit à afficher un beau logo sur notre site et à avoir un support prioritaire par mail pour les questions techniques.</p>
<p>Et pour l’instant, ça marche.</p>
<h2 id="toc-le-futur">Le futur</h2>
<p>Même si CourtBouillon est jeune, nous arrivons actuellement à vivre en passant la grande majorité de notre temps de travail sur le libre.</p>
<p>Bien sûr, c’est une situation extrêmement grisante et souvent très épanouissante : qui n’a jamais rêvé de vivre du libre dans ce bon vieux repaire de libristes extrémistes qu’est LinuxFR 😍️ ? Nous avons travaillé notre communication pour toucher des personnes qui partagent nos valeurs, ce qui nous a amenés à rencontrer des gens absolument formidables. Nous avons pu croiser la route de clients disséminés un peu partout dans le monde, nous ouvrir à des problématiques que nous ne connaissions pas et apporter notre aide à des personnes pour lesquelles nous avons beaucoup d’estime et de sympathie…</p>
<p>Il y a bien sûr des contreparties à tout ce bonheur. Au niveau financier, si l’activité actuelle nous permet de nous rémunérer (et c’est déjà appréciable au bout de deux ans), nous sommes loin des standards auxquels nous pourrions postuler en tant qu’ingénieurs en informatique. Nos sponsors et nos clients nous apportent aujourd’hui la majorité de nos revenus, nous sommes donc évidemment soumis aux aléas de la demande avec une alternance de semaines chargées lorsque nous avons beaucoup de clients et des semaines plus creuses où nous pouvons nous atteler au travail invisible. Nous essayons donc au maximum de développer les dons et les sponsors récurrents pour assurer autant que possible la stabilité de notre modèle.</p>
<p>Au niveau des fonctionnalités qui arrivent (parce que c’est ça qui intéresse les gens, hein !), nous avons ouvert un <a href="https://thym.courtbouillon.org/1">sondage</a> pour mieux connaître les besoins attendus. <a href="https://www.courtbouillon.org/blog/00010-weasyprint-survey-2021">Celui de l’an dernier</a> nous avait éclairés sur les points à traiter en priorité, nous avons donc pu mettre notre énergie au service des attentes les plus grandes… et bien sûr des clients qui ont gracieusement financé certains de ces développements ! Plusieurs fonctionnalités toutes fraîches sont déjà bien avancées : nous proposerons par exemple dans les prochains mois la possibilité de générer des fichiers PDF plus accessibles (avec le support partiel de PDF/UA) et le support des <a href="https://developer.mozilla.org/fr/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide">polices variables</a>.</p>
<p>Et un jour, peut-être, nous pourrons enfin nous lancer à corps perdu dans le support de <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout">CSS Grid</a>… si le temps nous le permet 😀️.</p>
<h2 id="toc-en-attendant-la-suite">En attendant la suite</h2>
<p>En attendant la suite des aventures, n’hésitez pas à nous suivre, à jeter un coup d’œil à WeasyPrint si vous ne l’avez jamais essayé, à ouvrir des tickets pour râler si vous rencontrez des problèmes, à nous soutenir si vous aimez ce que l’on fait, et à nous solliciter si vous avez des envies particulières.</p>
<p>Pour celles et ceux qui sont moins intéressés par le côté technique, nous sommes également ouverts pour discuter de gestion de projets libres, du lien à la communauté, de modèles économiques, et de tout ce qui pourrait vous intéresser sur le sujet !</p>
</div><div><a href="https://linuxfr.org/news/des-nouvelles-de-weasyprint-ou-comment-developper-du-libre-a-presque-plein-temps.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/128823/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/des-nouvelles-de-weasyprint-ou-comment-developper-du-libre-a-presque-plein-temps#comments">ouvrir dans le navigateur</a>
</p>
liZeBenoît SibaudXavier TeyssierYsabeau 🧶https://linuxfr.org/nodes/128823/comments.atomtag:linuxfr.org,2005:News/411182022-07-16T00:32:11+02:002022-07-16T13:43:33+02:00PyPI déploie le système 2FA pour les projets critiques écrits en PythonLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>PyPI (de l’anglais « Python Package Index ») est le dépôt tiers officiel du langage de programmation Python. Son objectif est de doter la communauté des développeurs Python d’un catalogue complet recensant tous les paquets Python libres.<br>
Google, par l’intermédiaire de l’Open Source Security Foundation (OpenSSF) de la Linux Foundation, s’est attaqué à la menace des paquets malveillants et des attaques de la chaîne d’approvisionnement des logiciels open source. Elle a trouvé plus de 200 paquets JavaScript et Python malveillants en un mois, ce qui pourrait avoir des « conséquences graves » pour les développeurs et les organisations pour lesquelles ils écrivent du code lorsqu’ils les installent.<br>
PyPI déploie le <a href="https://fr.wikipedia.org/wiki/Double_authentification">système 2FA (pour double authentification ou authentification à deux facteurs)</a> pour les projets critiques écrits en Python. </p>
</div><ul><li>lien nᵒ 1 : <a title="https://pypi.org/security-key-giveaway/" hreflang="en" href="https://linuxfr.org/redirect/110809">Annonce implementation 2FA</a></li></ul><div><p>Ainsi, comme il est possible de le lire sur le compte <a href="https://twitter.com/pypi/status/1545455297388584960">twitter</a> de PyPI (version nitter <a href="https://nitter.fdn.fr/pypi/status/1545455297388584960">fdn.fr</a> ou <a href="https://twitter.censors.us/pypi/status/1545455297388584960">censors.us</a>), le dépôt va implémenter le 2FA obligatoire pour les projets critiques écrits en Python.</p>
<blockquote>
<p>Nous avons commencé à mettre en place une exigence 2FA : bientôt, les responsables de projets critiques devront avoir activé 2FA pour publier, mettre à jour ou modifier ces projets.</p>
<p>Pour s’assurer que ces mainteneurs puissent utiliser des méthodes 2FA fortes, nous distribuons également 4000 clés de sécurité matérielles !</p>
</blockquote>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f707970692e6f72672f7374617469632f696d616765732f746974616e2e38353361653132372e706e67/titan.853ae127.png" alt="Clé Titan" title="Source : https://pypi.org/static/images/titan.853ae127.png"></p>
<p>Les 4000 clés de sécurité matérielles sont des clés de sécurité Google Titan. Ces clés seront distribuées aux responsables de projets, avec l’aide de l’équipe de sécurité open source de Google.</p>
<h3 id="toc-vente-autorisées-mais-pas-partout">Vente autorisées, mais pas partout</h3>
<p>La vente des clés Titan n’est autorisée que dans certaines régions géographiques. Ainsi, seuls les développeurs d’Autriche, de Belgique, du Canada, de France, d’Allemagne, d’Italie, du Japon, d’Espagne, de Suisse, du Royaume-Uni et des États-Unis peuvent en recevoir une gratuitement.</p>
<h3 id="toc-le-2fa-en-progressions">Le 2FA en progressions</h3>
<p>Il y a déjà un certain nombre de développeurs qui ont activé le 2FA, ainsi, et en toute transparence selon PyPI, le dépôt <a href="https://p.datadoghq.com/sb/7dc8b3250-389f47d638b967dbb8f7edfd4c46acb1?from_ts=1657460532354&to_ts=1657546932354&live=true">publie les données sur les comptes 2FA</a>. <br>
Selon PyPI, il y a déjà plus de 28 600 utilisateurs avec 2FA activé, dont près de 27 000 utilisant une application <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password">TOTP</a> (par exemple FreeOTP+ sur mobile Android ou via KeepassXC sur un ordinateur).</p>
<p>La PSF (Python Software Foundation) indique qu’elle considère comme critique tout projet figurant dans le top 1 % des téléchargements au cours des six derniers mois. Actuellement, il y a plus de 350 000 projets sur PyPI, ce qui signifie que plus de 3 500 projets sont considérés comme critiques. PyPI calcule ce chiffre quotidiennement, de sorte que le don de Titan devrait permettre de couvrir une grande partie des mainteneurs clés, mais pas tous. <br>
Bien que la plupart des développeurs soient familiers avec le système 2FA, cette exigence pourrait créer des difficultés de connexion.<br><br>
En effet, si, par exemple, un utilisateur perd sa clé 2FA et qu’il n’a pas configuré d’autres options 2FA, il risque de perdre l’accès à son compte et donc la nécessité de récupérer entièrement un compte, ce qui est lourd et prend du temps à la fois pour les mainteneurs et les administrateurs de PyPI. Il serait donc préférable d’avoir plusieurs méthodes 2FA pour réduire les perturbations potentielles si l’une d’entre elles est perdue.</p>
<h2 id="toc-conclusion">Conclusion</h2>
<p>Est-ce que le 2FA va réellement sécuriser les projets Python ? C’est l’avenir qui le dira. En attendant, il semblerait que PyPI et plus globalement la <a href="https://www.python.org/psf/">PSF</a> ait décidé de prendre les choses en main quant à la sécurité des projets Python.</p>
<p><abbr title="Note de la modération">N. D. M. :</abbr> voir aussi le journal <a href="//linuxfr.org/users/serge_ss_paille/journaux/pypi-et-les-projets-critiques">PyPI et les projets critiques</a> sur le même sujet, abordant notamment l'ajout de contraintes supplémentaires (complexité, temps, éventuel coût matériel, vérification régulière du 2FA à effectuer, etc.) pour les passionnés/hobbyistes.</p>
</div><div><a href="https://linuxfr.org/news/pypi-deploie-le-systeme-2fa-pour-les-projets-critiques-ecrits-en-python.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/128285/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/pypi-deploie-le-systeme-2fa-pour-les-projets-critiques-ecrits-en-python#comments">ouvrir dans le navigateur</a>
</p>
Startrek1701Benoît SibaudXavier Teyssierpalm123https://linuxfr.org/nodes/128285/comments.atomtag:linuxfr.org,2005:News/410222022-05-28T19:27:51+02:002022-05-28T19:27:51+02:00Environnement moderne de travail PythonLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><h2 id="toc-environnement-moderne-de-travail-python">Environnement moderne de travail Python</h2>
<p>Si vous développez ou utilisez des programmes s’exécutant au-dessus de l’interpréteur Python, il peut arriver que vous vous retrouviez avec un environnement très dégradé sur votre poste de travail..</p>
<p>Je propose ici de découvrir un ensemble d’outils permettant de configurer des environnements Python qui vous éviteront de polluer votre système ou vos futurs environnements de développement. En effet, entre votre système Linux et les multiples projets de développement sur lequel vous travaillez vous avez souvent besoin d’interpréteur Python dans des versions différentes ou de librairies dans des versions particulières.</p>
<p>Dans ce guide, nous allons voir comment installer un environnement Python répondant aux cas d’usage suivants :</p>
<ul>
<li>gestion facile de multiple versions de l’interpréteur Python ;</li>
<li>isolation d’applications CLI basées sur Python ;</li>
<li>création d’environnements de développement isolés les uns des autres.</li>
</ul>
</div><ul></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#toc-installation-de-lenvironnement">Installation de l’environnement</a><ul>
<li><a href="#toc-pyenv">Pyenv</a></li>
<li><a href="#toc-pip">Pip</a></li>
<li><a href="#toc-pipx">Pipx</a></li>
<li><a href="#toc-poetry">Poetry</a></li>
</ul>
</li>
<li><a href="#toc-mot-de-la-fin">Mot de la fin</a></li>
</ul>
<h2 id="toc-installation-de-lenvironnement">Installation de l’environnement</h2>
<p>Nous allons utiliser les outils suivants:</p>
<ul>
<li>pyenv ;</li>
<li>pip ;</li>
<li>pipx ;</li>
<li>poetry.</li>
</ul>
<h3 id="toc-pyenv">Pyenv</h3>
<p><a href="https://github.com/pyenv/pyenv">Pyenv</a> est un outil qui permet d’installer et gérer facilement vos interpréteurs Python. Il vous permet d’utiliser un interpréteur qui n’est pas celui de votre système.</p>
<p>Les distributions Linux sont souvent livrées avec un Python pré-installé (généralement sous /bin/python) permettant de gérer des programmes nécessaires au bon fonctionnement de votre système. Certains de ces programmes sont critiques et il est donc recommandé de <strong>ne pas toucher à l’interpréteur natif de votre système Linux</strong>.</p>
<p>Installation de Pyenv</p>
<pre><code class="bash">curl https://pyenv.run <span class="p">|</span> bash</code></pre>
<p>Une fois installé, il faut modifier la configuration de votre shell afin qu’il initialise Pyenv à chaque ouverture de terminal.</p>
<p>Ajoutez ces lignes a votre bashrc (ou zshrc):</p>
<pre><code class="bash"><span class="nb">export</span> <span class="nv">PYENV_ROOT</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.pyenv"</span>
<span class="nb">command</span> -v pyenv >/dev/null <span class="o">||</span> <span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PYENV_ROOT</span><span class="s2">/bin:</span><span class="nv">$PATH</span><span class="s2">"</span>
<span class="nb">eval</span> <span class="s2">"</span><span class="k">$(</span>pyenv init -<span class="k">)</span><span class="s2">"</span></code></pre>
<p>Ouvrez un nouveau terminal et testez que pyenv :</p>
<pre><code class="bash">pyenv
pyenv <span class="m">2</span>.3.0</code></pre>
<p>Pour installer une version particulière de Python, utilisez la commande <code>pyenv install</code>. Par exemple :</p>
<pre><code class="bash">pyenv install <span class="m">3</span>.9.10</code></pre>
<p>Vous pouvez lister les versions de Python installées</p>
<pre><code class="bash">pyenv versions</code></pre>
<h3 id="toc-pip">Pip</h3>
<p>Pip est tout simplement un installeur de librairie Python disponible sur <a href="https://pypi.org/">https://pypi.org/</a>.</p>
<p>On télécharge l’installeur officiel :</p>
<pre><code class="bash">wget https://bootstrap.pypa.io/get-pip.py</code></pre>
<p>On le lance (avec le python3 du système)</p>
<pre><code class="bash">sudo python3 get-pip.py</code></pre>
<p>On vérifie que pip3 est bien installé pour notre interpréteur système</p>
<pre><code class="bash">pip3 --version
pip <span class="m">22</span>.1 from /usr/local/lib/python3.10/dist-packages/pip <span class="o">(</span>python <span class="m">3</span>.10<span class="o">)</span></code></pre>
<h3 id="toc-pipx">Pipx</h3>
<p><a href="https://pypa.github.io/pipx/">Pipx</a> permet d’installer des outils CLI dans des environnements isolés et les expose de la même façon que s’ils avaient été installés par pip ou un paquet système.</p>
<p>Très pratique pour éviter les conflits entre plusieurs outils de CLI qui reposent souvent sur des librairies open-source communes mais dans des versions différentes. Il permet aussi d’installer plusieurs versions différentes d’une application CLI.</p>
<p>Installation du paquet <code>venv</code> qui permet de créer des environnements virtuels. Cette librairie est utilisée par pipx pour isoler les installations.</p>
<pre><code class="bash">sudo apt install python3-venv</code></pre>
<p>Installation de pipx via pip</p>
<pre><code class="bash">sudo pip3 install pipx virtualenv</code></pre>
<p>Pipx expose les applications qu’il installe en ajoutant un lien dans le chemin <code>~/.local/bin</code>. Il faut donc ajouter ce chemin à votre « PATH ». Dans votre bashrc (ou zshrcè) :</p>
<pre><code class="bash"><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PATH</span><span class="s2">:</span><span class="nv">$HOME</span><span class="s2">/.local/bin/"</span></code></pre>
<p>On va à présent installer une application basée sur Python. Prenons par exemple Ansible. Ici, nous précisons à pipx d’utiliser l’interpréteur Python installé avec Pyenv précédemment :</p>
<pre><code class="bash">pipx install --python /home/nico/.pyenv/versions/3.9.10/bin/python3.9 ansible --include-deps</code></pre>
<p>Ansible est à présent installé dans un environnement virtuel. Les bibliothèques qu’il utilise sont isolées des bibliothèques Python utilisées par le système.</p>
<p>Pour lister les programmes que vous avez installés via pipx :</p>
<pre><code class="bash">pipx list</code></pre>
<p>Si le programme que vous avez installé a lui-même besoin d’autres bibliothèques Python pour fonctionner, c’est le cas, par exemple, avec Ansible dont les modules reposent parfois sur des bibliothèques communautaires, vous pouvez les injecter dans l’environnement virtuel. Dans cet exemple, on injecte les bibliothèques pyvmomi, python-ldap, kubernetes et hvac à notre environnement <code>ansible</code> :</p>
<pre><code class="bash">pipx inject ansible pyvmomi python-ldap kubernetes hvac</code></pre>
<p>Il est possible également de gérer plusieurs versions d’un même programme et de l’exposer au système avec un suffixe différent. Ainsi, je souhaite installer la dernière version d’Ansible (Ansible 5) tout en gardant la première version installée :</p>
<pre><code class="bash">pipx install --suffix -5 --python /home/nico/.pyenv/versions/3.9.10/bin/python3.9 --include-deps ansible</code></pre>
<p>La commande <code>ansible-playbook</code> pour cette version est alors exposée sous <code>ansible-playbook-5</code>.</p>
<pre><code class="bash">ansible-playbook-5 --version
ansible-playbook <span class="o">[</span>core <span class="m">2</span>.12.5<span class="o">]</span>
config <span class="nv">file</span> <span class="o">=</span> None
configured module search <span class="nv">path</span> <span class="o">=</span> <span class="o">[</span><span class="s1">'/home/nico/.ansible/plugins/modules'</span>, <span class="s1">'/usr/share/ansible/plugins/modules'</span><span class="o">]</span>
ansible python module <span class="nv">location</span> <span class="o">=</span> /home/nico/.local/pipx/venvs/ansible/lib/python3.9/site-packages/ansible
ansible collection <span class="nv">location</span> <span class="o">=</span> /home/nico/.ansible/collections:/usr/share/ansible/collections
executable <span class="nv">location</span> <span class="o">=</span> /home/nico/.local/bin//ansible-playbook
python <span class="nv">version</span> <span class="o">=</span> <span class="m">3</span>.9.10 <span class="o">(</span>main, May <span class="m">19</span> <span class="m">2022</span>, <span class="m">14</span>:16:15<span class="o">)</span> <span class="o">[</span>GCC <span class="m">11</span>.2.0<span class="o">]</span>
jinja <span class="nv">version</span> <span class="o">=</span> <span class="m">3</span>.1.2
<span class="nv">libyaml</span> <span class="o">=</span> True</code></pre>
<h3 id="toc-poetry">Poetry</h3>
<p><a href="https://python-poetry.org/">Poetry</a> est un gestionnaire de dépendances Python. C’est une sorte de pip++. Il permet d’installer des librairies Python, de s’assurer que l’environnement de développement d’un projet soit isolé et identique pour tous les développeurs, de gérer les conflits d’installation ou encore de publier son paquet Python.</p>
<p>Poetry est un outil CLI écrit en Python, pour l’installation nous pouvons donc utiliser pipx :</p>
<pre><code class="bash">pipx install poetry</code></pre>
<p>On vérifie l’installation</p>
<pre><code class="bash">poetry --version
Poetry version <span class="m">1</span>.1.13</code></pre>
<p>Création d’un projet</p>
<pre><code class="bash">poetry new poetry-demo</code></pre>
<p>Cette commande effectue 2 choses :</p>
<ul>
<li>la création d’un répertoire pour le projet avec des fichiers nécessaires à la gestion des dépendances,</li>
<li>la création d’un environnement virtuel pour le projet.</li>
</ul>
<pre><code class="bash">tree poetry-demo
poetry-demo
├── pyproject.toml
├── README.rst
├── poetry_demo
│ └── __init__.py
└── tests
├── __init__.py
└── test_poetry_demo.py</code></pre>
<p>Si l’on veut ajouter une librairie, on va alors se servir de la CLI :</p>
<pre><code class="bash">poetry add django</code></pre>
<p>Si l’on souhaite activer l’environnement virtuel du projet, on se place dedans et on appelle la commande shell.</p>
<pre><code class="bash"><span class="nb">cd</span> poetry-demo
poetry shell</code></pre>
<h2 id="toc-mot-de-la-fin">Mot de la fin</h2>
<p>Ce tutoriel a été réalisé sur une machine Ubuntu 22.04, mais devrait fonctionner de la même façon sur d’autres distributions. Il existe, bien sur, d’autres façons de gérer ces environnements Python, ceci n’est qu’un exemple qui permet de travailler plus proprement avec Python. Il fonctionne dans un contexte d’administration système avec la gestion des CLI via pipx ou dans un contexte de développement logiciel via l’usage de poetry.</p>
</div><div><a href="https://linuxfr.org/news/environnement-moderne-de-travail-python.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/127799/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/environnement-moderne-de-travail-python#comments">ouvrir dans le navigateur</a>
</p>
sispheorpalm123Pierre JarillonYsabeau 🧶Nils Ratuszniktedhttps://linuxfr.org/nodes/127799/comments.atomtag:linuxfr.org,2005:News/408072021-12-25T20:07:41+01:002021-12-25T20:07:41+01:00Python pour la fin de l’année 2021Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>En 2019 <a href="//linuxfr.org/users/oliver_h">Oliver</a> commençait une série de dépêches sur le langage Python, série qu’il nous promettait pour la rentrée 2019. Divers aléas ont fait que cette série n’a pu être terminée qu’en juin 2021.</p>
<p>Et, comme cela forme un tout cohérent et qu’il y avait, dès le départ, l’idée de les réunir, l’aboutissement <a href="https://github.com/la415/epub-python/blob/docs/Python-linuxfr.epub">est un</a>, en fait plutôt <a href="https://github.com/la415/epub-python/blob/docs/Python-linuxfr-sans-tableau.epub">deux epub</a> qui compilent les dépêches <strong>et</strong> leurs commentaires parce que la maison ne se refuse rien. Ils sont sur un dépôt <a href="https://github.com/la415/epub-python">github</a> avec les images de couvertures en version svg et png. Idéalement, ils devraient être sur un dépôt plutôt LinuxFr que celui-là.</p>
<p>Vous n’échapperez, évidemment, pas aux petits secrets d’ateliers en fin de dépêche.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f6769746875622e636f6d2f6c613431352f657075622d707974686f6e2f626c6f622f646f63732f636f75762d707974686f6e2d696c6c75646570656368652e706e673f7261773d74727565/couv-python-illudepeche.png?raw=true" alt="Les pages de couverture" title="Source : https://github.com/la415/epub-python/blob/docs/couv-python-illudepeche.png?raw=true"></p>
</div><ul><li>lien nᵒ 1 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-1" hreflang="fr" href="https://linuxfr.org/redirect/109619">Python — partie 1 ― Popularité</a></li><li>lien nᵒ 2 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-2" hreflang="fr" href="https://linuxfr.org/redirect/109620">Python — partie 2 ― Python 2</a></li><li>lien nᵒ 3 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets" hreflang="fr" href="https://linuxfr.org/redirect/109621">Python — partie 3 — Installation de Python et de paquets </a></li><li>lien nᵒ 4 : <a title="https://linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv" hreflang="fr" href="https://linuxfr.org/redirect/109622">Python — partie 4 — Py Pyenv</a></li><li>lien nᵒ 5 : <a title="https://linuxfr.org/news/python-partie-5-nix-et-guix" hreflang="fr" href="https://linuxfr.org/redirect/109623">Python — partie 5 — Nix (et Guix)</a></li><li>lien nᵒ 6 : <a title="https://linuxfr.org/news/python-partie-6-pip-et-pipx" hreflang="fr" href="https://linuxfr.org/redirect/109624">Python — partie 6 — Pip et Pipx</a></li><li>lien nᵒ 7 : <a title="https://linuxfr.org/news/python-pour-noel-202x-partie-7-environnements-virtuels" hreflang="fr" href="https://linuxfr.org/redirect/109625">Python — partie 7 — Environnements virtuels</a></li><li>lien nᵒ 8 : <a title="https://linuxfr.org/news/python-partie-8-pipenv" hreflang="fr" href="https://linuxfr.org/redirect/109626">Python — partie 8 — Pipenv </a></li><li>lien nᵒ 9 : <a title="https://linuxfr.org/news/python-partie-10-formateur-de-code-analyse-statique" hreflang="fr" href="https://linuxfr.org/redirect/109627">Python ― partie 9 ― Formateur de code, analyse statique</a></li><li>lien nᵒ 10 : <a title="https://linuxfr.org/news/python-partie-11-entretiens" hreflang="fr" href="https://linuxfr.org/redirect/109628">Python — partie 10 — Entretiens</a></li><li>lien nᵒ 11 : <a title="https://github.com/la415/epub-python/blob/docs/Python-linuxfr.epub" hreflang="fr" href="https://linuxfr.org/redirect/109629">Le fichier epub avec les tableaux d’origine</a></li><li>lien nᵒ 12 : <a title="https://github.com/la415/epub-python/blob/docs/Python-linuxfr-sans-tableau.epub" hreflang="fr" href="https://linuxfr.org/redirect/109630">La versions sans tableau plus accessible</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-un-grand-merci-pour-commencer">Un grand merci pour commencer</a></li>
<li>
<a href="#toc-le-ou-plut%C3%B4t-les-livres-%C3%A9lectroniques">Le, ou plutôt les livres électroniques</a><ul>
<li><a href="#toc-les-sujets-trait%C3%A9s">Les sujets traités</a></li>
<li><a href="#toc-une-compilation-comment%C3%A9e">Une compilation commentée</a></li>
<li><a href="#toc-des-pourquoi-deux-fichiers-epub">Des ? Pourquoi deux fichiers epub ?</a></li>
<li><a href="#toc-o%C3%B9-il-est-question-de-licence">Où il est question de licence</a></li>
</ul>
</li>
<li>
<a href="#toc-dans-la-fabrique-des-epub">Dans la fabrique des epub</a><ul>
<li><a href="#toc-les-logiciels-utilis%C3%A9s">Les logiciels utilisés</a></li>
<li><a href="#toc-travailler-dans-sigil-deux-trois-astuces">Travailler dans Sigil deux trois astuces</a></li>
<li><a href="#toc-transposer-les-tableaux">Transposer les tableaux</a></li>
</ul>
</li>
<li><a href="#toc-la-question-finale">La question finale</a></li>
</ul>
<h2 id="toc-un-grand-merci-pour-commencer">Un grand merci pour commencer</h2>
<p>Merci à toutes les personnes qui ont contribué d’une façon ou d’une autre à ces dépêches, ou qui les ont commentées. Cela forme un corpus précieux qui réunit à la fois l’aspect didactique et les expériences.</p>
<p>Un merci tout particulier à : </p>
<ul>
<li>
<a href="//linuxfr.org/users/oliver_h">Oliver</a> qui a commencé la série ;</li>
<li>
<a href="//linuxfr.org/users/nokomprendo-3">nokomprendo</a> qui est l’auteur principal du chapitre 5 sur Nix (et Guix) ;</li>
<li>et à <a href="//linuxfr.org/users/bluebird">Philippe F.</a> qui a terminé la série.</li>
</ul>
<p>Sans oublier le remotivateur, j’ai nommé : <a href="//linuxfr.org/users/tisaac">tisaac</a>.</p>
<h2 id="toc-le-ou-plutôt-les-livres-électroniques">Le, ou plutôt les livres électroniques</h2>
<h3 id="toc-les-sujets-traités">Les sujets traités</h3>
<p>Vous retrouverez les liens en bas de la page, mais une présentation succincte s’impose. Donc, on commence par la popularité de <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-1">Python</a> (idéalement j’aurais dû mettre ça à jour, mais bon), pour dire ensuite au revoir à <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-2">Python 2</a>.</p>
<p>Après ces préliminaires, si je puis dire, on aborde le logiciel sous forme de tutoriels <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets">Installation</a>, <a href="//linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv">Py Pyenv</a>, <a href="//linuxfr.org/news/python-partie-5-nix-et-guix">Nix et Giux</a>, <a href="//linuxfr.org/news/python-partie-6-pip-et-pipx">Pip et Pipx</a>, les <a href="//linuxfr.org/news/python-pour-noel-202x-partie-7-environnements-virtuels">environnements virtuels</a> et <a href="//linuxfr.org/news/python-partie-8-pipenv">Pipenv</a>. La dépêche sur le <a href="//linuxfr.org/news/python-partie-10-formateur-de-code-analyse-statique">formateur de code</a> est un retour d’expérience sous forme d’un dialogue à deux voix entre Oliver et Philippe F.</p>
<p>Et enfin, pour conclure en beauté, il y a une série d’<a href="//linuxfr.org/news/python-partie-11-entretiens">entretiens</a> d’utilisateurs du langage. Chacun avec une utilisation, et donc une expérience différente.</p>
<h3 id="toc-une-compilation-commentée">Une compilation commentée</h3>
<p>On a ainsi la compilation de la série de dépêches plus les commentaires. Les commentaires n’ont, toutefois, pas été tous été repris, pas uniquement par volonté de censure de ma part, mais était-il nécessaire de garder une série de commentaires portant sur une coquille ou complètement hors-sujet ? Ce qui, au final fait très très peu de commentaires en moins (dont des miens soit dit en passant). En revanche, tous les avatars sont partis à la trappe, c’est comme ça<sup id="fnref1"><a href="#fn1">1</a></sup>. J’ai opté pour ajouter un titre « Commentaires », balisé <code><h2></code> et d’insérer des filets (que je ne vois pas sur ma liseuse) entre chaque bloc de commentaires pour les différencier. La balise <code><h2></code> permet d’arriver directement sur les commentaires.</p>
<p>Par ailleurs, les seules images purement décoratives qui ont été gardées sont celles des entrées de chapitre sur lesquelles est basée la couverture des livres, soit dit en passant.</p>
<p>Il n’y a plus qu’une seule table des matières qui les réunit toutes. J’ai également supprimé des informations redondantes (licence par exemple) ou dépassées (PyConFR de 2019).</p>
<p>Le résultat final est très sûrement perfectible.</p>
<h3 id="toc-des-pourquoi-deux-fichiers-epub">Des ? Pourquoi deux fichiers epub ?</h3>
<p>Sur ma liseuse, les tableaux de la première dépêche sur la <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-1">popularité de Python</a> ne rendaient pas du tout et étaient illisibles. J’ai donc fait deux versions, identiques, à cela près qu’il y en a une qui a les tableaux, comme des tableaux et l’autre pour laquelle ils sont en texte, ce qui les transpose dans l’autre sens pour une meilleure lisibilité. Les classements en colonne par année et en ligne pour les langages, c’est bien quand il s’agit d’un tableau. Pour le texte, il vaut mieux avoir une ligne par année.</p>
<p>De cette façon, en plus c’est plus lisible pour les personnes qui ne peuvent pas lire avec leurs yeux. Et ça me paraît extrêmement important.</p>
<h3 id="toc-où-il-est-question-de-licence">Où il est question de licence</h3>
<p>Les dépêches et les commentaires d’Oliver sont sous <a href="https://fr.wikipedia.org/wiki/Licence_CC0">licence CC0</a>. Celle par défaut de LinuxFr.org est la licence <a href="https://creativecommons.org/licenses/by-sa/4.0/deed.fr">CC-BY-SA 4</a> (Attribution ― Partage dans les mêmes conditions). Les epub sont donc sous cette dernière licence, ce qui me paraît un bon compromis.</p>
<h2 id="toc-dans-la-fabrique-des-epub">Dans la fabrique des epub</h2>
<h3 id="toc-les-logiciels-utilisés">Les logiciels utilisés</h3>
<p>Pour arriver au résultat, j’ai utilisé l’extension pour Firefox <a href="https://addons.mozilla.org/fr/firefox/addon/saveasebook/">Save as eBook</a> dont j’avais touché deux mots <a href="//linuxfr.org/users/ysabeau/journaux/un-fichier-epub-d-une-depeche-ou-d-un-journal-avec-tous-les-commentaires">ici-même en septembre 2020</a> et sélectionné le texte (dépêches plus commentaires) pour chaque dépêche. Ça a généré un fichier epub de base mais pas du tout communicable en l’état pour plusieurs raisons outre les éléments déjà donnés :</p>
<ul>
<li>il y avait la « décoration » LinuxFr : notes, le couple pertinent-inutile, plus les « Discuter », etc. qui n’avaient plus, de mon point de vue, aucun intérêt en dehors du site ;</li>
<li>il fallait ajouter les fichiers images (qui, sinon n’apparaissent pas sur l’epub, ou nécessitent une connexion) ;</li>
<li>plus quelques fautes ici et là (je n’ai pas la prétention d’avoir tout corrigé).</li>
</ul>
<p>Sans parler des titres des chapitres qui avaient besoin d’être homogénéisés. Et enfin, le fichier généré n’est pas terriblement bien formé apparemment. Il a fallu donc utiliser <a href="https://fr.wikipedia.org/wiki/Sigil_(logiciel)">Sigil</a> (formidable éditeur epub) pour corriger tout ça. Et, évidemment, la postface, les remerciements et la transposition des tableaux en texte ont été faits à partir de LibreOffice. La couverture, quant-à-elle a été concoctée avec Inkscape.</p>
<h3 id="toc-travailler-dans-sigil-deux-trois-astuces">Travailler dans Sigil deux trois astuces</h3>
<p>Petit rappel et au cas où : un fichier epub c’est en fait une collection de fichiers texte, ici un par chapitre, images, styles, etc. Le tout dans des dossiers bien spécifiques. Donc, quand on navigue dans Sigil pour modifier un epub, on navigue par fichier texte.</p>
<p>Pour tout dire, j’ai vraiment fait la connaissance de l’éditeur d’epub avec ce travail. Les éléments ci-dessous ne sont là que pour vous donner une idée de comment moi je l’ai utilisé.</p>
<p>Sigil a des boites détachables, comme Inkscape par exemple, je trouve ça très pratique avec deux écrans. Le principal avec le fichier à travailler, le <code>Navigateur</code> et la <code>Prévisualisation</code> dans l’autre ainsi que la boite <code>Insérer un caractère spécial</code>, très utile notamment pour les espaces insécables. En effet, on ne peut pas utiliser le clavier pour ça, à moins d’écrire le code html. Cela dit, avoir des connaissances de base en html est plutôt utile, ça permet de gagner du temps.</p>
<p>Je suggère très fortement de commencer par ouvrir l’<code>Éditeur de métadonnées</code> (menu <code>Outils</code> ou touche <code>F8</code>), en indiquant la langue (par défaut, chez moi c’est l’anglais apparemment), vous pourrez utiliser le module adéquat de correction orthographique (s’il y a du code, ce n’est pas forcément très utile) et en profiter pour donner un titre au document (dc:title), ainsi que d’indiquer le nom de l’auteur (dc:creator, ici j’ai mis « Collectif », c’est mieux qu’inconnu) et, éventuellement, l’éditeur (dc:publisher, ici LinuxFr.org). On peut en ajouter d’autres<sup id="fnref2"><a href="#fn2">2</a></sup>. Ces métadonnées sont vraiment importantes.</p>
<p>On peut utiliser les fonctions de recherche et de remplacement soit par fichier, soit pour tous les fichiers.</p>
<p>Revoir la table des matières est également indispensable, ne serait-ce que pour lui donner son nom, chez moi c’était « Table of Contents », pour un livre en français, ça ne sonne pas terrible. Noter que, quand on la génère, on peut sélectionner les niveaux retenus.</p>
<p>Pour les images, il faut commencer par les ajouter dans le dossier <code>Images</code> via le <code>Navigateur</code> et ensuite on peut les insérer dans le fichier texte sur lequel on travaille. Et, c’est aussi dans le <code>Navigateur</code> qu’on réorganise l’ordre des chapitres et on met la table des matières où on veut (par défaut elle est en deuxième position, je l’ai déplacé tout à la fin). Curieusement, si Sigil accepte les images au format svg pour les illustrations, il les refuse pour la couverture.</p>
<p>Et, évidemment, le logiciel est capable de corriger un fichier epub pas très correct.</p>
<h3 id="toc-transposer-les-tableaux">Transposer les tableaux</h3>
<p>J’imagine qu’il est possible de faire un script pour ça, mais j’en serais bien incapable. Donc la procédure que j’ai utilisée, plus rapide à faire qu’à décrire :</p>
<ol>
<li>copie du tableau à partir de la dépêche ;</li>
<li>collage dans Calc puis à nouveau copie de ce tableau <strong>dans</strong> Calc ;</li>
<li>toujours dans Calc, collage <strong>transposé</strong> ;</li>
<li>ajout des colonnes supplémentaires là où il faut, pour ici ajouter le rang du langage, les balises (ici <code><p></code>et <code></p></code> pour chaque ligne mais aussi <code><b></code> et <code></b></code> pour la mise en relief), rentrer le premier élément d’une colonne (par exemple le numéro avec les fioritures pour la lecture), sélectionner les cellules de la colonne et faire un <code>Ctrl + D</code> pour que tout ça se recopie vers le bas ;</li>
<li>copier et coller <strong>sans</strong> mise en forme le tableau dans Writer pour le transformer en texte ;</li>
<li>copier le texte obtenu et le coller dans Sigil.</li>
</ol>
<h2 id="toc-la-question-finale">La question finale</h2>
<p>Il y en aura d’autres ?</p>
<p>Non !</p>
<p>Sur ce bonne lecture et bonne fin d’année.</p>
<div class="footnotes">
<hr>
<ol>
<li id="fn1">
<p>Il aurait peut-être été intéressant de les garder, malgré le travail supplémentaire que cela impliquait de les ajouter, si tout le monde avait eu un avatar personnalisé. Ce qui n’est pas le cas. <a href="#fnref1">↩</a></p>
</li>
<li id="fn2">
<p>DC : pour métadonnées du Dublin Core qui traite de la description des ressources informatiques. <a href="#fnref2">↩</a></p>
</li>
</ol>
</div>
</div><div><a href="https://linuxfr.org/news/python-pour-la-fin-de-l-annee-2021.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/126328/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/python-pour-la-fin-de-l-annee-2021#comments">ouvrir dans le navigateur</a>
</p>
YsabeauNils Ratusznikpalm123https://linuxfr.org/nodes/126328/comments.atomtag:linuxfr.org,2005:News/407922021-12-16T20:58:20+01:002021-12-16T20:58:20+01:00Décès de Fredrik Lundh (Effbot), auteur des bibliothèques PIL et TkinterLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Fredrik Lundh était un contributeur Python ayant créé les bibliothèques de traitement d’image PIL et de GUI Tkinter. Il était connu pour avoir mis en ligne le site de tutoriel effbot.org couvrant ces bibliothèques. Son décès a été annoncé le 10 décembre 2021 sur la liste de diffusion des développeurs principaux du langage Python.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://en.wikipedia.org/wiki/Tkinter" hreflang="en" href="https://linuxfr.org/redirect/109564">Tkinter (Wikipedia)</a></li><li>lien nᵒ 2 : <a title="https://en.wikipedia.org/wiki/Python_Imaging_Library" hreflang="en" href="https://linuxfr.org/redirect/109565">PIL (Wikipedia)</a></li><li>lien nᵒ 3 : <a title="https://mail.python.org/archives/list/[email protected]/thread/36Q5QBILL3QIFIA3KHNGFBNJQKXKN7SD/" hreflang="en" href="https://linuxfr.org/redirect/109566">Annonce du décès sur la liste de diffusion Python-Dev </a></li><li>lien nᵒ 4 : <a title="https://web.archive.org/web/20201109205132/http://effbot.org/" hreflang="en" href="https://linuxfr.org/redirect/109567">effbot.org avant sa maintenance</a></li><li>lien nᵒ 5 : <a title="https://wiki.python.org/moin/FredrikLundh" hreflang="en" href="https://linuxfr.org/redirect/109583">Page de présentation sur wiki.python.org</a></li></ul><div><p>Fredrik Lundh a été un contributeur influent des débuts du langage Python et a beaucoup participé à la promotion de ce langage à travers l’édition de diverses bibliothèques et par l’aide qu’il apportait sur Usenet <a href="https://wiki.python.org/moin/CompLangPython"><code>comp.lang.python</code></a> aux débutants aussi bien qu’aux utilisateurs avancés.</p>
<p>Il a cofondé dans les années 90 une des premières startups Python <em>« Secret Labs AB »</em> au sein de laquelle il a développé entre autres, l’IDE <em>PythonWorks</em>. En 2001, il a publié chez O'Reilly <a href="https://www.oreilly.com/library/view/python-standard-library/0596000960/">« Python Standard Library »</a> dans le but de fournir une documentation testée et précise de tous les modules de la Python Standard Library, avec plus de 300 exemples de scripts annotés utilisant les modules.</p>
<p><a href="https://mail.python.org/archives/list/[email protected]/message/36Q5QBILL3QIFIA3KHNGFBNJQKXKN7SD/">Guido van Rossum dans son hommage</a> destiné à toute la communauté des développeurs Python termine avec ces mots : « Il va nous manquer ».</p>
</div><div><a href="https://linuxfr.org/news/deces-de-fredrik-lundh-effbot-auteur-des-bibliotheques-pil-et-tkinter.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/126253/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/deces-de-fredrik-lundh-effbot-auteur-des-bibliotheques-pil-et-tkinter#comments">ouvrir dans le navigateur</a>
</p>
voltsYves BourguignonNaoneNÿcoYsabeau 🧶Barnabéhttps://linuxfr.org/nodes/126253/comments.atomtag:linuxfr.org,2005:News/404532021-08-04T15:30:26+02:002021-10-07T18:29:42+02:00Python 3.10 est disponibleLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Python 3.10 (<em>rc1</em>) est sorti le <strong>2 août 2021</strong>, après quinze mois de développement (dont cinq à cheval sur les <em>bêta</em> et les <em>rc</em> de Python 3.9). Il reste deux mois avec des candidats (<em>RC</em>) avant la sortie définitive, prévue le <strong>4 octobre 2021</strong>.</p>
<p>Voyons ce que cette version apporte comme nouveautés…</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f7777772e707974686f6e2e6f72672f7374617469632f696d672f707974686f6e2d6c6f676f2e706e67/python-logo.png" alt="Logo Python" title="Source : https://www.python.org/static/img/python-logo.png"></p>
</div><ul><li>lien nᵒ 1 : <a title="https://www.python.org/downloads/release/python-3100rc1/" hreflang="en" href="https://linuxfr.org/redirect/108385">Notes de version 3.10.0rc1</a></li><li>lien nᵒ 2 : <a title="https://www.python.org/downloads/release/python-3100/" hreflang="en" href="https://linuxfr.org/redirect/109252">Notes de version 3.10.0</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-pep623--pr%C3%A9paration-de-la-suppression-de-wstr-dans-pyunicodeobject">PEP 623 − Préparation de la suppression de <code>wstr</code> dans <code>PyUnicodeObject</code></a></li>
<li><a href="#toc-pep-604--%C3%89crire-les-unions-de-types-plus-facilement">PEP 604 − Écrire les unions de types plus facilement</a></li>
<li><a href="#toc-pep-612--am%C3%A9lioration-du-typage-pour-les-d%C3%A9corateurs">PEP 612 − Amélioration du typage pour les décorateurs</a></li>
<li><a href="#toc-pep-626--num%C3%A9ro-de-ligne-pr%C3%A9cis-pour-les-d%C3%A9bugueurs-et-autres-outils">PEP 626 − Numéro de ligne précis pour les débugueurs et autres outils</a></li>
<li><a href="#toc-pep-618--ajout-dune-v%C3%A9rification-optionnelle-des-longueurs-dans-zip">PEP 618 − Ajout d’une vérification optionnelle des longueurs dans <code>zip</code></a></li>
<li><a href="#toc-bug-12782--groupement-des-gestionnaires-de-contexte-avec-des-parenth%C3%A8ses">Bug 12782 − Groupement des gestionnaires de contexte avec des parenthèses</a></li>
<li><a href="#toc-pep-632--d%C3%A9pr%C3%A9ciation-du-module-distutils">PEP 632 − Dépréciation du module <code>distutils</code></a></li>
<li><a href="#toc-pep-613--alias-de-types-explicites">PEP 613 − Alias de types explicites</a></li>
<li><a href="#toc-pep-634635636--ajout-du-mot-cl%C3%A9-match-pour-faire-du-filtrage-par-motif">PEP 634/635/636 − Ajout du mot-clé <code>match</code> pour faire du filtrage par motif.</a></li>
<li><a href="#toc-pep-644--openssl111-ou-plus-r%C3%A9cent-est-maintenant-requis">PEP 644 − OpenSSL 1.1.1 ou plus récent est maintenant requis</a></li>
<li><a href="#toc-pep-624--retrait-des-api-dencodage-py_unicode">PEP 624 − Retrait des API d’encodage <code>Py_UNICODE</code></a></li>
<li><a href="#toc-pep-597--ajout-dun-encodingwarning-optionnel">PEP 597 − Ajout d’un <code>EncodingWarning</code> optionnel</a></li>
<li><a href="#toc-bug-38605--%C3%89valuation-retard%C3%A9e-des-annotations-non-inclus-dans-python-310">Bug 38605 − Évaluation retardée des annotations (non-inclus dans Python 3.10)</a></li>
<li>
<a href="#toc-pour-tester">Pour tester</a><ul>
<li><a href="#toc-utilisation-dun-conteneur">Utilisation d’un conteneur</a></li>
<li><a href="#toc-compilation-depuis-les-sources">Compilation depuis les sources</a></li>
</ul>
</li>
<li><a href="#toc-compatibilit%C3%A9">Compatibilité</a></li>
</ul>
<p>Cette version comporte pas moins de <strong>onze nouveautés</strong> et <strong>une correction</strong> par rapport à la version 3.9 sortie le 5 octobre 2020.<br>
Une correction (changement de défaut) n’a finalement pas été incluse dans cette version.</p>
<h2 id="toc-pep623--préparation-de-la-suppression-de-wstr-dans-pyunicodeobject">PEP 623 − Préparation de la suppression de <code>wstr</code> dans <code>PyUnicodeObject</code>
</h2>
<p><a href="https://www.python.org/dev/peps/pep-0623/">PEP 623</a> : il n’y a plus aucune raison de garder la compatibilité avec les chaînes à larges caractères (<em>wide string</em>) dans l’implémentation Unicode de Python.</p>
<p>En effet, <code>wstr</code> et <code>wstr_length</code> avaient été introduits pour une meilleure compatibilité des extensions en <code>C</code> pour Python 2 et 3. Vu que Python 2 est maintenant déclaré mort, il n’y a plus besoin de garder ces API qui seront marquées comme dépréciées à partir de Python 3.10 et supprimées en version 3.12.</p>
<p>Le gain en mémoire est de 8 octets par chaîne de caractères, ce qui n’est pas rien.</p>
<h2 id="toc-pep-604--Écrire-les-unions-de-types-plus-facilement">PEP 604 − Écrire les unions de types plus facilement</h2>
<p><a href="https://www.python.org/dev/peps/pep-0604/">PEP 604</a> : avant, pour écrire l’union de type en Python, il fallait écrire <code>Union[X, Y]</code>.</p>
<p>Il fallait donc importer <code>Union</code> de <code>typing</code> pour pouvoir faire une union, et la lecture n’était pas limpide.</p>
<p>Cette proposition permet d’écrire <code>X | Y</code>, ce qui apporte à la fois les avantages d’une lecture simplifiée, et d’un import de moins.</p>
<p>Cela parait simple au niveau grammaire, mais pour que ça fonctionne au niveau du code, il a fallu ajouter la méthode magique <code>__or__</code> à la super-classe/l’objet <code>type</code>.</p>
<p>Du coup on gagne aussi dans d’autres domaines, comme les écritures suivantes :</p>
<pre><code class="python"><span class="nb">isinstance</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="nb">int</span> <span class="o">|</span> <span class="nb">str</span><span class="p">)</span>
<span class="nb">isinstance</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="nb">int</span> <span class="o">|</span> <span class="bp">None</span><span class="p">)</span></code></pre>
<h2 id="toc-pep-612--amélioration-du-typage-pour-les-décorateurs">PEP 612 − Amélioration du typage pour les décorateurs</h2>
<p><a href="https://www.python.org/dev/peps/pep-0612/">PEP 612</a> : la <a href="https://www.python.org/dev/peps/pep-0484/">PEP 484</a> et la <a href="https://www.python.org/dev/peps/pep-0544/">PEP 544</a> permettent de décrire des fonctions de retour d’appel (<em>callback</em>).</p>
<p>Sur un décorateur, en définissant un <code>TypeVar</code>, on peut définir une valeur de retour pour le décorateur qui correspond au type de la fonction décorée. Il n’était toutefois pas possible de faire la même chose avec les arguments.</p>
<p>La PEP 612 comble ce manque en introduisant <code>ParamSpec</code> qui englobe la définition d’arguments. <code>ParamSpec</code> contient notamment les attributs <code>args</code> et <code>kwargs</code> qui correspondent respectivement aux types des arguments positionnels et nommés.</p>
<p>Exemple :</p>
<pre><code class="python"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Awaitable</span><span class="p">,</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">ParamSpec</span><span class="p">,</span> <span class="n">TypeVar</span>
<span class="n">P</span> <span class="o">=</span> <span class="n">ParamSpec</span><span class="p">(</span><span class="s2">"P"</span><span class="p">)</span>
<span class="n">R</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">"R"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">add_logging</span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">])</span> <span class="o">-></span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">Awaitable</span><span class="p">[</span><span class="n">R</span><span class="p">]]:</span>
<span class="n">async</span> <span class="k">def</span> <span class="nf">inner</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-></span> <span class="n">R</span><span class="p">:</span>
<span class="n">await</span> <span class="n">log_to_database</span><span class="p">()</span>
<span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span> <span class="n">inner</span>
<span class="nd">@add_logging</span>
<span class="k">def</span> <span class="nf">takes_int_str</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">7</span>
<span class="n">await</span> <span class="n">takes_int_str</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s2">"A"</span><span class="p">)</span> <span class="c1"># accepté</span>
<span class="n">await</span> <span class="n">takes_int_str</span><span class="p">(</span><span class="s2">"B"</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="c1"># rejeté par le vérifieur de type</span></code></pre>
<h2 id="toc-pep-626--numéro-de-ligne-précis-pour-les-débugueurs-et-autres-outils">PEP 626 − Numéro de ligne précis pour les débugueurs et autres outils</h2>
<p><a href="https://www.python.org/dev/peps/pep-0626/">PEP 626</a> : les attributs <code>f_lineno</code> et <code>co_lnotab</code> d’une pile (<em>frame</em>) d’exécution ne donnent pas toujours le bon numéro de ligne du code source dans certains cas.</p>
<p>La correction consiste à marquer des <em>bytecodes</em> comme virtuels et n’ayant pas de ligne dans le code source.</p>
<p>Afin de garantir la compatibilité avec les outils existants, rien n’est modifié sur <code>co_lnotab</code> mais une nouvelle méthode <code>co_lines()</code> est disponible. <code>co_lnotab</code> sera donc toujours disponible mais généré à la demande.</p>
<h2 id="toc-pep-618--ajout-dune-vérification-optionnelle-des-longueurs-dans-zip">PEP 618 − Ajout d’une vérification optionnelle des longueurs dans <code>zip</code>
</h2>
<p><a href="https://www.python.org/dev/peps/pep-0618/">PEP 618</a> : sécurisation optionnelle de l’utilisation de la fonction <code>zip</code>.</p>
<p>La fonction <code>zip</code> permet d’itérer sur plusieurs séquences ou générateurs à la fois et de retourner un tuple de valeurs des différents itérateurs.</p>
<p>Si un itérateur n’a plus d’élément à itérer, <code>zip</code> s’arrête sans erreur, même si un autre itérateur a encore des éléments à itérer. Cette PEP rajoute une option pour déclencher une erreur dans une telle situation.</p>
<p>Exemple :</p>
<pre><code class="python"><span class="o">>>></span> <span class="nb">list</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">)))</span>
<span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">)]</span>
<span class="o">>>></span> <span class="nb">list</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">)))</span>
<span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">)]</span>
<span class="o">>>></span> <span class="nb">list</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="n">strict</span><span class="o">=</span><span class="bp">True</span><span class="p">))</span>
<span class="ne">ValueError</span><span class="p">:</span> <span class="nb">zip</span><span class="p">()</span> <span class="n">argument</span> <span class="mi">2</span> <span class="ow">is</span> <span class="n">shorter</span> <span class="n">than</span> <span class="n">argument</span> <span class="mi">1</span></code></pre>
<h2 id="toc-bug-12782--groupement-des-gestionnaires-de-contexte-avec-des-parenthèses">Bug 12782 − Groupement des gestionnaires de contexte avec des parenthèses</h2>
<p><a href="https://bugs.python.org/issue12782">BPO 12782</a> : grouper des gestionnaires de contexte avec des parenthèses est désormais officiellement autorisé.</p>
<p>Contrairement aux autres mots-clés, il n’était pas possible d’utiliser les parenthèses avec <code>with</code>:</p>
<pre><code class="python"><span class="k">with</span> <span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s2">"a_really_long_foo"</span><span class="p">)</span> <span class="k">as</span> <span class="n">foo</span><span class="p">,</span>
<span class="nb">open</span><span class="p">(</span><span class="s2">"a_really_long_bar"</span><span class="p">)</span> <span class="k">as</span> <span class="n">bar</span><span class="p">):</span>
<span class="k">pass</span></code></pre>
<pre><code>Traceback (most recent call last):
File "<input>", line 1, in <module>
File "demo.py", line 19
with (open("a_really_long_foo") as foo,
^
SyntaxError: invalid syntax
</code></pre>
<p>Il fallait s’en remettre aux <code>\</code> pour sauter des lignes:</p>
<pre><code class="python"><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"a_really_long_foo"</span><span class="p">)</span> <span class="k">as</span> <span class="n">foo</span><span class="p">,</span> \
<span class="nb">open</span><span class="p">(</span><span class="s2">"a_really_long_bar"</span><span class="p">)</span> <span class="k">as</span> <span class="n">bar</span><span class="p">:</span>
<span class="k">pass</span></code></pre>
<p>Désormais, la première syntaxe sera valide, comme on peut légitimement s’y attendre, renforçant la cohérence du langage. Cette fonctionnalité a pu être introduite facilement grâce au <a href="https://www.python.org/dev/peps/pep-0617/">nouveau PEG parser ajouté dans Python 3.9</a>.</p>
<h2 id="toc-pep-632--dépréciation-du-module-distutils">PEP 632 − Dépréciation du module <code>distutils</code>
</h2>
<p><a href="https://www.python.org/dev/peps/pep-0632/">PEP 632</a> : le module <code>distutils</code> est déprécié car une version est maintenant <a href="https://github.com/pypa/setuptools/issues/417">intégrée dans <code>setuptools</code></a> qui ne dépend plus de la librairie standard de Python.</p>
<p><code>distutils</code> n’était pas très documenté ni maintenu dans la librairie standard de toutes façons.</p>
<p>Ça laisse également le champ libre pour la <a href="https://www.python.org/dev/peps/pep-0517/">PEP 517</a> qui veut construire un système de construction indépendant.</p>
<h2 id="toc-pep-613--alias-de-types-explicites">PEP 613 − Alias de types explicites</h2>
<p><a href="https://www.python.org/dev/peps/pep-0613/">PEP 613</a> : ajout d’un type « Alias de type » pour permettre de définir les alias de type de manière explicite.</p>
<p>Jusqu’à présent la différence entre une affectation d’une valeur à une variable et d’une définition d’alias de type était assez ténue et ambiguë.</p>
<p>Exemple d’ambiguïté :</p>
<pre><code class="python"><span class="n">MonType</span> <span class="o">=</span> <span class="s2">"ClassName"</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">-></span> <span class="n">MonType</span><span class="p">:</span>
<span class="k">pass</span></code></pre>
<p>Ici, il est difficile de savoir si <code>MonType</code> est une variable dont la valeur est une string <code>ClassName</code> ou si c’est un alias de type sur la classe <code>ClassName</code> qui sera définie plus tard. Les vérifieurs de types se trompant souvent et rapportant de fausses erreurs, il a fallu se rendre à l’évidence : cette notation n’est pas assez explicite.</p>
<p>Cette PEP, propose :</p>
<pre><code class="python"><span class="n">MonType</span><span class="p">:</span> <span class="n">TypeAlias</span> <span class="o">=</span> <span class="s2">"ClassName"</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">-></span> <span class="n">MonType</span><span class="p">:</span>
<span class="k">pass</span></code></pre>
<p>Là, plus d’ambiguïté.</p>
<h2 id="toc-pep-634635636--ajout-du-mot-clé-match-pour-faire-du-filtrage-par-motif">PEP 634/635/636 − Ajout du mot-clé <code>match</code> pour faire du filtrage par motif.</h2>
<p><a href="https://www.python.org/dev/peps/pep-0634/">PEP 634</a>, <a href="https://www.python.org/dev/peps/pep-0635/">PEP 635</a> et <a href="https://www.python.org/dev/peps/pep-0636/">PEP 636</a>: <a href="https://fr.wikipedia.org/wiki/Filtrage_par_motif">filtrage par motif</a> grâce au mot-clé <code>match</code>.</p>
<p>Cette PEP a été divisée en trois parties : <a href="https://www.python.org/dev/peps/pep-0634/">définition</a>, <a href="https://www.python.org/dev/peps/pep-0635/">justification</a>, <a href="https://www.python.org/dev/peps/pep-0636/">tutoriel</a>. La lecture du tutoriel est un bon point de départ pour lire ces PEP.</p>
<p>L’amélioration vient du constat suivant : il est souvent nécessaire de faire une suite de <code>if … elif … elif … else</code> qui concerne un même élément. Ces tests peuvent porter sur du <em>duck-typing</em> (est-ce que cet objet couac comme un canard), sur de l’héritage, sur une liste de valeurs (comparaison sur des <em>strings</em> ou des <em>numériques</em> par exemple), sur une taille de séquence (<em>liste</em>, <em>tuple</em>) et d’autres tests.</p>
<p>Cela fonctionne très bien avec des <code>if</code> mais c’est pas toujours super lisible parfois, et comme ce schéma se répète souvent dans les projets Python, il a été décidé d’aider les développeurs (ceux qui écrivent mais surtout ceux qui lisent) avec l’aide des mots-clés <code>match</code> et <code>case</code> (et copier ce qu’on peut trouver sur d’autres langages)</p>
<p>Les mots-clés <code>match</code> et <code>case</code> ne sont pas des termes réservés, on peut donc toujours les utiliser dans des noms de variables, le parseur faisant la différence de construction. Le code pré-python-3.10 reste donc compatible. Python parle de <em>soft keywords</em>.</p>
<p>Le concept de <strong>filtrage par motifs</strong> s’explique par le fait que la variable (ou l’expression) à tester est comparée à des motifs et permet de sélectionner exclusivement un bloc de code.</p>
<p>Exemple de code gérant un jeu de rôle par entrée textuelle :</p>
<pre><code class="python"><span class="n">command</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Que voulez-vous faire ? "</span><span class="p">)</span>
<span class="n">match</span> <span class="n">command</span><span class="o">.</span><span class="n">split</span><span class="p">():</span>
<span class="n">case</span> <span class="p">[</span><span class="s2">"quitter"</span><span class="p">]:</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Au revoir !"</span><span class="p">)</span>
<span class="n">quit_game</span><span class="p">()</span>
<span class="n">case</span> <span class="p">[</span><span class="s2">"regarder"</span><span class="p">]</span> <span class="o">|</span> <span class="p">[</span><span class="s2">"scruter"</span><span class="p">]:</span>
<span class="n">current_room</span><span class="o">.</span><span class="n">describe</span><span class="p">()</span>
<span class="n">case</span> <span class="p">[</span><span class="s2">"aller"</span><span class="p">,</span> <span class="s2">"côté"</span><span class="p">,</span> <span class="p">(</span><span class="s2">"Nord"</span> <span class="o">|</span> <span class="s2">"Sud"</span> <span class="o">|</span> <span class="s2">"Est"</span> <span class="o">|</span> <span class="s2">"Ouest"</span><span class="p">)</span> <span class="k">as</span> <span class="n">direction</span><span class="p">]</span> <span class="o">|</span> <span class="p">[(</span><span class="s2">"Nord"</span> <span class="o">|</span> <span class="s2">"Sud"</span> <span class="o">|</span> <span class="s2">"Est"</span> <span class="o">|</span> <span class="s2">"Ouest"</span><span class="p">)</span> <span class="k">as</span> <span class="n">direction</span><span class="p">]</span> <span class="k">if</span> <span class="n">direction</span> <span class="ow">in</span> <span class="n">current_room</span><span class="o">.</span><span class="n">exits</span><span class="p">:</span>
<span class="n">current_room</span> <span class="o">=</span> <span class="n">current_room</span><span class="o">.</span><span class="n">neighbor</span><span class="p">(</span><span class="n">direction</span><span class="p">)</span>
<span class="n">case</span> <span class="p">[(</span><span class="s2">"Nord"</span> <span class="o">|</span> <span class="s2">"Sud"</span> <span class="o">|</span> <span class="s2">"Est"</span> <span class="o">|</span> <span class="s2">"Ouest"</span><span class="p">)]</span> <span class="o">|</span> <span class="p">[</span><span class="n">_</span><span class="p">]:</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Désolé, il n’y a pas de sortie dans cette direction"</span><span class="p">)</span>
<span class="n">case</span> <span class="p">[</span><span class="s2">"prendre"</span><span class="p">,</span> <span class="n">obj</span><span class="p">]</span> <span class="o">|</span> <span class="p">[</span><span class="s2">"ramasser"</span><span class="p">,</span> <span class="n">obj</span><span class="p">]</span> <span class="o">|</span> <span class="p">[</span><span class="s2">"mettre"</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="s2">"dans"</span><span class="p">,</span> <span class="s2">"le"</span><span class="p">,</span> <span class="s2">"sac"</span><span class="p">]:</span>
<span class="n">character</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">current_room</span><span class="p">)</span>
<span class="n">case</span> <span class="p">[</span><span class="s2">"lâcher"</span><span class="p">,</span> <span class="o">*</span><span class="n">objects</span><span class="p">]:</span>
<span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">objects</span><span class="p">:</span>
<span class="n">character</span><span class="o">.</span><span class="n">drop</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">current_room</span><span class="p">)</span>
<span class="n">case</span> <span class="n">_</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">f</span><span class="s2">"Désolé, je ne comprends pas ce que veut dire {command!r}"</span><span class="p">)</span></code></pre>
<p>On peut comprendre le code en un clin d’œil, ce qui ne serait pas forcément le cas avec des <code>if</code>. De plus on peut voir que la syntaxe de <strong>déconstruction</strong> permet de valider le format attendu, mais également de récupérer des <strong>variables liées</strong> (<em>bind</em> en anglais). Si des alternatives sont présentes dans le motif, alors chaque alternative doit lier les mêmes variables. L’utilisation de l’expression <code>if condition</code> après le motif permet de <strong>garder</strong> le bloc sous une condition spéciale, supplémentaire au motif, et évaluée après la correspondance au motif et après les <em>bind</em>.</p>
<p>Enfin l’utilisation de la variable <code>_</code> permet de définir des cas par défaut. En effet il est de convention en Python d’utiliser <code>_</code> pour définir une variable que l’on ne va pas utiliser et qui sert de variable fictive (<em>placeholder</em>).</p>
<p>On peut également utiliser la notation avec une ou deux étoiles pour récupérer une liste d’éléments ou un dictionnaire. Notations <code>*lst</code> ou <code>**d</code> comme on peut le faire dans la syntaxe de déconstruction classique.</p>
<p>Les comparaisons de motifs peuvent se faire en profondeur. L’égalité (<code>==</code>) est utilisée pour comparer les objets et littéraux, sauf pour <code>True</code>, <code>False</code> et <code>None</code> ou c’est l’identité (<code>is</code>).</p>
<p>Il est également possible de comparer avec des classes en utilisant cette syntaxe :</p>
<pre><code class="python"><span class="n">match</span> <span class="n">event</span><span class="o">.</span><span class="n">get</span><span class="p">():</span>
<span class="n">case</span> <span class="n">Click</span><span class="p">(</span><span class="n">position</span><span class="o">=</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)):</span>
<span class="n">handle_click_at</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="n">case</span> <span class="n">KeyPress</span><span class="p">(</span><span class="n">key_name</span><span class="o">=</span><span class="s2">"Q"</span><span class="p">)</span> <span class="o">|</span> <span class="n">Quit</span><span class="p">():</span>
<span class="n">game</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
<span class="n">case</span> <span class="n">KeyPress</span><span class="p">(</span><span class="n">key_name</span><span class="o">=</span><span class="s2">"up arrow"</span><span class="p">):</span>
<span class="n">game</span><span class="o">.</span><span class="n">go_north</span><span class="p">()</span>
<span class="o">...</span>
<span class="n">case</span> <span class="n">KeyPress</span><span class="p">():</span>
<span class="k">pass</span> <span class="c1"># Ignore other keystrokes</span>
<span class="n">case</span> <span class="n">other_event</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="n">f</span><span class="s2">"Unrecognized event: {other_event}"</span><span class="p">)</span></code></pre>
<p>Ici on va vérifier le type de retour de <code>event.get()</code> avec les types d’objets indiqués dans les différents <code>case</code>. Attention, même si la syntaxe donne l’impression que des objets sont créés (<code>Click(position=(x, y))</code>, <code>Quit()</code>) il n’en est rien.</p>
<p>Ce qui est donné entre parenthèses restreint le motif à une classe qui hérite de la classe indiquée (exemple <code>Click</code>) et qui contient un attribut <code>position</code> dont le motif correspond à un tuple de deux éléments qui seront liés aux variables <code>x</code> et <code>y</code>.</p>
<p>On peut bien évidemment mixer tous ces types de motifs. L’expressivité créée par cette PEP est énorme et des tests menés par Python montre une meilleure lisibilité sur une grosse partie du code source des modules Python et une réduction du nombre de lignes.</p>
<p>Bref, après des années de demande pour avoir un <code>switch/case</code> en Python, en voilà un sous stéroïdes !</p>
<h2 id="toc-pep-644--openssl111-ou-plus-récent-est-maintenant-requis">PEP 644 − OpenSSL 1.1.1 ou plus récent est maintenant requis</h2>
<p><a href="https://www.python.org/dev/peps/pep-0644/">PEP 644</a> : <code>OpenSSL</code> (version <code>1.1.1</code> ou supérieure) sera nécessaire pour Python 3.10.</p>
<p>Il existe trop de différences entre les versions d’<code>openssl</code> (<code>1.0.2</code>, <code>1.1.0</code> et <code>1.1.1</code>) pour que les <em>deux experts</em> qui gèrent ça chez Python puissent fournir un code compatible avec toutes. C’est la même raison pour <code>libressl</code> qui ne sera plus pris en charge à partir de Python 3.10.</p>
<p>Pour résumer, seule l’API d’<code>openssl 1.1.1</code> est prise en charge.</p>
<p>Ça ne devrait pas trop impacter les distributions selon la PEP 644 car <code>openssl 1.1.1</code> est la version par défaut sur la plupart des plateformes et distributions (Linux et BSD). Les rares distributions qui utilisent <code>LibreSSL</code> devraient probablement avoir migré sur <code>OpenSSL</code> d’ici à ce que Python 3.10 sorte.</p>
<h2 id="toc-pep-624--retrait-des-api-dencodage-py_unicode">PEP 624 − Retrait des API d’encodage <code>Py_UNICODE</code>
</h2>
<p><a href="https://www.python.org/dev/peps/pep-0624/">PEP 624</a> : planification du retrait des API inutilisée <code>PyUnicode_*</code> pour <em>Python 3.11</em>.</p>
<p>Cette PEP sort donc pour Python 3.10 mais n’aura d’effet que pour la version suivante.</p>
<h2 id="toc-pep-597--ajout-dun-encodingwarning-optionnel">PEP 597 − Ajout d’un <code>EncodingWarning</code> optionnel</h2>
<p><a href="https://www.python.org/dev/peps/pep-0597/">PEP 597</a> : ajout d’un <code>EncodingWarning</code> optionnel lors de l’ouverture d’un fichier en mode texte.</p>
<p>En effet sous Linux, BSD et MacOS, l’encodage par défaut des fichiers est très généralement <code>UTF-8</code>. Mais ce n’est pas toujours le cas. Quand on ouvre un fichier en mode texte, c’est l’encodage par défaut du système qui est utilisé (qui peut être surchargé avec la variable d’environnement <code>LANG</code> par exemple).</p>
<p>On peut préciser/forcer l’encodage à utiliser pour les caractères/octets en le spécifiant dans un paramètre <code>encoding</code>. Celui-ci existe depuis fort longtemps, mais force est de constater que la plupart des bibliothèques et codes en python ne le précisent pas.</p>
<p>Cette PEP vise à ajouter un avertissement <code>EncodingWarning</code> quand le paramètre <code>encoding</code> n’a pas été précisé pour un fichier ouvert en mode texte.</p>
<p>Afin d’activer cet avertissement, il faut invoquer Python avec les paramètres <code>-X warn_default_encoding</code> ou bien définir la variable d’environnement <code>PYTHONWARNDEFAULTENCODING</code> à <code>1</code>.</p>
<p>Illustration de code correct (ne générant pas le warning) :</p>
<pre><code class="python"><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="sb">`my_file.txt`</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">content</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span></code></pre>
<p>--</p>
<h2 id="toc-bug-38605--Évaluation-retardée-des-annotations-non-inclus-dans-python-310">Bug 38605 − Évaluation retardée des annotations (non-inclus dans Python 3.10)</h2>
<p><a href="https://bugs.python.org/issue38605">BPO 38605</a> : l’évaluation retardée des annotations, qui est disponible depuis Python 3.7 en utilisant une ligne <code>from __future__ import annotations</code> est maintenant disponible par défaut.</p>
<p>Pour rappel, la <a href="https://www.python.org/dev/peps/pep-0563/">PEP 563</a> permet d’évaluer les annotations au lancement et pas simplement lors de la définition/compilation.</p>
<p>Les annotations sont présentes dans <code>__annotations__</code>. On peut les lire avec <code>get_type_hints</code> de <code>typing</code>. Les annotations sont toujours présentées comme du code python valide.</p>
<p>On peut par exemple les lire à l’exécution comme suit :</p>
<pre><code class="python"><span class="o">>>></span> <span class="kn">import</span> <span class="nn">typing</span>
<span class="o">>>></span> <span class="k">def</span> <span class="nf">x</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="s1">'int'</span><span class="p">):</span> <span class="k">pass</span>
<span class="o">>>></span> <span class="n">typing</span><span class="o">.</span><span class="n">get_type_hints</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="p">{</span><span class="s1">'a'</span><span class="p">:</span> <span class="n">ForwardRef</span><span class="p">(</span><span class="s1">'int'</span><span class="p">)}</span></code></pre>
<p>** /!\ Cette modification a été repoussée à Python 3.11 en raison de certaines incompatibilités **<br>
<a href="https://mail.python.org/archives/list/[email protected]/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/">Plus d’info</a></p>
<h2 id="toc-pour-tester">Pour tester</h2>
<p>Pas envie d’attendre que ce soit disponible dans votre distribution ? Deux options : utiliser un conteneur ou compiler depuis les sources.</p>
<h3 id="toc-utilisation-dun-conteneur">Utilisation d’un conteneur</h3>
<pre><code class="sh">$ docker run --rm -it python:3.10.0rc1-slim</code></pre>
<h3 id="toc-compilation-depuis-les-sources">Compilation depuis les sources</h3>
<p>En fait c’est pas si compliqué du tout et ça se compile assez vite, donc c’est une option viable, même pour tester.</p>
<p>Dépendances nécessaires : <code>expat</code>, <code>bzip2</code>, <code>gdbm</code>, <code>openssl</code>, <code>libffi</code>, <code>zlib</code>, <code>tk</code>, <code>sqlite</code>, <code>bluez-libs</code>, <code>mpdecimal</code><br>
Sous Debian/Ubuntu c’est : <code>expat libbz2-dev liblzma-dev libgdbm-dev libdb5.3-dev libdb5.3++-dev libssl-dev libffi-dev zlib1g-dev tk-dev libsqlite3-dev libbluetooth-dev libncurses-dev libreadline-dev</code></p>
<p>Ensuite il suffit de récupérer le tarball et de lancer la compilation et l’installation (c’est tiré du paquet AUR sur ArchLinux) :</p>
<pre><code class="sh">$ wget <span class="s1">'https://www.python.org/ftp/python/3.10.0/Python-3.10.0rc1.tar.xz'</span>
$ tar xf Python-3.10.0rc1.tar.xz
$ <span class="nb">cd</span> Python-3.10.0rc1
$ rm -rf Modules/<span class="o">{</span>expat,zlib<span class="o">}</span> Modules/_ctypes/<span class="o">{</span>darwin,libffi<span class="o">}</span>*
$ <span class="c1"># Vous pouvez vous passer de enable-optimizations pour compiler plus vite si vous voulez</span>
$ ./configure --prefix<span class="o">=</span>/usr <span class="se">\</span>
--enable-shared <span class="se">\</span>
--with-computed-gotos <span class="se">\</span>
--enable-ipv6 <span class="se">\</span>
--with-system-expat <span class="se">\</span>
--enable-loadable-sqlite-extensions <span class="se">\</span>
--without-ensurepip <span class="se">\</span>
--enable-optimizations
$ make
$ mkdir -p pkg
$ sudo make <span class="nv">DESTDIR</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/pkg"</span> altinstall maninstall
$ sudo rm -f <span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/pkg/usr/lib/libpython3.so"</span> <span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/pkg/usr/share/man/man1/python3.1"</span>
$ sudo ln -sf ../../libpython3.10m.so <span class="se">\</span>
<span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/pkg/usr/lib/python3.10/config-3.10-</span><span class="k">$(</span>uname -m<span class="k">)</span><span class="s2">-linux-gnu/libpython3.10m.so"</span>
$ sudo ln -sf python3.10m-config <span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/pkg/usr/bin/python3.10-config"</span>
$ sudo install -dm755 <span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/pkg/usr/lib/python3.10/Tools"</span>/<span class="o">{</span>i18n,scripts<span class="o">}</span>
$ sudo install -m755 Tools/i18n/<span class="o">{</span>msgfmt,pygettext<span class="o">}</span>.py <span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/pkg/usr/lib/python3.10/Tools/i18n/"</span>
$ sudo install -m755 Tools/scripts/<span class="o">{</span>README,*py<span class="o">}</span> <span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/pkg/usr/lib/python3.10/Tools/scripts/"</span>
$ sudo cp -a pkg/* /
$ python3.10</code></pre>
<h2 id="toc-compatibilité">Compatibilité</h2>
<p>Python 3.10 est grandement compatible avec les versions précédentes. Ça devrait donc être très facile de passer à cette version pour votre projet.<br>
Il y a toutefois quelques limitations temporaires :</p>
<ul>
<li>Certains paquets Python compilés ne fournissent par encore de <code>wheel</code> (paquets binaires) pour Python 3.10 et donc l’installation d’un tel paquet va demander une compilation.<br>
Le paquet <code>numpy</code>, ainsi que <code>pandas</code> qui en dépend, fait partie de cette catégorie. La compilation peut s’avérer compliquée si vous êtes sur une distribution qui fait du morcelage de paquets (<code>-dev</code>, <code>-doc</code>, …). Il vous faudra alors installer une myriade de paquets systèmes (en plus du compilateur) pour pouvoir installer votre paquet python compilé.<br>
Donc changer uniquement votre environnement en Python 3.10 n’est pas forcément suffisant. Ceci est bien sûr temporaire et les auteurs de ces bibliothèques fourniront pour sûr des <code>wheels</code> prochainement.</li>
<li>Certains paquets Python n’ont pas pensé que la version mineure de Python pouvait tenir sur deux chiffres au lieu d’un.<br>
Je pense notamment à <code>uwsgi</code>. Mais c’est corrigé sur la branche principale et une version va voir le jour.</li>
</ul>
<p>Ce ne sont que de petites contraintes qui vont se corriger d’ici que la finale sorte début octobre, mais en attendant, si vous voulez tester/anticiper, vous savez à quoi vous attendre.</p>
</div><div><a href="https://linuxfr.org/news/python-3-10-est-disponible.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/124131/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/python-3-10-est-disponible#comments">ouvrir dans le navigateur</a>
</p>
Cyrille PontvieuxSnarkAnonymeYsabeau 🧶ariasuniYves BourguignonBenoît Sibaudbobble bubbledourouc05pamputtpalm123https://linuxfr.org/nodes/124131/comments.atomtag:linuxfr.org,2005:News/394212021-06-09T09:49:40+02:002021-12-21T18:09:09+01:00Python — partie 9 ― formateur de code, analyse statiqueLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Cette dépêche est la suite d’une série sur Python initiée en septembre 2019. Après un sommeil cryogénique d’un an et demi, on repart en forme avec d’autres contenus Python à vous proposer : actualité, bonnes pratiques, astuces, témoignages… Elle a été rédigée principalement à deux voix, <a href="//linuxfr.org/users/oliver_h">Oliver</a> et <a href="//linuxfr.org/users/bluebird">Philippe</a>, qui vous font part de leur expérience sur les fonctions.</p>
<p>Cette dixième partie présente les formateurs de code bien pratiques et les analyseurs de code. 🐍 🐍 🐍</p>
<p><a href="https://github.com/olibre/GreatPractices/tree/master/python"><img src="//img.linuxfr.org/img/687474703a2f2f6f6c696272652e6769746875622e696f2f4772656174546970732f707974686f6e2f6e6577732f7061727469652d31302e706e67/partie-10.png" alt='Le logo de Python est entouré de petites icônes symbolisant la variété des domaines où s’applique Python, et, à droite, un joyeux barbu se tient derrière un écran d’ordinateur qui affiche « partie = 10, "Formateurs" \n print(partie) »' title="Source : http://olibre.github.io/GreatTips/python/news/partie-10.png"></a></p>
<p>Pour rappel, les autres dépêches déjà publiées :</p>
<ul>
<li>Python — <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-1">Popularité</a>
</li>
<li>Python — <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-2">Python 2</a>
</li>
<li>Python — <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets">Installation de Python et de paquets</a>
</li>
<li>Python — <a href="//linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv">Py Pyenv</a>
</li>
<li>Python — <a href="//linuxfr.org/news/python-partie-5-nix-et-guix">Nix (et Guix)</a>
</li>
<li>Python — <a href="//linuxfr.org/news/python-partie-6-pip-et-pipx">Pip et Pipx</a>
</li>
<li>Python — <a href="//linuxfr.org/news/python-pour-noel-202x-partie-7-environnements-virtuels">environnements virtuels</a>
</li>
<li>Python — <a href="//linuxfr.org/news/python-partie-8-pipenv">Pipenv</a>
</li>
<li>Python — <a href="//linuxfr.org/news/python-partie-11-entretiens">Entretiens</a>
</li>
</ul>
</div><ul><li>lien nᵒ 1 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-1" hreflang="fr" href="https://linuxfr.org/redirect/109677">Python — partie 1 ― Popularité</a></li><li>lien nᵒ 2 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-2" hreflang="fr" href="https://linuxfr.org/redirect/109678">Python — partie 2 ― Python 2 </a></li><li>lien nᵒ 3 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets" hreflang="fr" href="https://linuxfr.org/redirect/109679">Python — partie 3 — Installation de Python et de paquets</a></li><li>lien nᵒ 4 : <a title="https://linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv" hreflang="fr" href="https://linuxfr.org/redirect/109680">Python — partie 4 — Py Pyenv</a></li><li>lien nᵒ 5 : <a title="https://linuxfr.org/news/python-partie-6-pip-et-pipx" hreflang="fr" href="https://linuxfr.org/redirect/109681">Python — partie 6 — Pip et Pipx </a></li><li>lien nᵒ 6 : <a title="https://linuxfr.org/news/python-pour-noel-202x-partie-7-environnements-virtuels" hreflang="fr" href="https://linuxfr.org/redirect/109682">Python — partie 7 — Environnements virtuels </a></li><li>lien nᵒ 7 : <a title="https://linuxfr.org/news/python-partie-8-pipenv" hreflang="fr" href="https://linuxfr.org/redirect/109683">Python— partie 8 — Pipenv</a></li><li>lien nᵒ 8 : <a title="https://linuxfr.org/news/python-partie-11-entretiens" hreflang="fr" href="https://linuxfr.org/redirect/109684">Python — partie 10 — Entretiens</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#toc-formateurs-de-code-source">Formateurs de code source</a><ul>
<li><a href="#toc-black"><code>black</code></a></li>
<li><a href="#toc-blue"><code>blue</code></a></li>
<li><a href="#toc-yapf"><code>yapf</code></a></li>
<li><a href="#toc-autopep8"><code>autopep8</code></a></li>
<li><a href="#toc-isort"><code>isort</code></a></li>
<li>
<a href="#toc-bilan">Bilan</a><ul>
<li><a href="#toc-retour-doliver"><em>Retour d’Oliver</em></a></li>
<li><a href="#toc-retour-de-philippe"><em>Retour de Philippe</em></a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#toc-formateurs-de-docstring">Formateurs de docstring</a><ul>
<li><a href="#toc-docformatter"><code>docformatter</code></a></li>
<li><a href="#toc-pyment"><code>pyment</code></a></li>
<li><a href="#toc-g%C3%A9n%C3%A9ration-de-la-documentation">Génération de la documentation</a></li>
</ul>
</li>
<li>
<a href="#toc-analyse-statique-de-code">Analyse statique de code</a><ul>
<li>
<a href="#toc-pylint">pylint</a><ul>
<li><a href="#toc-retour-de-philippe-1">Retour de Philippe</a></li>
<li><a href="#toc-mon-bilan">Mon bilan</a></li>
</ul>
</li>
<li>
<a href="#toc-pyflakes">pyflakes</a><ul>
<li><a href="#toc-retour-de-philippe-2">Retour de Philippe</a></li>
</ul>
</li>
<li>
<a href="#toc-flake8">flake8</a><ul>
<li><a href="#toc-configuration">Configuration</a></li>
<li><a href="#toc-retour-de-philippe-3">Retour de Philippe</a></li>
</ul>
</li>
<li>
<a href="#toc-bandit">bandit</a><ul>
<li><a href="#toc-retour-de-philippe-4">Retour de Philippe</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#toc-annotation-de-type">Annotation de type</a><ul>
<li>
<a href="#toc-mypy"><code>mypy</code></a><ul>
<li><ul>
<li><a href="#toc-retour-de-philippe-5">Retour de Philippe</a></li>
</ul></li>
</ul>
</li>
<li>
<a href="#toc-pyre"><code>pyre</code></a><ul>
<li><ul>
<li><a href="#toc-retour-de-philippe-6">Retour de Philippe</a></li>
</ul></li>
</ul>
</li>
<li>
<a href="#toc-monkeytype"><code>monkeytype</code></a><ul>
<li><ul>
<li><a href="#toc-retour-de-philippe-7">Retour de Philippe</a></li>
</ul></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#toc-tests-unitaires">Tests unitaires</a><ul>
<li><a href="#toc-unittest"><code>unittest</code></a></li>
<li><a href="#toc-pytest"><code>pytest</code></a></li>
<li><a href="#toc-nose"><code>nose</code></a></li>
<li><a href="#toc-ward"><code>ward</code></a></li>
<li><a href="#toc-unittestmock"><code>unittest.mock</code></a></li>
<li><a href="#toc-pytest-mock"><code>pytest-mock</code></a></li>
<li><a href="#toc-hypothesis"><code>hypothesis</code></a></li>
<li><a href="#toc-coverage"><code>coverage</code></a></li>
<li><a href="#toc-pytest_cov"><code>pytest_cov</code></a></li>
<li><a href="#toc-voir-aussi">Voir aussi</a></li>
</ul>
</li>
<li><a href="#toc-et-tes-astuces">Et tes astuces ?</a></li>
</ul>
<h2 id="toc-formateurs-de-code-source">Formateurs de code source</h2>
<p>Les formateurs de code source ne dépendent pas des modules utilisés par les projets. Donc nous pouvons les installer avec <code>pip</code> :</p>
<pre><code class="shell">python3 -m pip install --progress-bar emoji --user --upgrade black
python3 -m pip install --progress-bar emoji --user --upgrade yapf
python3 -m pip install --progress-bar emoji --user --upgrade autopep8
python3 -m pip install --progress-bar emoji --user --upgrade docformatter</code></pre>
<p>Par la suite, ils seront illustrés avec l’exemple suivant :</p>
<pre><code class="python"><span class="err">$</span> <span class="n">cat</span> <span class="o">></span> <span class="n">bateau</span><span class="o">.</span><span class="n">py</span>
<span class="n">capitaine</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'age'</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span> <span class="c1"># univers ?</span>
<span class="s1">'nom'</span><span class="p">:</span> <span class="s1">'Grant'</span><span class="p">,</span>
<span class="s1">'pays'</span><span class="p">:</span> <span class="s1">'Royaume-Uni'</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">navire</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'nom'</span><span class="p">:</span> <span class="s1">'Britannia'</span><span class="p">,</span>
<span class="s1">'longueur'</span><span class="p">:</span> <span class="mi">127</span><span class="p">,</span><span class="c1"># metres</span>
<span class="s1">'tonnage'</span><span class="p">:</span> <span class="mi">5860</span><span class="p">,</span>
<span class="s1">'lancement'</span><span class="p">:</span> <span class="s2">"16 mars 1953"</span>
<span class="p">}</span>
<span class="n">mission</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"commandant"</span> <span class="p">:</span> <span class="n">capitaine</span> <span class="p">,</span> <span class="s1">'bateau'</span> <span class="p">:</span> <span class="n">navire</span> <span class="p">,</span> <span class="p">}</span>
<span class="n">f</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="bp">True</span> <span class="k">if</span> <span class="n">x</span><span class="o">%</span><span class="mi">9</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="bp">False</span></code></pre>
<h3 id="toc-black"><code>black</code></h3>
<p>Le projet <a href="https://github.com/psf/black"><code>black</code></a> est très récent, son premier commit date de mars 2018. Et pourtant ce formateur de code Python bénéficie d’un succès énorme avec plus de 20 000 étoiles sur GitHub (et une centaine de contributeurs).</p>
<p>Son succès est lié à la quasi-absence de configuration et fonctionne dans le même esprit que <code>gofmt</code>, c’est-à-dire que les développeurs n’ont plus à discuter des règles de codage. C’est toujours <code>black</code> qui a raison et on ne perd plus de temps à négocier les règles, à les rediscuter en revue de code… On se concentre sur son travail : coder sans se prendre la tête à bien indenter. De toutes façons, <code>black</code> va changer l’indentation avec ses propres règles de codage non-négociables : <em>uncompromising Python code formater</em>.</p>
<p>Les deux seuls paramètres sur lesquels on peut encore chipoter :</p>
<ul>
<li><code>--line-length 88</code></li>
<li>
<code>--skip-string-normalization</code><br>
(si présent ne remplace pas <code>'texte'</code> par <code>"texte"</code>)</li>
</ul>
<p>Exemple :</p>
<pre><code class="shell">$ black .
reformatted bateau.py
All <span class="k">done</span>! ✨ 🍰 ✨
<span class="m">1</span> file reformatted.</code></pre>
<pre><code class="python"><span class="err">$</span> <span class="n">cat</span> <span class="n">bateau</span><span class="o">.</span><span class="n">py</span>
<span class="n">capitaine</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"age"</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span> <span class="s2">"nom"</span><span class="p">:</span> <span class="s2">"Grant"</span><span class="p">,</span> <span class="s2">"pays"</span><span class="p">:</span> <span class="s2">"Royaume-Uni"</span><span class="p">}</span> <span class="c1"># univers ?</span>
<span class="n">navire</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"nom"</span><span class="p">:</span> <span class="s2">"Britannia"</span><span class="p">,</span>
<span class="s2">"longueur"</span><span class="p">:</span> <span class="mi">127</span><span class="p">,</span> <span class="c1"># metres</span>
<span class="s2">"tonnage"</span><span class="p">:</span> <span class="mi">5860</span><span class="p">,</span>
<span class="s2">"lancement"</span><span class="p">:</span> <span class="s2">"16 mars 1953"</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">mission</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"commandant"</span><span class="p">:</span> <span class="n">capitaine</span><span class="p">,</span> <span class="s2">"bateau"</span><span class="p">:</span> <span class="n">navire</span><span class="p">}</span>
<span class="n">f</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="bp">True</span> <span class="k">if</span> <span class="n">x</span> <span class="o">%</span> <span class="mi">9</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="bp">False</span></code></pre>
<p>Une discussion a été ouverte sur le fait de passer le code de la lib standard de Python par <code>black</code>, mais, pour l’instant, il y a pas mal d’éléments qui font que ça n’aura pas lieu. Un des arguments principaux est de ne pas surcharger le nombre d’outils nécessaires pour une contribution à Python.</p>
<h3 id="toc-blue"><code>blue</code></h3>
<p>Le projet <a href="https://pypi.org/project/blue/"><code>blue</code></a> est un dérivé de <code>black</code> avec quelques ajustements sur les points qui sont les plus controversés.</p>
<p>Les différences avec <code>black</code>:</p>
<ul>
<li>utilisation des simples guillemets pour les chaînes de caractère (en dehors de docstring) ;</li>
<li>longueur de ligne à 79 caractères ;</li>
<li>configuration via plusieurs mécanismes possibles, <code>pyproject.toml</code>, <code>setup.cfg</code>, <code>tox.ini</code>, et <code>.blue</code>. </li>
</ul>
<p>Il s’utilise à l’identique de <code>black</code> et est disponible sous pypi.org</p>
<h3 id="toc-yapf"><code>yapf</code></h3>
<p>Le projet <a href="https://github.com/google/yapf">Yet Another Python Formatter</a> est plus vieux que <code>black</code> (premier commit en mars 2015), a moins d’étoiles (9 700) et le même nombre de contributeurs.</p>
<p>L’innovation de <code>yapf</code> réside dans la réutilisation du puissant <a href="https://github.com/llvm-mirror/clang/tree/master/tools/clang-format"><code>clang-format</code></a>. Les règles de sa configuration sont prises en compte pour calculer le score de tel ou tel reformatage et de boucler ainsi afin d’obtenir le meilleur score.</p>
<p>L’idée est superbe, mais en pratique, on passe trop de temps à essayer de peaufiner la configuration sans trop comprendre quel paramètre influe sur telle indentation. Et comme c’est configurable, une personne va passer du temps pour tenter d’améliorer les choses. Et pire, dans de rares circonstances, <code>yapf</code> peut reformater un code source différemment deux fois de suite ! (avec la même configuration)</p>
<p>En fait, le seul paramètre que nous devrions tester c’est <code>--style</code> avec les valeurs actuelles : <code>pep8</code> (défaut), <code>google</code>, <code>chromium</code> et <code>facebook</code>.</p>
<p>Le résultat à partir du même fichier d’origine quel que soit le paramètre <code>--style</code> :</p>
<pre><code class="python"><span class="err">$</span> <span class="n">yapf</span> <span class="n">bateau</span><span class="o">.</span><span class="n">py</span>
<span class="n">capitaine</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'age'</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span> <span class="c1"># univers ?</span>
<span class="s1">'nom'</span><span class="p">:</span> <span class="s1">'Grant'</span><span class="p">,</span>
<span class="s1">'pays'</span><span class="p">:</span> <span class="s1">'Royaume-Uni'</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">navire</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'nom'</span><span class="p">:</span> <span class="s1">'Britannia'</span><span class="p">,</span>
<span class="s1">'longueur'</span><span class="p">:</span> <span class="mi">127</span><span class="p">,</span> <span class="c1"># metres</span>
<span class="s1">'tonnage'</span><span class="p">:</span> <span class="mi">5860</span><span class="p">,</span>
<span class="s1">'lancement'</span><span class="p">:</span> <span class="s2">"16 mars 1953"</span>
<span class="p">}</span>
<span class="n">mission</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"commandant"</span><span class="p">:</span> <span class="n">capitaine</span><span class="p">,</span>
<span class="s1">'bateau'</span><span class="p">:</span> <span class="n">navire</span><span class="p">,</span>
<span class="p">}</span></code></pre>
<h3 id="toc-autopep8"><code>autopep8</code></h3>
<p>Le projet <a href="https://github.com/hhatto/autopep8"><code>autopep8</code></a> est encore plus vieux (premier commit en décembre 2010), a encore moins d’étoiles (3 000) et moins de contributeurs (une trentaine).</p>
<p>Ce formateur de code est beaucoup moins agressif que les deux premiers, car il ne reformate pas ce qui est compatible avec les règles PEP8. Cependant quelques corrections sont intéressantes comme le remplacement de <code>f = lambda x:</code> par <code>def f(x):</code>.</p>
<p>Le formateur <code>autopep8</code> semble avoir <code>--max-line-length</code> comme seule règle de formatage. En fait, sa configuration est différente des deux autres : l’option <code>--ignore</code> permet de désactiver des règles. Les options <code>--aggressive</code> et <code>--experimental</code> sont intéressantes.</p>
<p>Exemple :</p>
<pre><code class="python"><span class="err">$</span> <span class="n">autopep8</span> <span class="o">--</span><span class="n">aggressive</span> <span class="o">--</span><span class="n">aggressive</span> <span class="o">--</span><span class="n">aggressive</span> <span class="n">bateau</span><span class="o">.</span><span class="n">py</span>
<span class="n">capitaine</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'age'</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span> <span class="c1"># univers ?</span>
<span class="s1">'nom'</span><span class="p">:</span> <span class="s1">'Grant'</span><span class="p">,</span>
<span class="s1">'pays'</span><span class="p">:</span> <span class="s1">'Royaume-Uni'</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">navire</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'nom'</span><span class="p">:</span> <span class="s1">'Britannia'</span><span class="p">,</span>
<span class="s1">'longueur'</span><span class="p">:</span> <span class="mi">127</span><span class="p">,</span> <span class="c1"># metres</span>
<span class="s1">'tonnage'</span><span class="p">:</span> <span class="mi">5860</span><span class="p">,</span>
<span class="s1">'lancement'</span><span class="p">:</span> <span class="s2">"16 mars 1953"</span>
<span class="p">}</span>
<span class="n">mission</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"commandant"</span><span class="p">:</span> <span class="n">capitaine</span><span class="p">,</span> <span class="s1">'bateau'</span><span class="p">:</span> <span class="n">navire</span><span class="p">,</span> <span class="p">}</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">True</span> <span class="k">if</span> <span class="n">x</span> <span class="o">%</span> <span class="mi">9</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="bp">False</span></code></pre>
<h3 id="toc-isort"><code>isort</code></h3>
<p>Le projet <a href="https://github.com/PyCQA/isort.git"><code>isort</code></a> a une ambition plus modeste que les précédents formateurs de code. Il se focalise sur les imports et vous propose de les reformater pour vous simplifier la vie. La première version officielle date de 2013 et le projet est toujours assez actif et dispose de 3900 étoiles sous GitHub.</p>
<p>Le README du projet donne un bon exemple de son action.</p>
<p>Avant :</p>
<pre><code class="python"><span class="kn">from</span> <span class="nn">my_lib</span> <span class="kn">import</span> <span class="n">Object</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">my_lib</span> <span class="kn">import</span> <span class="n">Object3</span>
<span class="kn">from</span> <span class="nn">my_lib</span> <span class="kn">import</span> <span class="n">Object2</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">third_party</span> <span class="kn">import</span> <span class="n">lib15</span><span class="p">,</span> <span class="n">lib1</span><span class="p">,</span> <span class="n">lib2</span><span class="p">,</span> <span class="n">lib3</span><span class="p">,</span> <span class="n">lib4</span><span class="p">,</span> <span class="n">lib5</span><span class="p">,</span> <span class="n">lib6</span><span class="p">,</span> <span class="n">lib7</span><span class="p">,</span> <span class="n">lib8</span><span class="p">,</span> <span class="n">lib9</span><span class="p">,</span> <span class="n">lib10</span><span class="p">,</span> <span class="n">lib11</span><span class="p">,</span> <span class="n">lib12</span><span class="p">,</span> <span class="n">lib13</span><span class="p">,</span> <span class="n">lib14</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">absolute_import</span>
<span class="kn">from</span> <span class="nn">third_party</span> <span class="kn">import</span> <span class="n">lib3</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Hey"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"yo"</span><span class="p">)</span></code></pre>
<p>Après isort :</p>
<pre><code class="python"><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">absolute_import</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">third_party</span> <span class="kn">import</span> <span class="p">(</span><span class="n">lib1</span><span class="p">,</span> <span class="n">lib2</span><span class="p">,</span> <span class="n">lib3</span><span class="p">,</span> <span class="n">lib4</span><span class="p">,</span> <span class="n">lib5</span><span class="p">,</span> <span class="n">lib6</span><span class="p">,</span> <span class="n">lib7</span><span class="p">,</span> <span class="n">lib8</span><span class="p">,</span>
<span class="n">lib9</span><span class="p">,</span> <span class="n">lib10</span><span class="p">,</span> <span class="n">lib11</span><span class="p">,</span> <span class="n">lib12</span><span class="p">,</span> <span class="n">lib13</span><span class="p">,</span> <span class="n">lib14</span><span class="p">,</span> <span class="n">lib15</span><span class="p">)</span>
<span class="kn">from</span> <span class="nn">my_lib</span> <span class="kn">import</span> <span class="n">Object</span><span class="p">,</span> <span class="n">Object2</span><span class="p">,</span> <span class="n">Object3</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"Hey"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s2">"yo"</span><span class="p">)</span></code></pre>
<p>Donc concrètement :</p>
<ul>
<li>regroupement des imports par groupe, les <code>__future__</code> en premier, la lib standard en deuxième, les autres lib par la suite ;</li>
<li>regroupement des imports divers d’une bibliothèque sur un seul import ;</li>
<li>tri par ordre alphabétique des imports au sein d’un même groupe ;</li>
<li>formatage sous forme de bloc équilibré pour <code>lib1</code> à <code>lib15</code> par exemple ;</li>
<li>on ne touche pas au reste du code.</li>
</ul>
<p>L’ambition du projet est modeste mais sympathique, et ça fonctionne bien. À voir s’il est essentiel pour vous d’avoir de beaux imports dans vos projets. Perso, je ne l’utiliserais pas sur mes petits projets, mais sur des projets un peu gros, gérés sur un temps long avec une équipe qui bouge, ça peut être une bonne idée.</p>
<h3 id="toc-bilan">Bilan</h3>
<h4 id="toc-retour-doliver"><em>Retour d’Oliver</em></h4>
<p>Personnellement, je regrette que <code>black</code> mette sur une seule ligne un petit dictionnaire que je trouve plus lisible sur plusieurs lignes. Je suis déçu des quatre styles fournis par <code>yapf</code>, et je n’ai pas trouvé une superbe configuration magique. Finalement, sur notre projet c’est le bon vieux vénérable <code>autopep8</code> qui est utilisé, car il ne change que très peu le code source que nous écrivons.</p>
<p>Sur notre projet on utilise <code>black</code> dans une ancienne version non taguée. L’intégration continue vérifie que le formatage est « standard ». Et contraint tous les contributeurs à utiliser la même version. Et par la même, limite la mise à jour de <code>black</code>. Malgré ce petit désagrément, j’ai configuré l’outil pour formater à chaque sauvegarde. Laisser un outil faire le formatage complet est très confortable. Avec une grosse base de code, j’ai vraiment autre chose à faire qu’aligner des commentaires et des valeurs de dictionnaires.</p>
<h4 id="toc-retour-de-philippe"><em>Retour de Philippe</em></h4>
<p>J’ai testé pour vous les différents <code>formatters</code> sur un petit projet libre. Il s’agit d’un projet développé initialement par un stagiaire, que j’ai retravaillé avant que ma boite ne le mette en open source. Du coup, le style est un peu hétérogène, ça fait un bon candidat.</p>
<p>Voici les liens vers les diff sous GitHub :</p>
<ul>
<li>
<a href="https://github.com/bluebird75/sxtool/commit/ffca043d63bf649efc3a59528991f67398861116">reformatage avec black, zéro configuration</a> ;</li>
<li>
<a href="https://github.com/bluebird75/sxtool/commit%C2%A0;/8523a4354e75df924ab3a12c255000f95db4fd41">reformatage avec black</a>, longueur de ligne à 120, pas de changements sur les chaînes (conservation des simple quote) et pas de <code>magic-trailing-comma</code> (je n’ai aucune idée de ce que ça fait) ;</li>
<li>
<a href="https://github.com/bluebird75/sxtool/commit/89fe05e28d1df64bc1be0a6f8bf7cfd7afcff95e">reformatage avec blue</a>, longueur de ligne à 120 ;</li>
<li>
<a href="https://github.com/bluebird75/sxtool/commit/ae4f8e07e82de84df22e516cff0bcdf3c1c41c9c">reformatage avec yapf</a>, longueur de ligne à 110 ;</li>
<li>
<a href="https://github.com/bluebird75/sxtool/commit/9f29f578eeddc2f44e75e24065059ce6a3a28d83">reformatage avec autopep8</a> ;</li>
<li>
<a href="https://github.com/bluebird75/sxtool/commits/reformat-isort">reformatage avec isort</a>.</li>
</ul>
<p>Mon point de vue général : sur un projet où je travaille seul, je vais pas utiliser de formateur de code. Je sais assez bien ce que j’aime et je suis relativement cohérent sur mon style. Sur un projet en équipe, c’est à réfléchir, mais je trouve le style de <code>black/blue</code> plutôt désagréable. Ma tentation de l’utiliser viendra donc de l’écart entre mon style et celui de mes coparticipants : s’il est grand, autant unifier avec un outil extérieur. Si on est proche, on garde comme ça.</p>
<p>À noter que j’ai un style pas forcément commun. J’aime bien que l’affichage ait une densité raisonnable. Par exemple, si je peux faire en sorte de voir la totalité d’une fonction sur un seul écran en tirant parti de lignes un peu plus longues, il y a un vrai bénéfice pour moi puisque je peux capturer en un coup d’œil l’ensemble du traitement. C’est pour ça que j’ai réglé la longueur maximum de ligne à 110 (ou 120 quand je me suis gouré) car c’est ce que j’affiche sans problème sur un portable 14 pouces. J’utilise aussi le formatage pour faire ressortir des similitudes dans le code, ce qui va s’opposer parfois à un formatage agressif tenant compte avec rigueur du niveau d’imbrication des structures.</p>
<p>Mon bilan sur ce projet-là :</p>
<ul>
<li>
<code>isort</code>, c’est gentil, j’aime bien, mais je vois pas trop l’intérêt. La valeur ajoutée est vraiment très faible, surtout que j’aime bien grouper tous les imports de la stdlib de Python sur une seule ligne (notion de densité de code évoquée plus haut), ce qu’il se refuse à faire (comme la majorité des gens) ;</li>
<li>
<code>autopep8</code>, c’est assez peu envahissant. Ça me convient bien pour rectifier une base de code comme dans le projet que j’ai pris, sans pour autant tout péter mon style. J’aime bien ;</li>
<li>
<code>yapf</code>, <code>black</code>, <code>blue</code> : honnêtement, je suis entre deux. Il y a clairement des gains en lisibilité par endroits, et d’autres où le code devient inutilement étalé sur plusieurs lignes et où la perte de densité me semble dommageable à la compréhension globale. Donc je suis réservé sur l’amélioration, mais pas hostile au concept en général.</li>
</ul>
<p>Finalement, tout ça est vraiment très subjectif. Je comprends tout à fait pourquoi des gros projets ont adopté <code>black</code>, au moins, on évite ce type de discussion et le style reste tout à fait raisonnable.</p>
<h2 id="toc-formateurs-de-docstring">Formateurs de docstring</h2>
<h3 id="toc-docformatter"><code>docformatter</code></h3>
<p>Le projet <a href="https://github.com/myint/docformatter"><code>docformatter</code></a> permet de reformater la partie <a href="https://en.wikipedia.org/wiki/Docstring#Python">docstring</a> du code source.</p>
<p>Nous l’utilisons avec ces paramètres :</p>
<pre><code class="shell">docformatter --wrap-summaries <span class="m">444</span> --pre-summary-newline --in-place --recursive .</code></pre>
<h3 id="toc-pyment"><code>pyment</code></h3>
<p>La documentation du code source Python se fait à l’aide des <a href="http://sametmax.com/les-docstrings/"><strong>docstring</strong></a> standardisée par la <a href="https://www.python.org/dev/peps/pep-0257/">PEP 257</a> (2001). Plusieurs types de <strong>docstring</strong> sont utilisés, les plus connus étant :</p>
<ul>
<li>le format <a href="https://fr.wikipedia.org/wiki/ReStructuredText">reStructuredText</a> de la <a href="https://www.python.org/dev/peps/pep-0287/">PEP 287</a> (2002) ;</li>
<li>le <a href="https://google.github.io/styleguide/pyguide.html#comments">format recommandé par Google</a> (<a href="https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html">exemple</a>) ;</li>
<li>le format utilisé par le projet NumPy, connu sous le nom de <a href="https://numpydoc.readthedocs.io/en/latest/format.html">numpydoc</a> (<a href="https://numpydoc.readthedocs.io/en/latest/example.html#example">exemple</a>).</li>
</ul>
<p>L’outil <code>pyment</code> permet de créer, corriger et de modifier ces représentations docstring.</p>
<p>L’auteur de <a href="https://pypi.org/project/pyment/"><code>pyment</code></a>, <a href="http://daouzli.com/blog/pages/About.html">Adel Daouzli</a> (dadadel) nous avait <a href="//linuxfr.org/users/dadadel/journaux/pyment-patcher-les-docstrings-python">présenté son outil dans son journal</a> (2014). Mais Adel ne semble plus maintenir le code source ces derniers temps.</p>
<p>Comme plusieurs bugs sont corrigés dans des <em>Pull Requests</em> fights, j’ai donc pris en compte ces corrections et autres améliorations apportées et j’ai tout *<em>mergé</em> sur la branche <code>olibre</code> publiée sur un triple <em>fork</em> :</p>
<ul>
<li><a href="https://framagit.org/olibre/pyment">https://framagit.org/olibre/pyment</a></li>
<li><a href="https://gitlab.com/olibre/pyment">https://gitlab.com/olibre/pyment</a></li>
<li><a href="https://github.com/olibre/pyment">https://github.com/olibre/pyment</a></li>
</ul>
<p>Attention, l’annotation des types (<a href="https://docs.python.org/fr/3/library/typing.html"><em>type hints</em> Python 3.5</a>) n’est pas prise en charge par <code>pyment</code>.</p>
<h3 id="toc-génération-de-la-documentation">Génération de la documentation</h3>
<p>C’est pratique quand la documentation de son code source et automatiquement générée. Deux outils intéressants :</p>
<ul>
<li>
<a href="https://en.wikipedia.org/wiki/Pdoc">Pdoc</a>, successeur du bon vieux <a href="https://en.wikipedia.org/wiki/Epydoc">Epydoc</a> ;</li>
<li>
<a href="https://fr.wikipedia.org/wiki/Sphinx_(g%C3%A9n%C3%A9A9rateur_de_documentation)">Sphinx</a>, LE générateur de documentation Python le plus connu.</li>
</ul>
<p>Attention, pour Pdoc, nous avons deux projets qui ont divergé : <a href="https://github.com/mitmproxy/pdoc"><code>pdoc</code></a> (l’original) et <a href="https://github.com/pdoc3/pdoc"><code>pdoc3</code></a> (le <em>fork</em>, plus actif). </p>
<h2 id="toc-analyse-statique-de-code">Analyse statique de code</h2>
<h3 id="toc-pylint">pylint</h3>
<p><a href="https://www.pylint.org/">Pylint</a> est je cite : « un outil qui recherche des erreurs dans le code Python, qui essaye d’imposer un standard de codage et qui cherche du <em>code malodorant</em> (code smells). Il peut aussi trouver certains types d’erreurs, faire des recommandations sur la façon dont un bloc peut être réorganisé et détaille la complexité du code ».</p>
<p>Pylint est un projet ancien (plus de 15 ans), qui analyse le code Python dans plusieurs optiques différentes :</p>
<ul>
<li>conformité à un style de codage, le fameux pep8 plus quelques petits détails supplémentaires ;</li>
<li>analyse de la complexité du code (nombre de chemins d’exécution dans une fonction, etc.) ;</li>
<li>erreurs de codage ;</li>
<li>améliorations possibles (suppression de parenthèses, simplifications…).</li>
</ul>
<p>Chaque problème reporté peut-être désactivable, en ligne de commande ou via une variété de fichiers de configuration (.pylintrc, pyproject.toml ou setup.cfg). On peut aussi dans le code activer ou désactiver spécifiquement des configurations, au niveau du fichier, d’une fonction, d’un bloc de code ou, tout simplement, d’une ligne.</p>
<h4 id="toc-retour-de-philippe-1">Retour de Philippe</h4>
<p>J’ai fait une tentative sur le même projet, <a href="https://github.com/bluebird75/sxtool">sxtool</a>. J’ai un peu galéré pour le lancer et je n’ai pas trouvé la ligne magique où il comprend tous les imports de mon projet. La première exécution m’a retourné <a href="https://gist.github.com/bluebird75/dcfb7b66d9f7ba70bb6c982f0214fea8">1 500 lignes d’erreurs</a>. La très grande majorité sont des erreurs de style (lignes trop longues, nommage des variables pas en snake_case, docstring manquantes, absence de fin de ligne en fin de fichier…). En désactivant les erreurs de style les plus courantes, je tombe sur <a href="https://gist.github.com/bluebird75/34b1000e4171761606a74c4d966b143d">quelques erreurs plus intéressantes</a> du type :</p>
<ul>
<li>trop de <code>return</code> dans une fonction ;</li>
<li>trop de chemins d’exécution dans une fonction ;</li>
<li>variables ou import inutilisés ;</li>
<li>redéfinition de nom pré-intégrés, format, file ;</li>
<li>clause d’attrapage d’exception trop large ;</li>
<li>
<code>else</code> inutile après un <code>return</code>.</li>
</ul>
<p>Les problèmes de code signalés sont légitimes. Ils correspondent à du code peu lisible et des erreurs liées au manque de familiarité avec Python de l’auteur initial. Le code correspondant a été développé par un stagiaire qui débutait en Python.</p>
<p>Mais, une fois ce constat fait, il est totalement irréaliste d’imaginer passer du temps à rectifier le code en question. Ce serait très coûteux en temps, et le bénéfice reste modeste. Exiger du code avec un style parfaitement conforme est l’apanage de quelques rares projets ou entreprises de logiciel très exigeantes. Le reste du monde vit très bien avec du code aux styles complètement hétérogènes (et je suis le premier à le regretter). Essayez de le mettre en place dans une équipe et vous verrez ! C’est ce qui fait que les linters Python, bien qu’existant depuis longtemps, ne sont pas plus populaires que cela. Honnêtement, se faire rappeler à l’ordre par un outil parce qu’il manque une espace après une virgule, c’est très pénible.</p>
<p>L’approche récente du reformatage prise par <code>black</code> et consorts résout ce problème de façon plus pérenne.</p>
<p>Concernant l’analyse de la complexité du code, j’aime beaucoup le concept, mais j’imagine mal le mettre en place. Sur mes projets solo, je suis déjà attentif à la complexité et la lisibilité, donc il ne m’apportera rien. Sur des projets en équipe, les gens qui vont être favorables à la mise en place d’un tel outil sont justement ceux qui sont conscients du problème de la complexité du code, et qui ont déjà tendance à ne pas privilégier ce style. Les non-favorables tombent vite dans des guerres de chapelle (« mais si, sept <code>if</code> imbriqués, c’est très bien ! ») et on ne s’en sort pas. Les cas qui me paraissent réalistes pour la mise en place seraient ceux où des managers sont conscients des bénéfices d’un code peu complexe et imposent l’outil. Ou alors une équipe qui aborde une base de code héritée importante et qui souhaite cibler les modules où le risque de bug est plus élevé qu’ailleurs.</p>
<p>Restent, enfin, les erreurs que peut détecter <code>pylint</code>. On en trouve la liste dans la <a href="https://pylint.readthedocs.io/en/latest/technical_reference/features.html">documentation de référence</a>. Les classes d’erreur ont l’air intéressantes bien que certaines soient un peu louches à mon goût : je vois pas bien comment du code pourrait tourner avec certaines des erreurs qui sont signalées. J’imagine qu’elles sont pourtant toutes basées sur des cas réels. </p>
<p>Voici quelques erreurs prises au hasard :</p>
<ul>
<li>nonlocal-and-global (E0115): Emitted when a name is both nonlocal and global ;</li>
<li>not-in-loop (E0103): Used when break or continue keywords are used outside a loop ;</li>
<li>return-in-init (E0101): Used when the special class method <strong>init</strong> has an explicit return value ;</li>
<li>inherit-non-class (E0239): Used when a class inherits from something which is not a class ;</li>
<li>…</li>
</ul>
<p><code>pylint</code> peut être lancé avec <code>-E</code> pour ne signaler que les erreurs de ce type. Serait-ce parce que c’est sa plus grande valeur ajoutée ?</p>
<h4 id="toc-mon-bilan">Mon bilan</h4>
<p>Le concept est intéressant mais le côté pédant de l’outil est désagréable et les autres bénéfices restent trop réduits. Je pense que mettre en place des revues de code sera plus efficace et plus constructif que de passer un projet à <code>pylint</code>.</p>
<h3 id="toc-pyflakes">pyflakes</h3>
<p><a href="https://github.com/PyCQA/pyflakes"><code>PyFlakes</code></a> est avec <code>pylint</code> un des anciens linter/checker de code Python : les premières versions datent de 2009. Le principe est simple : <em>un programme simple qui vérifie les fichiers source Python à la recherche d’erreurs.</em> En complément, le <code>README</code> ajoute <em>il ne va jamais se plaindre à propos du style, il va essayer très très intensément de ne jamais émettre de faux positifs</em> .</p>
<h4 id="toc-retour-de-philippe-2">Retour de Philippe</h4>
<p>Tout ça est très prometteur. Par contre, la documentation ajoute que <em><code>pyflakes</code> est plus limité dans les types d’erreurs qu’il peut trouver, car il inspecte l’arbre de syntaxe plutôt qu’importer le code</em>.</p>
<p>Voyons voir ce que ça donne, je prends le même cobaye, <a href="https://github.com/bluebird75/sxtool">sxtool</a>. Pas de problème à l’installation, pas de problème à l’exécution. Il me signale une cinquantaine d’erreurs qui ne correspondent en fait qu’à deux cas :</p>
<ul>
<li>un nom est importé mais pas utilisé ;</li>
<li>une variable ou un argument est inutilisé.</li>
</ul>
<p>Intéressant, mais pas fantastique. Mon projet n’a pas d’erreur manifeste, c’est cool. </p>
<p>Je jette un coup d’œil à la documentation pour en savoir plus sur le potentiel de <code>pyflakes</code> pour découvrir qu’il n’y a pas de documentation. Impossible de savoir quelles classes d’erreurs sont détectées. La lecture du <a href="https://github.com/PyCQA/pyflakes/blob/master/NEWS.rst">ChangeLog</a> laisse entrevoir quelques idées mais sans plus.</p>
<p>Rien non plus sur la configuration, on ne peut pas ignorer certains fichiers ou annoter une ligne pour ignorer une erreur. Il me semble que c’est parce que <code>pyflakes</code> ne s’utilise plus tel quel. Le projet a fait cause commune avec un autre linter, <code>pep8</code>, pour former <code>flake8</code>, un lanceur de linter/checker Python. flake8 est couvert dans la suite de la dépêche et c’est lui qui permet de configurer finement la vérification d’un fichier et la désactivation de certaines erreurs. Par contre, la documentation de <code>flake8</code> n’en dit pas plus sur les types de vérifications effectuées par <code>pyflakes</code>.</p>
<p>En conclusion, je n’ai pas pu mettre en évidence l’intérêt de <code>pyflakes</code>, mais je sais qu’il a plutôt une bonne réputation dans la communauté Python. Mon projet cobaye est aussi assez simple, il n’utilise que très peu de fonctionnalités de Python. Sur des projets plus élaborés fonctionnellement, j’imagine qu’il peut trouver des erreurs intéressantes.</p>
<p>Si vous utilisez pyflakes et que vous connaissez sa valeur, n’hésitez pas à nous le partager dans les commentaires.</p>
<h3 id="toc-flake8">flake8</h3>
<p>Flake8 est un lanceur de linter. Il est né du rapprochement des projets pyflakes et pep8 (qui est devenu pycodestyle au passage). La version de base fait du 3-en-1 :</p>
<ul>
<li>PyFlakes, la recherche d’erreurs générales ;</li>
<li>pycodestyle, les vérifications de style façon pep8 ;</li>
<li>le script McCabe de Ned Batchelder, la vérification de la complexité du code.</li>
</ul>
<p>Flake8 exécute tous les outils en lançant la commande unique flake8. Il affiche les avertissements par fichiers dans une sortie commune.</p>
<p>Il ajoute également quelques fonctionnalités :</p>
<ul>
<li>les fichiers qui contiennent cette ligne sont ignorés :</li>
</ul>
<pre><code># flake8 : noqa
</code></pre>
<ul>
<li>les lignes qui contiennent un commentaire # noqa à la fin n’émettront pas d’avertissement ;</li>
<li>vous pouvez ignorer des erreurs spécifiques sur une ligne avec # noqa : , par exemple, # noqa : E234. Plusieurs codes peuvent être donnés, séparés par une virgule, le jeton noqa n’est pas sensible à la casse, les deux points avant la liste des codes sont nécessaires, sinon la partie après noqa est ignorée ;</li>
<li>des hooks Git et Mercurial ;</li>
<li>extensible via les points d’entrée flake8.extension et flake8.formatting.</li>
</ul>
<h4 id="toc-configuration">Configuration</h4>
<p>Exemple de fichier de configuration :</p>
<pre><code>[flake8]
max-line-length = 88
select = C,E,F,W,B,B9
ignore = E203, E501, W503
exclude = __init__.py
</code></pre>
<p>Ce contenu peut être glissé dans un fichier <code>.flake8</code>, ou dans <code>tox.ini</code> ou encore un <code>setup.cfg</code>, ce qui permet de s’intégrer dans un fichier de config partagé avec d’autres outils de l’écosystème de packaging python.</p>
<p>La force de flake8, c’est qu’on peut facilement rajouter des plugins pour compléter son travail. Il existe des plugins pour tout un tas de vérifications supplémentaires, pour lancer d’autres outils ou pour adapter le format de sortie à des services spécifiques.</p>
<h4 id="toc-retour-de-philippe-3">Retour de Philippe</h4>
<p>flake8 a une bonne réputation dans l’écosystème Python. Je l’ai essayé toujours sur mon projet cobaye <a href="https://github.com/bluebird75/sxtool">sxtool</a>. Je n’ai récupéré que des erreurs de style, et une ou deux variables non utilisées. En forçant le test de complexité à maximum 5, j’ai récupéré une erreur due à la complexité trop élevée d’une fonction.</p>
<p>Je suis plutôt déçu. Les erreurs de style ne m’intéressent pas, je les traiterai avec black. Mais, pas moyen de les ignorer toutes d’un coup. Pas d’erreurs de codage reporté, c’est bien pour mon projet, mais je n’ai toujours aucune idée du type d’erreur qui peut être détecté. Pour la complexité, pylint avait trouvé plus de fonctions nécessitant un retravail, je suis également déçu.</p>
<p>L’écosystème de plugin est réputé riche, mais là encore, la documentation n’en mentionne presque aucun. Vous pouvez piocher dans la longue liste de <a href="https://github.com/DmytroLitvinov/awesome-flake8-extensions">awesome-flake8-extensions</a> pour trouver votre bonheur. On trouve pas mal de plugins pour lancer d’autres outils dans <code>flake8</code>, genre <code>pylint</code> ou <code>mypy</code> ou encore <code>bandit</code>. On trouve aussi pas mal de plugins pour ajuster le format de sortie à un besoin spécifique, et encore des plugins pour faire quelques vérifications très ciblées. </p>
<h3 id="toc-bandit">bandit</h3>
<p><a href="https://github.com/PyCQA/bandit">Bandit</a> est un outil conçu pour trouver de failles de sécurité connues dans du code Python. Comme Pylint, il analyse les fichiers Python en construisant leur arbre syntaxique (AST) et exécute un ensemble de vérification sur ce dernier. Le projet existe depuis 2015 et a reçu plus de 3000 étoiles GitHub. </p>
<p>Bandit est extensible par plugin, à la fois pour rajouter des vérifications ou pour modifier le format de sortie. On le configure par un fichier en YAML ou par des directives dans les fichiers ou lignes de code concernées.</p>
<h4 id="toc-retour-de-philippe-4">Retour de Philippe</h4>
<p>Avant de lancer le projet, je note déjà que la documentation est bien faite et couvre les aspects qui m’intéressent facilement. Alors, toujours sur mon projet <a href="https://github.com/bluebird75/sxtool">sxtool</a>, que donne bandit ?</p>
<pre><code> > bandit -r sxtool
</code></pre>
<pre><code>[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.8.8
Run started:2021-05-15 16:49:52.455989
</code></pre>
<pre><code>[...]
</code></pre>
<pre><code>--------------------------------------------------
>> Issue: [B318:blacklist] Using xml.dom.minidom.parse to parse untrusted XML data is known to be vulnerable to XML attacks. Replace xml.dom.minidom.parse with its defusedxml equivalent function or make sure defusedxml.defuse_stdlib() is called
Severity: Medium Confidence: High
Location: .\src\utils.py:20
More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html#b313-b320-xml-bad-minidom
19 try:
20 self.tree = dom.parse(fileName)
21 except sax.SAXParseException :
--------------------------------------------------
</code></pre>
<pre><code>Code scanned:
Total lines of code: 3073
Total lines skipped (#nosec): 0
Run metrics:
Total issues (by severity):
Undefined: 0.0
Low: 5.0
Medium: 1.0
High: 1.0
Total issues (by confidence):
Undefined: 0.0
Low: 0.0
Medium: 0.0
High: 7.0
Files skipped (0):
</code></pre>
<p>Donc sept problèmes majeurs! Quand même! Si vous voulez voir les détails, <a href="https://gist.github.com/bluebird75/a85948e84d963e6ab52c0a6c2b1ceb0d">c’est ici</a>. </p>
<p>Les catégories de problème trouvés :</p>
<ul>
<li>utilisation de assert, alors qu’en compilant avec -O, les asserts disparaissent ;</li>
<li>lancement d’une commande dans un shell de façon non sécurisée ;</li>
<li>utilisation d’une bibliothèque non sécurisées pour parser du XML, notamment vulnérables à de l’injection de code.</li>
</ul>
<p>Tout ça est présenté dans un magnifique rapport, avec des belles couleurs selon le niveau de vulnérabilité estimé. Et on peut aussi en faire une version html, ou CSV. Pour chaque erreur, un lien vers la documentation indique la raison pour laquelle c’est un risque de sécurité, et pour la plupart la marche à suivre pour le corriger !</p>
<p>Conclusion : j’adore ! Le niveau de finition est très agréable !</p>
<h2 id="toc-annotation-de-type">Annotation de type</h2>
<p>Depuis 2006 et la version 3.0 de Python, il est possible de rajouter des annotations au code Python. Et depuis 2009, Dropbox livre un outil de vérification d’annotations de type pour Python : <code>mypy</code>. </p>
<p>Au fil des versions de Python, les annotations de type se sont généralisées (d’abord des arguments de fonctions à maintenant toutes les variables et attributs de classes) et simplifiées dans leur usage. Mypy a aussi continué à évoluer, permettant des définitions de plus en plus fines des types pour décrire un contenu. En parallèle, le nombre de projet avec du typage disponible n’a cessé d’évoluer, que ce soit directement dans le <a href="https://github.com/python/typeshed/tree/master/stubs">projet typeshed</a> qui regroupe les informations de types (typing stubs) de la lib standard ou alors livré en même temps avec le paquet concerné (comme Flask mentionné récemment dans une dépêche), ou encore via un paquet séparé qui ne fournit que les stubs (cas de PyQt5-stubs et django-stubs qui fournissent les stubs de respectivement PyQt5 et django).</p>
<p>Pour faire bref, ajouter des annotations de type à votre code va apporter les avantages suivants :</p>
<ul>
<li>les annotations créent une forme de documentation très compact des arguments et résultats des fonctions ;</li>
<li>les annotations permettent de garantir que votre code est utilisé de la bonne façon ; corollaire, ça permet de découvrir des bugs difficiles à trouver autrement, quand une fonction/méthode est utilisée de façon incorrecte, ou quand l’ensemble des types possibles d’une variable a mal été pris en compt ;</li>
<li>les IDE peuvent utiliser les annotations de types pour proposer une complétion plus intelligente.</li>
</ul>
<p>Bien sûr, tout cela a un coût :</p>
<ul>
<li>annoter une base de code est très rapide et simple au début, mais peut devenir assez fastidieux et chronophage si on vise le 100 % annoté. Heureusement, les outils fonctionnent très bien avec du code partiellement annoté ;</li>
<li>pour les cas complexes, il faut se pencher pas mal sur la documentation. C’est assez chronophage ;</li>
<li>certaines constructions dynamiques de Python, ou tout simplement la logique de votre code peut être impossible à capturer avec du typage statique ;</li>
<li>certaines annotations sont longues à écrire, et alourdissent la lisibilité du code : des définitions de fonctions vont passer de une ligne à plusieurs à cause de cela ;</li>
<li>la vérification de la cohérence globale, c’est un outil de plus à lancer, qui plus est un outil qui n’est pas forcément rapide, donc ça ralentit le processus de développement global.</li>
</ul>
<p>Depuis quelques années, Dropbox et Facebook/Instagram se sont mis au typage statique de tout leur code, et les retours des développeurs sont très positifs. Il y a eu plusieurs sessions au PyCon US sur ce sujet.</p>
<p>Pour comprendre l’intérêt du typage statique en Python, prenons un exemple simple:</p>
<pre><code class="python"><span class="k">def</span> <span class="nf">is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">if</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">True</span></code></pre>
<p>Bien qu’imparfaite, cette fonction va plutôt bien marcher sur tout ce qui implémente correctement l’égalité : les booléens, les entiers, les chaînes de caractères, etc. C’est pas mal, et ça veut aussi dire qu’on peut facilement passer à côté d’un bug. Voyons en pratique :</p>
<pre><code class="python"><span class="k">def</span> <span class="nf">print_is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">if</span> <span class="n">is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'Egalité pour'</span><span class="p">,</span> <span class="n">a</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'Différence:'</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span></code></pre>
<pre><code class="python"><span class="o">>>></span> <span class="n">print_is_equal</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">Egalit</span><span class="err">é</span> <span class="n">pour</span> <span class="mi">1</span>
<span class="o">>>></span> <span class="n">print_is_equal</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">Diff</span><span class="err">é</span><span class="n">rence</span><span class="p">:</span> <span class="mi">1</span> <span class="mi">2</span>
<span class="o">>>></span> <span class="n">print_is_equal</span><span class="p">(</span><span class="s1">'abc'</span><span class="p">,</span> <span class="s2">"abc"</span><span class="p">)</span> <span class="c1"># deux représentations de la même chaîne de caractère sont identiques</span>
<span class="n">Egalit</span><span class="err">é</span> <span class="n">pour</span> <span class="n">abc</span>
<span class="o">>>></span> <span class="n">print_is_equal</span><span class="p">(</span><span class="mf">0.3</span><span class="p">,</span> <span class="mf">0.3</span><span class="p">)</span>
<span class="n">Egalit</span><span class="err">é</span> <span class="n">pour</span> <span class="mf">0.3</span>
<span class="o">>>></span> <span class="n">print_is_equal</span><span class="p">(</span><span class="mf">0.3</span><span class="p">,</span> <span class="mf">0.2</span> <span class="o">+</span> <span class="mf">0.1</span><span class="p">)</span>
<span class="n">Diff</span><span class="err">é</span><span class="n">rence</span><span class="p">:</span> <span class="mf">0.3</span> <span class="mf">0.30000000000000004</span></code></pre>
<p>Oups ! Et oui, comme 0.1 se représente mal en base 2, il génère des erreurs dans les calculs. Donc, il faut éviter d’utiliser notre belle fonction avec des flottants. C’est ce que peut nous aider à faire les vérificateurs d’annotations de type.</p>
<p>Si on rajoute un brin de documentation et des annotations de type, voilà ce que ça donne :</p>
<pre><code class="python"><span class="k">def</span> <span class="nf">is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
<span class="sd">'''Compare two integers and return True if they are equal'''</span>
<span class="k">if</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">True</span>
<span class="k">def</span> <span class="nf">print_is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
<span class="sd">'''Display whether two numeric values are equal'''</span>
<span class="k">if</span> <span class="n">is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'Egalité pour'</span><span class="p">,</span> <span class="n">a</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'Différence:'</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="n">print_is_equal</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">print_is_equal</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">print_is_equal</span><span class="p">(</span><span class="mf">0.3</span><span class="p">,</span> <span class="mf">0.2</span> <span class="o">+</span> <span class="mf">0.1</span><span class="p">)</span></code></pre>
<p>Et lorsqu’on passe ce programme à travers <code>mypy</code> :</p>
<pre><code>>mypy src\is_equal.py
src\is_equal.py:1: error: Missing return statement
src\is_equal.py:16: error: Argument 1 to "print_is_equal" has incompatible type "float"; expected "int"
src\is_equal.py:16: error: Argument 2 to "print_is_equal" has incompatible type "float"; expected "int"
Found 3 errors in 1 file (checked 1 source file)
</code></pre>
<p>Il détecte bien, d’une part que la fonction est utilisée de façon incorrecte avec des flottants, d’autre part que nous avons oublié un <code>return</code> : la fonction renvoie <code>None</code> dans le cas d’une inégalité.</p>
<p>À noter que la documentation des deux fonctions est correcte sans être assez précise: <em>two numeric value</em> peut aussi bien faire référence à deux flottants qu’à deux entiers. De même, pour <em>is_equal()</em>, ce que retourne la fonction en cas d’inégalité n’est pas documenté et va fonctionner dans tous les tests qui ne vérifient pas exclusivement l’égalité à<code>False</code>. Le développeur avait sûrement en tête de retourner <code>False</code>, mais difficile d’en être sûr. C’est l’intérêt des annotations de type : elles obligent à plus de rigueur et elles capturent l’intention du développeur mieux que de la documentation.</p>
<p>Si vous voulez vous mettre à l’annotation de type dans Python, on trouve pas mal de ressources sur Internet, dont une conférence en français réalisée par un certain Philippe F.</p>
<p>Penchons-nous maintenant sur les outils de l’annotation de type.</p>
<h3 id="toc-mypy"><code>mypy</code></h3>
<p><a href="http://www.mypy-lang.org/">Mypy</a> est la référence en termes de vérification de typage. C’est l’émergence de mypy qui a permis aux annotations de s’imposer dans l’écosystème Python. L’outil est maintenu par l’équipe Python de Dropbox (dans laquelle <a href="https://fr.wikipedia.org/wiki/Guido_van_Rossum">Guido Van Rossum</a> a fait un séjour assez long). Le projet est très dynamique, avec des nouvelles versions fréquentes, qui permettent de pousser le typage de plus en plus finement. Mypy fournit <a href="https://mypy.readthedocs.io/">une documentation de bonne qualité</a> pour aider à se mettre au typage. L’outil dispose d’un <a href="https://mypy.readthedocs.io/en/stable/command_line.html">large jeu d’option</a>, qui permettent d’ajuster assez finement le niveau de typage qu’on souhaite, de très léger à très exigeant. C’est un peu comme les options de compilation de gcc, il y en a pour tous les goûts. Tout ça peut se régler aussi par un beau fichier de config au format ini.</p>
<p>Mypy gère les annotations Python 3 (directement dans le code) ou Python 2 (sous forme de commentaire). Comme pour les vérificateurs de code, il est possible directement depuis le code d’ignorer une erreur en ajoutant un commentaire <code># type: ignore</code>. On peut même préciser le type d’erreur à ignorer plus précisément.</p>
<p>Ça se lance en ligne de commande, mais comme l’outil est lent sur des grosses bases de code, on peut lancer <a href="https://mypy.readthedocs.io/en/stable/mypy_daemon.html">un serveur dmypy</a> qui va garder en cache les résultats intermédiaires et vérifier le code beaucoup plus vite.</p>
<p>Ah oui, et c’est écrit en Python, c’est pour ça que c’est lent ! (<em>attention un troll velu s’est caché dans la phrase précédente, à toi de le débusquer sans le nourrir !</em>).</p>
<h5 id="toc-retour-de-philippe-5">Retour de Philippe</h5>
<p>Sans surprise, je suis un grand fan de l’annotation de type et j’utilise <code>mypy</code> intensément. Il s’installe très simplement avec pip. À l’usage, sur mes projets, au fur à mesure que j’y rajoute les annotations de type, j’ai constaté que :</p>
<ul>
<li>c’est chronophage, notamment au début, où on se perd dans la documentation, et à la fin quand on essaye d’être 100 % typés, on croise des cas vraiment complexes à annoter ;</li>
<li>c’est bien documenté et on trouve facilement de l’aide, sur le site de mypy ou sur stackoverflow ;</li>
<li>il faut parfois modifier le code pour aider mypy avec quelques asserts, c’est sans conséquence et dans un certain nombre de cas, ça oblige à se poser les bonnes questions : est-ce que ma variable <code>trucmuche</code> peut encore être à <code>None</code> ou pas dans cette partie de code ?</li>
<li>je n’ai pas l’impression d’avoir trouvé des gros bugs avec ça, par contre, je sais que ma base de code est beaucoup plus fiable. Au boulot, j’ai récemment fait du « refactoring » sur des « callback » un peu poilus et j’étais content que <code>mypy</code> me pointe tous les endroits où je devais intervenir ;</li>
<li>l’aspect documentation compacte est incroyablement agréable. Mes collègues qui arrivent sur mes projets avec annotation de type me disent aussi qu’ils ont beaucoup plus de facilité pour comprendre le code. J’ai retravaillé un petit projet à moi de 15 ans d’âge récemment. J’étais à moitié perdu dans mon code de l’époque. J’ai décidé de le typer pour m’y retrouver mieux et ça a fait une vraie différence. </li>
</ul>
<p>Sur ce petit projet de 15 ans d’âge, je vous montre le code avant. C’est un tout petit bout de code qui doit déplacer une pièce d’un jeu.</p>
<pre><code class="python"> <span class="k">def</span> <span class="nf">move_tile</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pid</span><span class="p">,</span> <span class="n">d</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">move_enabled</span><span class="p">:</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">map</span><span class="o">.</span><span class="n">move</span><span class="p">(</span> <span class="n">pid</span><span class="p">,</span> <span class="n">d</span> <span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">board</span><span class="o">.</span><span class="n">move</span><span class="p">(</span> <span class="n">pid</span><span class="p">,</span> <span class="n">d</span> <span class="p">)</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span></code></pre>
<p>En revoyant ce code, je ne me rappelais plus ce qu’était <code>pid</code> et <code>d</code>. Avec les annotations, ça donne :</p>
<pre><code class="python"> <span class="k">def</span> <span class="nf">move_tile</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pid</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">d</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">move_enabled</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">map</span>
<span class="bp">self</span><span class="o">.</span><span class="n">map</span><span class="o">.</span><span class="n">move</span><span class="p">(</span> <span class="n">pid</span><span class="p">,</span> <span class="n">d</span> <span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">board</span><span class="o">.</span><span class="n">move</span><span class="p">(</span> <span class="n">pid</span><span class="p">,</span> <span class="n">d</span> <span class="p">)</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span></code></pre>
<p>Avec ces informations, j’ai recollé les morceaux: <code>pid</code> est un identifiant de pièce (piece-id) et <code>d</code> est le delta de déplacement, sous forme de deux entiers.</p>
<h3 id="toc-pyre"><code>pyre</code></h3>
<p><a href="https://pyre-check.org/">Pyre</a> est l’implémentation de Facebook pour la vérification de typage en Python. Écrite en OCaml, c’est un dérivé d’un moteur d’inférence de type qu’ils avaient déjà construit pour PHP. Au niveau de la vérification du typage statique, ils se sont calés sur <code>mypy</code>. Les commentaires pour supprimer une erreur façon mypy (<code># type: ignore</code>) sont d’ailleurs supporté aussi par pyre.</p>
<p>La documentation est correcte et le contrôle du niveau de vérification est un peu moins fin que mypy. En gros, on a vérification stricte ou pas stricte. </p>
<p>Pyre a la réputation d’être rapide pour valider beaucoup de lignes de code : ça fait quand même tourner plus d’un million de lignes de code chez Facebook. On peut notre quelques différences mineures d’interprétation entre <code>mypy</code> et <code>pyre</code>. En conséquence, il vaut mieux éviter d’utiliser les deux outils conjointement sur une base de code. Il y a un choix à faire.</p>
<p>À noter que Pyre fournit aussi <a href="https://pyre-check.org/docs/pysa-basics">Pysa</a> qui fait des vérifications de sécurité par analyse de code. Il vérifie qu’une chaîne de caractère sous contrôle de l’utilisateur (genre, un paramètre d’url) ne peut pas atteindre un composant critique (genre une écriture en base de donnée) sans passer par un désinfectant (« sanitizer »). Ce travail est fait en s’appuyant aussi sur les annotations, cette fois un peu moins orienté typage statique.</p>
<h5 id="toc-retour-de-philippe-6">Retour de Philippe</h5>
<p>Cette dépêche est l’occasion de faire un petit test de pyre. D’après la documentation, ça marche bien sous Linux et MacOs mais c’est expérimental sous Windows, et ça ne fonctionne que grâce a WSL (Windows Subsystem for Linux). Sur ma machine Windows, j’ai donc étrenné mon WSL Ubuntu avec pyre : ça se passe sans accroc, exactement comme décrit dans la documentation.</p>
<p>Pour mes projets PyQt, il n’interprète pas les stubs PyQt comme mypy. Du coup, du code avec 0 erreur sous mypy génère quelques erreurs sous pyre. Autre petit souci, la gestion de la valeur None ne se fait pas comme sous mypy, donc mon code qui plaît à mypy lui semble poser problème.</p>
<p>En dehors de ça, la vérification est rapide et les diagnostics sont clairs. Sur l’exemple que j’ai donné sur l’annotation, ça donne :</p>
<pre><code>(pyre-env) $ pyre --source-directory . check
ƛ Using virtual environment site-packages in search path...
ƛ Setting up a `.pyre_configuration` with `pyre init` may reduce overhead.
ƛ Found 2 type errors!
linuxfr.py:5:8 Incompatible return type [7]: Expected `bool` but got implicit return value of `None`.
linuxfr.py:16:15 Incompatible parameter type [6]: Expected `int` for 1st positional only parameter to call `print_is_equal` but got `float`.
</code></pre>
<p>Il a trouvé en gros les mêmes erreurs que mypy, sauf qu’il s’est arrêté au premier argument incorrect de la fonction alors que mypy a eu la générosité de signaler que les deux arguments étaient passés en <code>float</code> plutôt que en <code>int</code>. </p>
<p>En conclusion, je pense que c’est un bon projet même si je vais rester avec mypy. La bataille entre mypy et pyre, c’est un peu la bataille entre git et mercurial : mypy et git ont gagné depuis longtemps à ce qu’il me semble, mais pyre et mercurial restent de très bons outils.</p>
<h3 id="toc-monkeytype"><code>monkeytype</code></h3>
<p>Plutôt que d’annoter une base de code à la main, pourquoi ne pas la faire tourner en production et regarder quels types sont effectivement utilisés ? C’est ce que fait <a href="https://github.com/Instagram/MonkeyType"><code>monkeytype</code></a>, il vous aide à collecter les types lors de l’exécution du code, puis à les appliquer sous forme de typage statique sur une base de code. L’outil est maintenu par Instagram et a plus de 3 000 étoiles sur GitHub.</p>
<p>Évidemment, la collecte durant l’exécution du code ralentit énormément le programme (genre 10 fois plus lent). À partir des résultats de la collecte, Monkeytype génère un fichier stub de tous les types qu’il a rencontrés. Ceux-ci ne représentent certainement pas tous les contextes possibles d’exécution du code, il est donc essentiel de faire une relecture pour compléter et vérifier la cohérence du résultat. Cela dit, Monkeytype peut faire gagner pas mal de temps pour démarrer l’annotation d’une base de code.</p>
<h5 id="toc-retour-de-philippe-7">Retour de Philippe</h5>
<p>Je vais tester <code>monkeytype</code> sur le cas très simple du <code>is_equal.py</code> que j’ai présenté en version non annotée. Première, étape, il faut isoler le code à annoter dans un module. Je déplace donc les trois lignes contenant les appels à <code>print_is_equal()</code> dans un fichier run_is_equal.py . Après, l’utilisation est simple : je remplace Python par monkeytype et hop, ça tourne. </p>
<pre><code>> monkeytype run run_is_equal.py
Egalité pour 1
Différence: 1 2
Différence: 0.3 0.30000000000000004
</code></pre>
<p>Je constate apparition d’un fichier <code>monkeytype.sql3</code> dans mon répertoire, c’est bon signe.</p>
<p>Il y a plusieurs façons de générer des annotations. La première, c’est de générer un fichier stub à part. Je lui demande de faire ça pour le module <code>is_equal</code>:</p>
<pre><code class="python"><span class="o">></span><span class="n">monkeytype</span> <span class="n">stub</span> <span class="n">is_equal</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">Optional</span><span class="p">,</span>
<span class="n">Union</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">],</span> <span class="n">b</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">])</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]:</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">print_is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">int</span><span class="p">],</span> <span class="n">b</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span> <span class="o">...</span></code></pre>
<p>Voilà, je peux stocker ce résultat dans un fichier <code>is_equal.pyi</code> et mon code est vérifiable. On note que l’exécution du code a mis en évidence que les fonctions étaient appelées avec entiers ou des flottants, et que <code>is_equal()</code> retourne un booléen ou None. Comme ce n’est pas l’intention de départ, c’est bien de relire les stubs avant de les ajouter aveuglément au code.</p>
<p>On peut aussi demander à <code>monkeytype</code> de modifier directement le code pour ajouter les annotations. C’est mon mode préféré d’utilisation puisqu’on voit bien le diff avec git.</p>
<pre><code class="python"><span class="o">></span><span class="n">monkeytype</span> <span class="nb">apply</span> <span class="n">is_equal</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Union</span>
<span class="k">def</span> <span class="nf">is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">int</span><span class="p">],</span> <span class="n">b</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]:</span>
<span class="sd">'''Compare two integers and return True if they are equal, False if not'''</span>
<span class="k">if</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">True</span>
<span class="k">def</span> <span class="nf">print_is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">],</span> <span class="n">b</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">])</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
<span class="sd">'''Display whether two numeric values are equal'''</span>
<span class="k">if</span> <span class="n">is_equal</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'Egalité pour'</span><span class="p">,</span> <span class="n">a</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'Différence:'</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span></code></pre>
<p>Il a modifié le fichier sur place et m’a aussi montré le code sur la sortie standard.</p>
<p>À noter que si vous avez déjà des annotations, monkeytype ne va pas y toucher, mais vous pouvez lui demander de montrer la différence entre vos annotations et ce qu’il a vu passer.</p>
<p>En conclusion, <code>monkeytype</code> est très pratique pour annoter du code qui a déjà des tests et une utilisation en production. On gagne un temps vraiment important et l’outil est mature. </p>
<h2 id="toc-tests-unitaires">Tests unitaires</h2>
<p>Historiquement, nous avons <a href="https://docs.python.org/dev/library/unittest.html"><strong><code>unittest</code></strong></a>. D’autres alternatives intéressantes : <a href="https://nose.readthedocs.io/en/latest/"><strong><code>nose</code></strong></a> et <a href="https://docs.pytest.org/"><strong><code>pytest</code></strong></a>.</p>
<h3 id="toc-unittest"><code>unittest</code></h3>
<p><code>unittest</code> est la bibliothèque de test unitaire incluse par défaut dans Python. Le fonctionnement est calqué sur les bibliothèques de test du genre <code>junit</code> (première version), basée sur des classes et des fonctions spécifiques d’assertion.</p>
<pre><code class="py"><span class="k">def</span> <span class="nf">incremente</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">class</span> <span class="nc">TestIncremente</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">test_incremente</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">incremente</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="mi">4</span><span class="p">)</span></code></pre>
<p>C’est une très bonne bibliothèque de test, qu’on peut utiliser sur de très gros projets sans souci. Au-delà de ses fonctionnalités classiques, elle a des fonctionnalités méconnues mais bien sympathiques. Par exemple, il est possible de tester le comportement d’une fonction en faisant varier les paramètres. L’approche choisie est « pythonesque » puisqu’elle s’appuie sur les gestionnaires de contexte. Si on complète l’exemple précédent, ça donne :</p>
<pre><code class="py"><span class="k">def</span> <span class="nf">incremente</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">class</span> <span class="nc">TestIncremente</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">test_incremente</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">incremente</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="mi">4</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_many_increments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">for</span> <span class="n">value_in</span><span class="p">,</span> <span class="n">value_out</span> <span class="ow">in</span> <span class="p">[</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
<span class="p">(</span><span class="o">-</span><span class="mi">100</span><span class="p">,</span> <span class="o">-</span><span class="mi">99</span><span class="p">),</span>
<span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">),</span> <span class="c1"># oups, celui-la va échouer</span>
<span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">99</span><span class="p">),</span> <span class="c1"># celui-la va échouer aussi</span>
<span class="p">]:</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">subTest</span><span class="p">(</span><span class="n">value_in</span><span class="o">=</span><span class="n">value_in</span><span class="p">,</span> <span class="n">value_out</span><span class="o">=</span><span class="n">value_out</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">incremente</span><span class="p">(</span><span class="n">value_in</span><span class="p">),</span> <span class="n">value_out</span><span class="p">)</span></code></pre>
<p>Et à l’exécution :</p>
<pre><code class="py"><span class="o">></span> <span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">unittest</span> <span class="n">test_incremente</span><span class="o">.</span><span class="n">py</span>
<span class="o">======================================================================</span>
<span class="n">FAIL</span><span class="p">:</span> <span class="n">test_many_increments</span> <span class="p">(</span><span class="n">test_incremente</span><span class="o">.</span><span class="n">TestIncremente</span><span class="p">)</span> <span class="p">(</span><span class="n">value_in</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="n">value_out</span><span class="o">=-</span><span class="mi">2</span><span class="p">)</span>
<span class="o">----------------------------------------------------------------------</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
<span class="n">File</span> <span class="s2">"test_incremente.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">22</span><span class="p">,</span> <span class="ow">in</span> <span class="n">test_many_increments</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">incremente</span><span class="p">(</span><span class="n">value_in</span><span class="p">),</span> <span class="n">value_out</span><span class="p">)</span>
<span class="ne">AssertionError</span><span class="p">:</span> <span class="mi">0</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">2</span>
<span class="o">======================================================================</span>
<span class="n">FAIL</span><span class="p">:</span> <span class="n">test_many_increments</span> <span class="p">(</span><span class="n">test_incremente</span><span class="o">.</span><span class="n">TestIncremente</span><span class="p">)</span> <span class="p">(</span><span class="n">value_in</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="n">value_out</span><span class="o">=</span><span class="mi">99</span><span class="p">)</span>
<span class="o">----------------------------------------------------------------------</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
<span class="n">File</span> <span class="s2">"test_incremente.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">22</span><span class="p">,</span> <span class="ow">in</span> <span class="n">test_many_increments</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">incremente</span><span class="p">(</span><span class="n">value_in</span><span class="p">),</span> <span class="n">value_out</span><span class="p">)</span>
<span class="ne">AssertionError</span><span class="p">:</span> <span class="mi">101</span> <span class="o">!=</span> <span class="mi">99</span>
<span class="o">----------------------------------------------------------------------</span>
<span class="n">Ran</span> <span class="mi">2</span> <span class="n">tests</span> <span class="ow">in</span> <span class="mf">0.003</span><span class="n">s</span>
<span class="n">FAILED</span> <span class="p">(</span><span class="n">failures</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span></code></pre>
<p>On voit clairement quelles valeurs ont posé problème dans le rapport de test.</p>
<p>Autre aspect appréciable, <code>unittest</code> fait de gros effort pour vous aider à comprendre ce qui diffère lorsque vous comparez deux chaînes de caractères ou deux listes. Par exemple, en complétant le code précédent avec deux tests supplémentaires :</p>
<pre><code class="py"> <span class="k">def</span> <span class="nf">test_list_diff</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span> <span class="c1"># il manque la valeur 2</span>
<span class="k">def</span> <span class="nf">test_str_diff</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="s1">'abcdef'</span><span class="p">,</span> <span class="s1">'abdef'</span><span class="p">)</span> <span class="c1"># il manque le caractère 'c'</span></code></pre>
<p>Le rapport d’exécution :</p>
<pre><code class="py"><span class="o">======================================================================</span>
<span class="n">FAIL</span><span class="p">:</span> <span class="n">test_list_diff</span> <span class="p">(</span><span class="n">test_incremente</span><span class="o">.</span><span class="n">TestIncremente</span><span class="p">)</span>
<span class="o">----------------------------------------------------------------------</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
<span class="n">File</span> <span class="s2">"test_incremente.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">26</span><span class="p">,</span> <span class="ow">in</span> <span class="n">test_list_diff</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">])</span>
<span class="ne">AssertionError</span><span class="p">:</span> <span class="n">Lists</span> <span class="n">differ</span><span class="p">:</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span> <span class="o">!=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
<span class="n">First</span> <span class="n">differing</span> <span class="n">element</span> <span class="mi">1</span><span class="p">:</span>
<span class="mi">2</span>
<span class="mi">3</span>
<span class="n">First</span> <span class="nb">list</span> <span class="n">contains</span> <span class="mi">1</span> <span class="n">additional</span> <span class="n">elements</span><span class="o">.</span>
<span class="n">First</span> <span class="n">extra</span> <span class="n">element</span> <span class="mi">3</span><span class="p">:</span>
<span class="mi">4</span>
<span class="o">-</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
<span class="err">?</span> <span class="o">---</span>
<span class="o">+</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
<span class="o">======================================================================</span>
<span class="n">FAIL</span><span class="p">:</span> <span class="n">test_str_diff</span> <span class="p">(</span><span class="n">test_incremente</span><span class="o">.</span><span class="n">TestIncremente</span><span class="p">)</span>
<span class="o">----------------------------------------------------------------------</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
<span class="n">File</span> <span class="s2">"test_incremente.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">29</span><span class="p">,</span> <span class="ow">in</span> <span class="n">test_str_diff</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="s1">'abcdef'</span><span class="p">,</span> <span class="s1">'abdef'</span><span class="p">)</span>
<span class="ne">AssertionError</span><span class="p">:</span> <span class="s1">'abcdef'</span> <span class="o">!=</span> <span class="s1">'abdef'</span>
<span class="o">-</span> <span class="n">abcdef</span>
<span class="err">?</span> <span class="o">-</span>
<span class="o">+</span> <span class="n">abdef</span></code></pre>
<h3 id="toc-pytest"><code>pytest</code></h3>
<p><code>pytest</code> est un projet plus récent. Il permet de décrire les tests d’une manière plus « pythonesque », sans avoir à implémenter de classe ni à utiliser des méthodes spécifiques d’assertion.</p>
<pre><code class="py"><span class="k">def</span> <span class="nf">incremente</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">def</span> <span class="nf">test_incremente</span><span class="p">():</span>
<span class="k">assert</span> <span class="n">incremente</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">4</span></code></pre>
<p><code>pytest</code> est un outil de test très élaboré. Parmi ses fonctionnalités notables, on peut citer :</p>
<ul>
<li>la découverte automatique des fichiers de test ;</li>
<li>une compatibilité avec <code>unittest</code> et <code>nose</code>, permettant une migration en douceur ;</li>
<li>la possibilité d’affecter un ou plusieurs labels à un test, ce qui permet ensuite de lancer des groupes de test spécifiques assez facilement ;</li>
<li>un système d’initialisation des tests très élaboré, qui permet de partager aisément une ou plusieurs ressources entre plusieurs tests (pratique par exemple pour des opérations de créations de ressources coûteuses, qu’on souhaite partager entre plusieurs tests) ;</li>
<li>les tests paramétrés, c’est-à-dire la possibilité de lancer le même test avec un jeu de valeur ;</li>
<li>une ligne de commande élaborée, qui permet par exemple de lancer uniquement les derniers tests ayant échoué (très pratique en phase de debug) ;</li>
<li>un jeu d’extension via des plugins très pratiques. On en trouve pour tout, par exemple, <code>pytest-cov</code> permet de faire de la couverture de code</li>
</ul>
<p>Vous l’aurez compris, <code>pytest</code>, c’est le niveau au-dessus du test. Si vous rencontrez des limitations avec <code>unittest</code>, c’est le bon candidat à essayer. La documentation est de bonne qualité. Par contre, la mise en œuvre des tests utilise une approche différente de la famille de unittest, qui peut déstabiliser dans un premier temps.</p>
<h3 id="toc-nose"><code>nose</code></h3>
<p><code>nose</code> ajoute à <code>unittest</code> un lanceur et des extensions.</p>
<p>L’outil n’est plus maintenu, mais un projet <code>nose2</code>, fonctionnant avec les versions de Python maintenues fournit quasiment les mêmes services.</p>
<p><a href="https://nose2.readthedocs.io/en/latest/differences.html">https://nose2.readthedocs.io/en/latest/differences.html</a></p>
<h3 id="toc-ward"><code>ward</code></h3>
<p><a href="https://github.com/darrenburns/ward"><code>ward</code></a> reprend beaucoup des concepts de <code>pytest</code> notamment sur l’initialisation des tests (les fixtures) et leur découverte. L’auteur a, d’ailleurs, un plugin pour pytest qui propose une partie du rendu de ward. Ce qui distingue ward, c’est la façon d’écrire un test avec une description plutôt qu’un nom, et un affichage très soigné visuellement : il tire parti des couleurs d’avant-plan et d’arrière-plan, ainsi que de quelques symboles unicode pour faire un rendu riche en détails, en particulier sur l’analyse des différences :</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f757365722d696d616765732e67697468756275736572636f6e74656e742e636f6d2f353734303733312f3132303132353839382d35646661663738302d633162322d313165622d396163642d6239636430666632343131302e706e67/120125898-5dfaf780-c1b2-11eb-9acd-b9cd0ff24110.png" alt="La sortie de ward" title="Source : https://user-images.githubusercontent.com/5740731/120125898-5dfaf780-c1b2-11eb-9acd-b9cd0ff24110.png"></p>
<p>Notre exemple donne avec ward :</p>
<pre><code class="py"><span class="kn">from</span> <span class="nn">ward</span> <span class="kn">import</span> <span class="n">test</span>
<span class="k">def</span> <span class="nf">incremente</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>
<span class="nd">@test</span><span class="p">(</span><span class="s2">"incremente une valeur _positive_"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_</span><span class="p">():</span>
<span class="k">assert</span> <span class="n">incremente</span><span class="p">(</span><span class="mi">33</span><span class="p">)</span> <span class="o">==</span> <span class="mi">34</span></code></pre>
<h3 id="toc-unittestmock"><code>unittest.mock</code></h3>
<p>Pour isoler la partie de code que l’on souhaite tester, il est souvent nécessaire de construire un environnement spécifique. Un véritable environnement fonctionnel, ou des simulateurs de composants, ou encore des « mocks » : des simulateurs au comportement extrêmement simplifié.</p>
<p>La bibliothèque standard <code>unittest.mock</code> permet de créer de tels objets, et de s’en servir pour patcher ou instrumenter son environnement.</p>
<p>Les objets <code>Mock</code>, <code>PropertyMock</code>, <code>NonCallableMock</code> permettent, par combinaison, de construire des objets en leur associant un comportement simple. <code>MagicMock</code> et <code>NonCallableMagicMock</code> vont fonctionner de la même manière, mais leur construction sera construite automatiquement par le code testé.</p>
<p>Il est possible de remplacer en partie un objet, de le faire passer pour l’instance d’une classe particulière (<code>isinstance</code>), de générer une séquence de résultats ou encore de lever une exception.</p>
<p>Après exécution il est possible de vérifier que les appels se sont déroulés comme attendu.</p>
<pre><code class="py"><span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">database</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">value</span> <span class="o">**</span> <span class="mi">2</span>
<span class="k">if</span> <span class="n">database</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">saved</span> <span class="o">=</span> <span class="n">database</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">result</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">saved</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s2">"Database problem"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span>
<span class="kn">from</span> <span class="nn">unittest</span> <span class="kn">import</span> <span class="n">mock</span>
<span class="k">def</span> <span class="nf">test_database_is_called</span><span class="p">():</span>
<span class="n">database</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">NonCallableMock</span><span class="p">()</span>
<span class="n">database</span><span class="o">.</span><span class="n">save</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">database</span><span class="p">)</span>
<span class="c1"># La base de donnée a bien été appelé.</span>
<span class="c1"># Une seule et unique demande de sauvegarde a été faite</span>
<span class="c1"># avec pour argument la donnée d’entrée et le résultat</span>
<span class="n">database</span><span class="o">.</span><span class="n">save</span><span class="o">.</span><span class="n">assert_called_once_with</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">result</span><span class="p">)</span></code></pre>
<p>En sus l’utilitaire <code>create_autospec</code> permet de générer un mock répondant aux spécifications d’un objet particulier. Python 3.7 a également ajouté la fonction <code>seal</code> pour verrouiller les mocks dans un état donné.</p>
<p>Enfin les mocks peuvent être placés temporairement en remplacement de classes et de fonctions par le biais de <code>patchs</code>. Par défaut la fonction construit un <code>MagicMock</code>. Elle peut s’utiliser comme un décorateur ou comme une fonction contextuelle (avec l’opérateur <code>with</code>).</p>
<pre><code class="py"><span class="kn">import</span> <span class="nn">os</span>
<span class="k">def</span> <span class="nf">clean_up_config</span><span class="p">():</span>
<span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s2">"foo/bar"</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s2">"foo/baz"</span><span class="p">)</span>
<span class="kn">from</span> <span class="nn">unittest</span> <span class="kn">import</span> <span class="n">mock</span>
<span class="nd">@mock.patch</span><span class="p">(</span><span class="s2">"os.remove"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_clean_up</span><span class="p">(</span><span class="n">os_remove_mock</span><span class="p">):</span>
<span class="n">clean_up_config</span><span class="p">()</span>
<span class="c1"># Vérifie que la requête de supression des</span>
<span class="c1"># deux fichiers à bien été faite</span>
<span class="n">calls</span> <span class="o">=</span> <span class="p">[</span><span class="n">mock</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="s2">"foo/bar"</span><span class="p">),</span> <span class="n">mock</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="s2">"foo/baz"</span><span class="p">)]</span>
<span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="o">.</span><span class="n">assert_has_calls</span><span class="p">(</span><span class="n">calls</span><span class="p">,</span> <span class="n">any_order</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span></code></pre>
<p>Il existe encore d’autres utilitaires, qui permettent par exemple de patcher temporairement des dictionnaires, ou de simuler la fonction <code>open</code>. <a href="https://docs.python.org/3/library/unittest.mock.html">Voir la documentation complète</a>.</p>
<h3 id="toc-pytest-mock"><code>pytest-mock</code></h3>
<p><code>pytest-mock</code> fournit la même API par le biais d’une fixture pytest. Par ce biais, le contexte temporaire est géré automatiquement, et les retours d’erreurs sont également améliorés (en y nettoyant les traces d’exception induites par l’environnement de test).</p>
<pre><code class="py"><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">unittest</span> <span class="kn">import</span> <span class="n">mock</span>
<span class="k">def</span> <span class="nf">clean_up_config</span><span class="p">():</span>
<span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s2">"foo/bar"</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s2">"foo/baz"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_clean_up</span><span class="p">(</span><span class="n">mocker</span><span class="p">):</span>
<span class="n">mocker</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s2">"os.remove"</span><span class="p">)</span>
<span class="n">clean_up_config</span><span class="p">()</span>
<span class="c1"># Vérifie que la requête de supression des</span>
<span class="c1"># deux fichiers à bien été faite</span>
<span class="n">calls</span> <span class="o">=</span> <span class="p">[</span><span class="n">mock</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="s2">"foo/bar"</span><span class="p">),</span> <span class="n">mock</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="s2">"foo/baz"</span><span class="p">)]</span>
<span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="o">.</span><span class="n">assert_has_calls</span><span class="p">(</span><span class="n">calls</span><span class="p">,</span> <span class="n">any_order</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span></code></pre>
<p>Dans cet exemple de code, l’argument <code>mocker</code> est un appel à une extension de pytest nommée <a href="https://github.com/pytest-dev/pytest-mock/">pytest-mock</a> qui permet de mettre en place le mécanisme de mock pour la durée du test en question.</p>
<h3 id="toc-hypothesis"><code>hypothesis</code></h3>
<p><a href="https://hypothesis.works/">Hypothesis</a> est une implémentation de <a href="https://en.wikipedia.org/wiki/QuickCheck">QuickCheck</a> pour Python (et quelques autres langages).</p>
<p>Cette bibliothèque permet de décrire dans les tests <code>pytest</code> les propriétés qu’une fonction doit avoir. Sa combinatoire en entrée et ses invariants. À l’exécution du test elle génère automatiquement des entrées visant à trouver des effets de bord. Les jeux de test générés sont mis en cache et réutilisés.</p>
<p>C’est donc un complément à des tests standards.</p>
<p>La bibliothèque fournit un ensemble de stratégies pour différents type de données, allant des nombres, aux chaînes de caractères, en passant par les tableaux <code>numpy</code>, ainsi que des outils pour les combiner. Elle permet également de décrire des séquences de changements, que l’on construit à l’aide d’un automate à état, afin de vérifier des systèmes plus complexes.</p>
<p>Voici un exemple permettant de se faire une idée, avec la résolution d’une <a href="https://fr.wikipedia.org/wiki/%C3%89quation_du_second_degr%C3%A9">équation quadratique</a>.</p>
<pre><code class="py"><span class="k">def</span> <span class="nf">solve_poly2</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="sd">"""Résout l'équation ax^2 + bx + c == 0"""</span>
<span class="n">delta</span> <span class="o">=</span> <span class="n">b</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="mf">4.0</span> <span class="o">*</span> <span class="n">a</span> <span class="o">*</span> <span class="n">c</span>
<span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="n">delta</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
<span class="n">results</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="o">-</span><span class="n">b</span> <span class="o">+</span> <span class="n">delta</span><span class="o">**</span><span class="mf">0.5</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="mf">2.0</span> <span class="o">*</span> <span class="n">a</span><span class="p">))</span>
<span class="n">results</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="o">-</span><span class="n">b</span> <span class="o">-</span> <span class="n">delta</span><span class="o">**</span><span class="mf">0.5</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="mf">2.0</span> <span class="o">*</span> <span class="n">a</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">delta</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">results</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="o">-</span><span class="n">b</span> <span class="o">/</span> <span class="p">(</span><span class="mf">2.0</span> <span class="o">*</span> <span class="n">a</span><span class="p">))</span>
<span class="k">return</span> <span class="n">results</span>
<span class="kn">import</span> <span class="nn">pytest</span>
<span class="kn">import</span> <span class="nn">numpy</span>
<span class="kn">from</span> <span class="nn">hypothesis</span> <span class="kn">import</span> <span class="n">given</span>
<span class="kn">from</span> <span class="nn">hypothesis.strategies</span> <span class="kn">import</span> <span class="n">floats</span>
<span class="c1"># Décrit que la fonction prend trois flottants</span>
<span class="nd">@given</span><span class="p">(</span><span class="n">floats</span><span class="p">(),</span> <span class="n">floats</span><span class="p">(),</span> <span class="n">floats</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">test_solve_poly2</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">solve_poly2</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="c1"># Test que les résultats sont conformes</span>
<span class="n">poly</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">poly1d</span><span class="p">([</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">])</span>
<span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
<span class="k">assert</span> <span class="n">pytest</span><span class="o">.</span><span class="n">approx</span><span class="p">(</span><span class="n">poly</span><span class="p">(</span><span class="n">r</span><span class="p">),</span> <span class="mf">0.0</span><span class="p">)</span></code></pre>
<p>Et les résultats ne se font pas attendre.</p>
<pre><code class="shell">Falsifying example: test_solve_poly2<span class="o">(</span><span class="nv">a</span><span class="o">=</span><span class="m">0</span>.0, <span class="nv">b</span><span class="o">=</span><span class="m">1</span>.0, <span class="nv">c</span><span class="o">=</span><span class="m">0</span>.0<span class="o">)</span>
Traceback <span class="o">(</span>most recent call last<span class="o">)</span>:
File <span class="s2">"/home/ordinateur/Workspace/pytest/test_number.py"</span>, line <span class="m">35</span>, in test_solve_poly2
<span class="nv">results</span> <span class="o">=</span> solve_poly2<span class="o">(</span>a, b, c<span class="o">)</span>
File <span class="s2">"/home/ordinateur/Workspace/pytest/test_number.py"</span>, line <span class="m">9</span>, in solve_poly2
results.append<span class="o">((</span>-b + delta**0.5<span class="o">)</span> / <span class="o">(</span><span class="m">2</span>.0 * a<span class="o">))</span>
ZeroDivisionError: float division by zero
Falsifying example: test_solve_poly2<span class="o">(</span><span class="nv">a</span><span class="o">=</span><span class="m">0</span>.0, <span class="nv">b</span><span class="o">=</span><span class="m">0</span>.0, <span class="nv">c</span><span class="o">=</span><span class="m">0</span>.0<span class="o">)</span>
Traceback <span class="o">(</span>most recent call last<span class="o">)</span>:
File <span class="s2">"/home/ordinateur/Workspace/pytest/test_number.py"</span>, line <span class="m">35</span>, in test_solve_poly2
<span class="nv">results</span> <span class="o">=</span> solve_poly2<span class="o">(</span>a, b, c<span class="o">)</span>
File <span class="s2">"/home/ordinateur/Workspace/pytest/test_number.py"</span>, line <span class="m">12</span>, in solve_poly2
results.append<span class="o">(</span>-b / <span class="o">(</span><span class="m">2</span>.0 * a<span class="o">))</span>
ZeroDivisionError: float division by zero</code></pre>
<p>On trouve deux divisions par zéro que nos tests de base auraient pu oublier. </p>
<h3 id="toc-coverage"><code>coverage</code></h3>
<p>Pour vérifier que les jeux de tests ont une utilité, une bonne méthode est de vérifier leur couverture. La bibliothèque <code>coverage</code> permet cela.</p>
<p>Son utilisation se fait en deux étapes. D’une part l’exécution. Par exemple :</p>
<pre><code>coverage run -m pytest test_number.py
</code></pre>
<p>Puis la génération de résultat. Classiquement la couverture en pourcentage de chaque fichier python.</p>
<pre><code>coverage report
</code></pre>
<p>Ou plus utile pour améliorer ses tests, la couverture ligne par ligne. </p>
<pre><code>coverage annotate
more test_number.py,cover
> def solve_poly2(a, b, c):
> """Solve ax^3 + bx^2 + c == 0"""
> delta = b**2 - 4.0 * a * c
> results = []
> if delta > 0:
> results.append((-b + delta**0.5) / (2.0 * a))
> results.append((-b - delta**0.5) / (2.0 * a))
> elif delta == 0:
> results.append(-b / (2.0 * a))
> return results
> def pas_couvert():
! return O + O
</code></pre>
<h3 id="toc-pytest_cov"><code>pytest_cov</code></h3>
<p>Cette extension à <code>pytest</code> ajoute les options de couverture à la commande <code>pytest</code>.</p>
<p>Par exemple, pour générer un rapport du pourcentage de couverture.</p>
<pre><code>pytest test_number.py --cov=.
</code></pre>
<p>Ou pour générer les fichiers de couverture.</p>
<pre><code>pytest test_number.py --cov=. --cov-report annotate
</code></pre>
<h3 id="toc-voir-aussi">Voir aussi</h3>
<p><a href="https://wiki.python.org/moin/PythonTestingToolsTaxonomy">https://wiki.python.org/moin/PythonTestingToolsTaxonomy</a></p>
<h2 id="toc-et-tes-astuces">Et tes astuces ?</h2>
<p>Merci de partager tes recommandations, tes mésaventures, tes bonnes pratiques… :-D</p>
<p>J’ai découvert/appris Python en le pratiquant au bureau à l’arrache, et sans collègue à la fois expert et pédagogue. Du coup, j’ai accumulé plein de mauvaises pratiques que je tente désormais de corriger. Cette dépêche est pour partager mes astuces et faire éviter les mêmes pièges :-)</p>
<p>Je ne suis pas encore un expert Python, alors merci de me corriger gentiment dans les commentaires ;-)</p>
</div><div><a href="https://linuxfr.org/news/python-partie-9-formateur-de-code-analyse-statique.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118047/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/python-partie-9-formateur-de-code-analyse-statique#comments">ouvrir dans le navigateur</a>
</p>
Philippe FOliverYsabeau 🧶bayoYves BourguignonAtem18Gil Cot ✔Benoît SibaudtisaacvaxvmsAnonymegusterhackpatrick_gFrançois GUÉRINhttps://linuxfr.org/nodes/118047/comments.atomtag:linuxfr.org,2005:News/394672021-05-17T08:25:05+02:002021-05-17T08:25:05+02:00Python — partie 6 — Pip et PipxLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Cette dépêche est la suite d’une série sur Python initiée en septembre 2019. Après un sommeil cryogénique de un an et demi, on repart en forme avec d’autres contenus Python à vous proposer: actualité, bonnes pratiques, astuces, témoignages…</p>
<p>Cette sixième partie explique les inconvénients de <code>pip</code> et présente l’alternative <code>pipx</code>, le tout avec plein d’astuces et de conseils pour bien s’en sortir. 🚀 🐍</p>
<p>Pour rappel, les dépêches précédentes :</p>
<ul>
<li>
<a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-1">Python — partie 1</a> parlait de la popularité explosive du langage Python</li>
<li>
<a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-2">Python — partie 2</a> évoquait la fin du support de Python 2</li>
<li>
<a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets">Python — partie 3</a> parlait des différentes façons d’installer Python et des gestionnaires de paquets Python</li>
<li>
<a href="//linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv">Python — partie 4</a> vous présentait <code>py</code> et <code>pyenv</code> pour faciliter la gestion de plusieurs versions de Python en parallèle sur un poste</li>
<li>
<a href="//linuxfr.org/news/python-partie-5-nix-et-guix">Python — partie 5</a> vous faisait découvrir un autre moyen de gérer l’installation en parallèle de différentes versions de Python</li>
</ul>
<p><a href="https://github.com/olibre/GreatPractices/tree/master/python"><img src="//img.linuxfr.org/img/687474703a2f2f6f6c696272652e6769746875622e696f2f4772656174546970732f707974686f6e2f6e6577732f7061727469652d362e706e67/partie-6.png" alt='Le logo de Python est entouré de petites icônes symbolisant la variété des domaines où s’applique Python, et, à droite, un joyeux barbu se tient derrière un écran d’ordinateur qui affiche « partie = 6, "Pip Pipx" \n print(partie) »' title="Source : http://olibre.github.io/GreatTips/python/news/partie-6.png"></a></p>
</div><ul><li>lien nᵒ 1 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-1" hreflang="fr" href="https://linuxfr.org/redirect/108527">Python — partie 1 ― Popularité</a></li><li>lien nᵒ 2 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-2" hreflang="fr" href="https://linuxfr.org/redirect/108528">Python — partie 2 ― Python 2</a></li><li>lien nᵒ 3 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets" hreflang="fr" href="https://linuxfr.org/redirect/108529">Python — partie 3 — Installation de Python et de paquets</a></li><li>lien nᵒ 4 : <a title="https://linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv" hreflang="fr" href="https://linuxfr.org/redirect/108530">Python — partie 4 — Py Pyenv</a></li><li>lien nᵒ 5 : <a title="https://linuxfr.org/news/python-partie-5-nix-et-guix" hreflang="fr" href="https://linuxfr.org/redirect/108531">Python — partie 5 — Nix (et Guix) </a></li><li>lien nᵒ 6 : <a title="https://linuxfr.org/news/python-pour-noel-202x-partie-7-environnements-virtuels" hreflang="fr" href="https://linuxfr.org/redirect/108532">Python — partie 7 — Environnements virtuels</a></li><li>lien nᵒ 7 : <a title="https://linuxfr.org/news/python-partie-8-pipenv" hreflang="fr" href="https://linuxfr.org/redirect/108533">Python— partie 8 — Pipenv</a></li><li>lien nᵒ 8 : <a title="https://linuxfr.org/news/python-partie-10-formateur-de-code-analyse-statique" hreflang="fr" href="https://linuxfr.org/redirect/108534">Python ― partie 9 ― Formateur de code, analyse statique</a></li><li>lien nᵒ 9 : <a title="https://linuxfr.org/news/python-partie-11-entretiens" hreflang="fr" href="https://linuxfr.org/redirect/109668">Python — partie 10 — Entretiens </a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#toc-la-probl%C3%A9matique-de-linstallation-des-paquets-python">La problématique de l’installation des paquets Python</a><ul>
<li><a href="#toc-le-gestionnaire-de-paquets-des-distributions-gnulinux">Le gestionnaire de paquets des distributions GNU/Linux</a></li>
<li><a href="#toc-le-gestionnaire-de-paquets-pip">Le gestionnaire de paquets <code>pip</code></a></li>
</ul>
</li>
<li>
<a href="#toc-la-bonne-fa%C3%A7on-dutiliser-pip">La bonne façon d’utiliser <code>pip</code></a><ul>
<li><a href="#toc-sous-anaconda-ou-miniconda-avec-conda">Sous Anaconda ou Miniconda avec conda</a></li>
</ul>
</li>
<li>
<a href="#toc-mettre-%C3%A0-jour-les-paquets-python">Mettre à jour les paquets Python</a><ul>
<li><a href="#toc-avec-le-gestionnaire-de-paquets">Avec le gestionnaire de paquets</a></li>
<li><a href="#toc-avec-pip">Avec pip</a></li>
<li><a href="#toc-avec-conda">Avec Conda</a></li>
</ul>
</li>
<li><a href="#toc-environnement-python-virtualis%C3%A9">Environnement Python virtualisé</a></li>
<li><a href="#toc-pipx-pour-ne-plus-utiliser-pip"><code>pipx</code> pour ne plus utiliser <code>pip</code></a></li>
<li><a href="#toc-tes-astuces">Tes astuces ?</a></li>
<li><a href="#toc-licence">Licence</a></li>
</ul>
<h2 id="toc-la-problématique-de-linstallation-des-paquets-python">La problématique de l’installation des paquets Python</h2>
<h3 id="toc-le-gestionnaire-de-paquets-des-distributions-gnulinux">Le gestionnaire de paquets des distributions GNU/Linux</h3>
<p>Les distributions GNU/Linux fournissent des applications et bibliothèques Python. Les intégrateurs de ces paquets font très attention à la cohérence et la compatibilité entre les dépendances communes à toutes ces applications. Les dépendances et applications sont fournies, le plus souvent, dans des paquets séparés. De plus, les intégrateurs prennent en compte certains correctifs, notamment celles concernant les failles de sécurité. Nous avons donc des paquets Python compatibles entre eux, sans faille de sécurité, et dont la mise à jour est gérée automatiquement par le système.</p>
<p>Par contre, ces paquets sont bien souvent dans une seule version, et pas forcément dans la version la plus récente. Un autre inconvénient est le nombre limité de paquets Python disponibles via la distribution GNU/Linux.</p>
<p>C’est pour cela, que nous utilisons <code>pip</code> : pouvoir choisir la version exacte d’un paquet Python, et bien souvent la toute dernière version (voire une pré-version <em>(pre-release)</em>) ou pour installer des paquets Python qui ne sont pas fournis par votre distribution.</p>
<h3 id="toc-le-gestionnaire-de-paquets-pip">Le gestionnaire de paquets <code>pip</code>
</h3>
<p>L’outil <code>pip</code> permet d’installer et de gérer des paquets Python directement en liaison avec le <a href="https://pypi.org/">Python Package Index</a>, dépôt qui rend ceux-ci accessibles à tout le monde via l’Internet.</p>
<p>Mais <code>pip</code> n’empêche pas d’installer des dépendances dans des versions incompatibles avec d’autres applications. Et cela nous arrive de temps en temps de <em>casser</em> une application :-( ! </p>
<h2 id="toc-la-bonne-façon-dutiliser-pip">La bonne façon d’utiliser <code>pip</code>
</h2>
<p>Généralement, la documentation dit d’installer avec :</p>
<pre><code>pip install nom_paquet
</code></pre>
<p>Comme on rencontre un problème de permission, on commence à vouloir bien faire avec :</p>
<pre><code>sudo pip install "nom-du-module-python" # en root
</code></pre>
<p>Ce qui rentre en conflit avec :</p>
<pre><code>sudo apt install "autre-nom-du-meme-module-python"
sudo dnf install "autre-nom-du-meme-module-python"
sudo yum install "autre-nom-du-meme-module-python"
</code></pre>
<p>Pour éviter que <code>pip</code> (et <code>pip3</code>) écrase les fichiers installés par <code>apt</code>, sur Debian les deux installations sont séparées :</p>
<ul>
<li>
<code>/usr/lib/python2.7/dist-packages</code> avec <code>apt</code>
</li>
<li>
<code>/usr/local/lib/python2.7/dist-packages</code> avec <code>pip</code> </li>
</ul>
<p>Comme Python est très souvent utilisé par des outils de maintenance ou d’administration des machines, cela peut créer des problèmes : installation de versions de certains paquets directement à la place de ceux du système, ou encore installation de certains paquets pour l’utilisateur qui ont la priorité par rapport aux versions installées par le système. Il est très fortement conseillé d’utiliser des <em>environnements virtuels</em> . Il est fortement déconseillé d’installer un paquet via <code>pip</code> dans le cadre d’une commande <code>sudo</code> (le meilleur moyen de casser votre système).</p>
<p>Le pire, c’est de mettre à jour <code>pip</code> avec lui-même, car <code>pip</code> nous le réclame : <code>pip install --upgrade pip</code> et cela a quelquefois comme conséquence de casser <code>pip</code> ! Installer des modules Python devient un bazar si on ne prend pas les bonnes pratiques dès le début.</p>
<p><a href="https://xkcd.com/1987/"><img src="//img.linuxfr.org/img/68747470733a2f2f696d67732e786b63642e636f6d2f636f6d6963732f707974686f6e5f656e7669726f6e6d656e745f32782e706e67/python_environment_2x.png" alt="Image Xkcd décrivant la complexité de l’installation des modules Python" title="Source : https://imgs.xkcd.com/comics/python_environment_2x.png"></a></p>
<p>Cette problématique est très fréquente comme nous pouvons le constater sur le <a href="https://github.com/pypa/pip/issues/5599">ticket #5599 du projet <code>pip</code></a>.</p>
<p>Bonnes pratiques :</p>
<ol>
<li>Ne jamais utiliser <code>sudo pip</code> car la gestion des paquets (et des modules Python) du système est gérée par <code>apt</code>, <code>yum</code>, <code>dnf</code>…</li>
<li>Utiliser <code>pip install --user</code> pour installer des modules pour son besoin personnel sans écraser les modules Python du système ;</li>
<li>Ne pas utiliser <code>pip install</code> (même avec <code>--user</code>)</li>
</ol>
<p>Les pratiques n°2 et n°3 rentrant en conflit, bien réfléchir avant d’installer un module Python avec <code>pip</code>. Est-ce que le module peut être installé avec <code>apt</code>, <code>yum</code>, <code>dnf</code> ? De quelle version avons-nous besoin ? Et pourquoi pas un environnement Python virtualisé ? Et un conteneur ?…</p>
<p>Bon, revenons à <code>pip</code>, supposons que nous décidions d’appliquer la pratique n°2 et de ne pas écouter la pratique n°3. Nous allons donc installer notre module en précisant <strong>toujours</strong> l’argument <code>--user</code> :</p>
<pre><code class="sh">pip install <span class="s2">"nom-du-module-python"</span> --user</code></pre>
<p>Attention, notre <code>pip</code> utilise peut-être Python 2, voici une meilleure ligne de commande :</p>
<pre><code class="sh">pip3 install <span class="s2">"nom-du-module-python"</span> --user</code></pre>
<p>Attention, nous pourrions avoir plusieurs versions Python 3 installées sur notre machine.<br>
Voici une encore meilleure ligne de commande :</p>
<pre><code class="sh">python3 -m pip install <span class="s2">"nom-du-module-python"</span> --user</code></pre>
<p>Pour la différence entre <code>pip3</code> et <code>python3 -m pip</code> lire <a href="https://stackoverflow.com/q/25749621/938111">les détails sur stackoverflow</a> (en anglais). En bref, on est sûr que <code>python3 -m pip</code> utilise la même arborescence d’installation que le script qui est exécuté avec <code>python3 « nom-du-script.py"</code> (ou <code>#!/usr/bin/env python3</code>). C’est d’autant plus recommandé si le <code>$PATH</code> de la machine est tarabiscoté, s’il y a plusieurs installations de Python sur l’ordinateur ou encore que la machine soit sous Windows.</p>
<p>Et si nous voulions préciser l’installation Python avec laquelle nous souhaitons travailler :</p>
<pre><code class="sh">/chemin/vers/bin/python3 -m pip install <span class="s2">"nom-du-module-python"</span> --user</code></pre>
<p>Attention, si nous avions une ancienne version du module python déjà installée sur notre machine, les commandes <code>pip</code> ci-dessus ne nous permettent pas d’installer la dernière version du module Python :-/</p>
<p>Ajoutons l’argument <code>--upgrade</code> :</p>
<pre><code>python3 -m pip install "nom-du-module-python" --user --upgrade
</code></pre>
<p>Nous pouvons aussi changer la barre de progression :</p>
<pre><code>python3 -m pip install "nom-du-module-python" --user --upgrade --progress-bar emoji
</code></pre>
<p>Et pour utiliser le module Python fraichement installé :</p>
<pre><code class="sh">python3 -m <span class="s2">"nom-du-module-python"</span> <span class="o">[</span>options du module<span class="o">]</span></code></pre>
<p>Oui, cela devient compliqué ! Mais cette façon de faire, avec malheureusement de longues lignes de commande, va nous éviter bien des problèmes.</p>
<p>De plus, même en appliquant les recommandations 2 ou 3, si on installe un paquet pour utilisation avec le Python 3 standard de la machine, ceci peut casser le fonctionnement de certains programmes (le paquet ainsi installé étant prioritaire par rapport à celui du système, s’il y a des incompatibilités… boum). La bonne façon de faire est d'<strong>utiliser des environnements virtuels</strong> (où l’on peut <a href="https://pip.pypa.io/en/stable/quickstart/">utiliser directement <code>pip</code></a> sans risque). C’est une bonne pratique à adopter dès le début afin de s’éviter des problèmes ultérieurs. Cf la suite de la dépêche et la dépêche suivante sur Python où nous vous parlerons plus en détails des environnements virtuels.</p>
<h3 id="toc-sous-anaconda-ou-miniconda-avec-conda">Sous Anaconda ou Miniconda avec conda</h3>
<p>La société Continuum Analytics qui réalise ces distributions Python fournit des paquets déjà compilés pour de nombreuses bibliothèques Python (et autres langages), principalement dans le domaine des sciences et du calcul mais pas uniquement. Certaines de ces bibliothèques ne sont pas toujours triviales à reconstruire, particulièrement sous Windows. L’adoption d’Anaconda peut être une solution si l’installation de certains paquets échoue avec d'autres distributions.</p>
<p>Attention toutefois avec Anaconda ou Miniconda : ils ont tendance à être très gourmands en espace disque (on arrive assez rapidement à des Gio occupés).</p>
<p>Note : Sous Windows un menu d’application <em>Anaconda Prompt</em> est ajouté dans le groupe de programmes <em>Anaconda</em>. Il permet d’accéder à <code>conda</code> même si celui-ci n’est pas accessible dans les répertoires du <code>PATH</code>.</p>
<p>Conda gère un environnement <em>base</em>, celui par défaut correspondant à son installation de Python, mais peut aussi gérer des environnements supplémentaires avec leurs propres installations de Python et des paquets (avec les versions au choix).</p>
<pre><code class="bash">~$ conda activate base
<span class="o">(</span>base<span class="o">)</span> ~$ which python
/home/login/.miniconda3/bin/python
<span class="o">(</span>base<span class="o">)</span> ~$ conda install numpy
…<span class="o">(</span>liste de paquets à installer/mettre à jour<span class="o">)</span>…
Proceed <span class="o">([</span>y<span class="o">]</span>/n<span class="o">)</span>? y
…<span class="o">(</span>téléchargement puis installation<span class="o">)</span>…</code></pre>
<p>Lorsqu’un paquet n’est pas disponible dans les dépôts de conda, on peut l’installer simplement à partir de <a href="https://pypi.org/">PyPI</a> avec <code>pip</code>. Il faut juste prendre soin de lancer pip pour le Python fourni par conda :</p>
<pre><code>(base)~$ conda install osc4py3
…
PackagesNotFoundError: The following packages are not available from current channels:
- osc4py3
…
(base)~$ python -m pip install osc4py3
(base)~$ python
Python 3.6.9 |Anaconda, Inc.| (default, Jul 30 2019, 19:07:31)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import osc4py3
>>>
</code></pre>
<p>À noter : certains IDE comme <a href="https://pyzo.org/">Pyzo</a> donnent directement accès aux commandes <code>conda</code> et <code>pip</code> dans leur fenêtre de shell Python.</p>
<h2 id="toc-mettre-à-jour-les-paquets-python">Mettre à jour les paquets Python</h2>
<h3 id="toc-avec-le-gestionnaire-de-paquets">Avec le gestionnaire de paquets</h3>
<p>Les paquets installés avec les gestionnaires habituels sous Linux (<code>apt</code>, <code>yum</code>, <code>dnf</code>…) peuvent être mis à jours simplement avec ces mêmes gestionnaires. Ceux-ci assurent la cohérence des versions installées (et il vaut mieux ne pas interférer avec ce qu’ils installent). </p>
<p>Voir pour chaque gestionnaire de paquets sa documentation pour faire des mises à jour système.</p>
<h3 id="toc-avec-pip">Avec pip</h3>
<p>On a vu qu’il était possible avec <code>pip</code> de mettre à jour un paquet particulier avec l'option <code>--upgrade</code>:</p>
<pre><code>/chemin/vers/python -m pip install nom_du_paquet --user --upgrade
</code></pre>
<p>Mais il n’y a pas pour le moment de mise à jour automatique simple de l’ensemble des paquets installés avec pip. Celui-ci propose seulement un argument <code>--outdated</code> pour lister les paquets obsolètes. Avec un shell, en l’intégrant dans une expression en ligne de commande, il est toutefois possible de réaliser cette tâche :</p>
<pre><code class="sh">/chemin/vers/python -m pip list --user --outdated --format freeze <span class="p">|</span>
cut -d<span class="o">=</span> -f1 <span class="p">|</span>
xargs --no-run-if-empty /chemin/vers/python -m pip install --user --upgrade --progress-bar emoji</code></pre>
<p>Sous macOS, il faut retirer l’argument <code>--no-run-if-empty</code> de l’exemple ci-dessus.</p>
<p>Notons que de nombreuses applications système sont codées en Python. Mettre à jour les modules et leurs dépendances même avec l’argument <code>--user</code> peut changer le comportement des applications exécutées par l’utilisateur. Par exemple, si nous utilisons <a href="https://doc.ubuntu-fr.org/meld"><code>meld</code></a> en ligne de commande pour comparer deux fichiers, <code>meld</code> pourrait ne plus fonctionner correctement. C’est qu’il fallait respecter la bonne pratique n°3 ! (lire aussi le <a href="//linuxfr.org/users/oliver_h/journaux/quelques-bonnes-pratiques-python-pour-2019#comment-1766432">commentaire de lolop</a>)</p>
<p>Pour l’anecdote, un jour, Oliver décide de mettre à jour ses modules Python dont un module <code>redis</code> qui avait été installé à partir d’un fichier <code>requirements.txt</code> avec une version précise. Sans s’en rendre compte ce module passait de la version 2 à la version 3 avec une <a href="https://pypi.org/project/redis/#upgrading-from-redis-py-2-x-to-3-0">incompatibilité dans l’appel des fonctions</a>. Benoît Sibaud a eu un souci similaire avec <a href="//linuxfr.org/users/oliver_h/journaux/quelques-bonnes-pratiques-python-pour-2019#comment-1766396">pyOpenssl en conflit avec la version fournie par le gestionnaire de paquets de la distrib</a>. C’est à ce moment-là que l’on comprend pourquoi <code>pip</code> ne permet pas de mettre à jour tous les modules d’un coup !</p>
<p>Si quelqu’un s’amuse à mettre à jour tous tes modules avec <code>pip</code>, il serait prudent de prendre quand même des précautions comme enregistrer préalablement les versions des modules avant la mise à jour :</p>
<pre><code>python3 -m pip list --user --format freeze > ~/requirements_avant_pip_upgrade.txt
python3 -m pip list --user --outdated --format freeze |
cut -d= -f1 |
xargs --no-run-if-empty python3 -m pip install --user --upgrade --progress-bar emoji
python3 -m pip list --user --format freeze > ~/requirements_apres_pip_upgrade.txt
</code></pre>
<p>Pour visualiser les changements :</p>
<pre><code>sudo apt install meld
meld ~/requirements_avant_pip_upgrade.txt ~/requirements_apres_pip_upgrade.txt
</code></pre>
<p>En cas de problème, revenir aux versions précédentes :</p>
<pre><code>python3 -m pip install --user -r ~/requirements_avant_pip_upgrade.txt
</code></pre>
<h3 id="toc-avec-conda">Avec Conda</h3>
<p>Pour les paquets fournis via <em>Anaconda</em> ou <em>Miniconda</em>, une simple commande exécutée dans l’environnement conda activé permet d’effectuer les mises à jour <em>dans cet environnement</em> :</p>
<pre><code class="bash"><span class="o">(</span>base<span class="o">)</span> ~$ conda update --all
…</code></pre>
<h2 id="toc-environnement-python-virtualisé">Environnement Python virtualisé</h2>
<p>Alors, utiliser <code>pip</code> est dangereux ? Oui, sauf dans un <strong>environnement Python virtualisé</strong>. \o/</p>
<p>L’idée est d’avoir une installation des paquets Python spécifique pour chacun de ses projets, avec une maîtrise des versions et des dépendances, et qui n’interfère pas avec l’installation standard des paquets par le système. Et le tout versionné avec le code source de son projet. Ainsi nous avons une installation équivalente pour les différents développeurs et la production.</p>
<p>Quelques outils pour créer des environnements virtualisés :</p>
<ul>
<li>
<a href="https://docs.python.org/fr/3.9/library/venv.html"><em><code>python -m venv</code></em></a> fait partie des piles fournies avec Python ;</li>
<li>
<a href="https://github.com/pypa/virtualenv"><em><code>virtualenv</code></em></a> est l’ancêtre de <code>venv</code> mais il continue d’évoluer et il est bien plus utilisé que <code>venv</code> (<a href="https://python-guide-pt-br.readthedocs.io/fr/latest/dev/virtualenvs.html">article en français</a>) ;</li>
<li>
<a href="https://virtualenvwrapper.readthedocs.io/en/latest/"><em><code>virtualenvwrapper</code></em></a> est une série d’extensions pour <code>virtualenv</code> ;</li>
<li>
<a href="https://github.com/pypa/pipenv"><em><code>pipenv</code></em></a> est une surcouche agréable au-dessus de <code>virtualenv</code> et de <code>pip</code> ;</li>
<li>
<a href="https://en.wikipedia.org/wiki/Conda_(package_manager)"><em><code>conda</code></em></a> gère aussi des environnements virtuels, et propose un certain nombre de bibliothèques pré-packagées en supplément à <code>pip</code> ;</li>
<li>
<a href="https://pipxproject.github.io/pipx/"><em><code>pipx</code></em></a> crée un environnement virtuel pour chaque programme Python du système, que l’on souhaite protéger d’un cassage accidentel par mise à jour des dépendances.</li>
</ul>
<h2 id="toc-pipx-pour-ne-plus-utiliser-pip">
<code>pipx</code> pour ne plus utiliser <code>pip</code>
</h2>
<p>Comme nous le disions, malgré tout le soin apporté à la préparation d’un paquet Python par un développeur et au contrôle soigné de ses dépendances, il arrive que nous cassions de façon incontrôlée des dépendances de paquets Python.</p>
<p>Le cas typique est par exemple un paquet fournissant un utilisateur en ligne de commande, disons <code>supertoto</code>, qui dépend du paquet <code>super</code> en version 1. Par la suite, vous installez un autre utilitaire, <code>supertiti</code>, qui dépend aussi du paquet <code>super</code> mais en version 3. Lors de l’installation de <code>supertiti</code>, le paquet <code>super</code> va être mis à jour vers la version 3, qui malheureusement n’est pas rétro-compatible avec la version 1. Vous venez de casser <code>supertoto</code>.</p>
<p>Pour éviter ce type de problème d’interférence entre les différentes dépendances installées avec <code>pip</code>, l’astuce est d’installer chaque application et ses dépendances dans un environnement isolé.</p>
<p>Et voilà, <a href="https://pipxproject.github.io/pipx/"><strong><code>pipx</code></strong></a>, une alternative à <code>pip</code>, mais uniquement pour l’installation d’applications Python. Le gestionnaire de paquets <code>pipx</code> n’a pas été conçu pour l’installation de bibliothèques Python (les dépendances).</p>
<p>L’outil <code>pipx</code> installe chaque application dans un environnement virtuel avec <code>venv</code>. Ainsi, plus de conflit entre les différents paquets applicatifs et leurs dépendances respectives, et donc plus besoin de s’embêter avec les environnements virtuels.</p>
<p>Quant à l’outil <code>pip</code>, il peut être relégué à une seule utilisation : installer <code>pipx</code> (un peu comme, sous Windows, le navigateur Explorer/Edge ayant pour seule utilité de télécharger Firefox). On peut aussi installer <code>pipx</code> avec <code>apt</code>/<code>dnf</code>/<code>nix-env</code>/… Les puristes considèrent même que <code>pip</code> est volontairement dissocié de l’installation de Python pour permettre d’utiliser <code>pipx</code> sans jamais devoir passer par <code>pip</code> !</p>
<p>En fait, d’autres outils se basent sur <code>pip</code> comme <code>pipenv</code>, il vaut donc mieux garder un <code>pip</code> fonctionnel sur son ordi. 😁 Les deux outils <code>pip</code> et <code>pipx</code> cohabitent très bien, chacun avec ses avantages. 👍</p>
<p>Installation avec <code>pip</code> : </p>
<pre><code>/usr/bin/python3 -m pip install --user --upgrade pipx
</code></pre>
<p>Le plus dur est de prendre l’habitude d’utiliser <code>pipx</code> à la place de <code>pip</code>. Allez, on commence par désinstaller les applications préalablement installées avec <code>pip</code> :</p>
<pre><code class="bash">/usr/bin/python3 -m pip uninstall black flake8 pipenv poetry meld</code></pre>
<p>On résinstalle avec <code>pipx</code> :</p>
<pre><code class="bash">$ /usr/bin/python3 -m pipx install black
installed package black <span class="m">19</span>.3b0, Python <span class="m">3</span>.7.4
These apps are now globally available
- black
- blackd
<span class="k">done</span>! ✨ 🌟 ✨</code></pre>
<p>Il se souvient bien sûr que l’installation a déjà été faite :</p>
<pre><code class="bash">$ /usr/bin/python3 -m pipx install black
<span class="s1">'black'</span> already seems to be installed. Not modifying existing installation in <span class="s1">'/home/o/.local/pipx/venvs/black'</span>. Pass <span class="s1">'--force'</span> to force installation.</code></pre>
<p>D’autres utilitaires sympathiques en ligne de commande, à installer avec pipx :</p>
<pre><code class="bash">$ /usr/bin/python3 -m pipx install pipenv
installed package pipenv <span class="m">2018</span>.11.26, Python <span class="m">3</span>.7.4
These apps are now globally available
- pipenv
- pipenv-resolver
<span class="k">done</span>! ✨ 🌟 ✨</code></pre>
<pre><code class="bash">$ /usr/bin/python3 -m pipx install poetry
installed package poetry <span class="m">0</span>.12.17, Python <span class="m">3</span>.7.4
These apps are now globally available
- poetry
<span class="k">done</span>! ✨ 🌟 ✨</code></pre>
<pre><code class="bash">$ /usr/bin/python3 -m pipx install flake8
installed package flake8 <span class="m">3</span>.7.8, Python <span class="m">3</span>.7.4
These apps are now globally available
- flake8
<span class="k">done</span>! ✨ 🌟 ✨</code></pre>
<pre><code class="bash">$ /usr/bin/python3 -m pipx install meld --include-deps
⚠️ File exists at /home/o/.local/bin/futurize and points to /home/o/.local/bin/futurize, not /home/o/.local/pipx/venvs/meld/bin/futurize. Not modifying.
⚠️ File exists at /home/o/.local/bin/pasteurize and points to /home/o/.local/bin/pasteurize, not /home/o/.local/pipx/venvs/meld/bin/pasteurize. Not modifying.
installed package meld <span class="m">0</span>.2.3, Python <span class="m">3</span>.7.4
These apps are now globally available
- f2py
- f2py3
- f2py3.7
<span class="k">done</span>! ✨ 🌟 ✨</code></pre>
<p>Notons l'option <code>--include-deps</code> nécessaire pour installer <code>meld</code>.</p>
<h2 id="toc-tes-astuces">Tes astuces ?</h2>
<p>N’hésite pas à partager tes expériences, tes astuces et tes interrogations.</p>
<p>Et un peu d’aide pour la rédaction de cette série est toujours la bienvenue. Les prochaines dépêches parleront de <code>pipenv</code>, d’empaquetage, voire de tout autre sujet que tu pourrais nous amener pour en faire une dépêche. Viens faire un tour dans <a href="//linuxfr.org/redaction">l’espace de rédaction</a>!</p>
<h2 id="toc-licence">Licence</h2>
<p>Cette dépêche est publiée sous <a href="https://fr.m.wikipedia.org/wiki/Licence_CC0">licence CC0</a> (en domaine public dans les pays où cela est possible) pour permettre de la recopier, modifier, réutiliser et republier sans obligation de citer ses auteurs. Sauf dans certains pays, comme la France, dont la loi oblige quand même à citer les auteurs. Au moins, cela montre l’intention des auteurs à autoriser le plagiat.</p>
</div><div><a href="https://linuxfr.org/news/python-partie-6-pip-et-pipx.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118215/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/python-partie-6-pip-et-pipx#comments">ouvrir dans le navigateur</a>
</p>
Philippe FYsabeau 🧶OlivertisaacAnonymeYves Bourguignonpalm123tedgusterhackyalhttps://linuxfr.org/nodes/118215/comments.atomtag:linuxfr.org,2005:News/404772021-05-14T10:25:05+02:002021-05-14T14:25:05+02:00Sortie de Flask 2.0.0Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Flask est un micro‐<a href="https://fr.wikipedia.org/wiki/Framework">cadriciel</a> Web pour Python publié sous licence BSD. Il est conçu pour permettre une prise en main rapide et facile, tout en offrant la possibilité d'évoluer vers des applications complexes. Il a commencé comme une simple « enveloppe » autour de <a href="https://www.palletsprojects.com/p/werkzeug/">Werkzeug</a> et <a href="https://www.palletsprojects.com/p/jinja/">Jinja</a> et est devenu l'un des cadriciel web Python les plus populaires.</p>
<p>Flask suggère mais n'impose aucune dépendance ou plan de projet. C'est au développeur de choisir les outils et les bibliothèques qu'il souhaite utiliser. Il existe de nombreuses extensions fournies par la communauté qui facilitent l'ajout de nouvelles fonctionnalités.</p>
<p>La version 2 est sortie le 11 mai 2021 soit 3 ans après la <a href="//linuxfr.org/news/sortie-de-flask-1-0">version 1</a>.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://www.palletsprojects.com/p/flask/" hreflang="en" href="https://linuxfr.org/redirect/108498">Site officiel</a></li><li>lien nᵒ 2 : <a title="https://flask.palletsprojects.com/en/2.0.x/changes/#version-2-0-0" hreflang="en" href="https://linuxfr.org/redirect/108499">Les changements de la version 2.0.0</a></li></ul><div><h3 id="toc-les-points-notables">Les points notables</h3>
<p>Flask est maintenant uniquement compatible avec Python 3.6 et plus. Retirer le code de compatibilité pour les versions précédentes de Python a rendu Flask plus rapide et plus facile à maintenir tout en simplifiant les contributions.</p>
<p>Des annotations complètes de type ont été ajoutées au code. Cela rend la vérification des types de votre code plus utile, et permet aux <a href="https://fr.wikipedia.org/wiki/Environnement_de_d%C3%A9veloppement">EDI</a> de fournir de meilleures complétions et aides.</p>
<p>Le projet Flask utilise maintenant des outils comme le pre-commit, <a href="https://black.readthedocs.io/en/stable/">black</a>, <a href="https://flake8.pycqa.org/en/latest/">flake8</a> et <a href="https://github.com/asottile/pyupgrade">pyupgrade</a> pour s'assurer que le code et les nouveaux ajouts de code suivent le même style que l'ensemble du projet.</p>
<p>Prise en charge du mode asynchrone pour les vues et autres rappels tels que les gestionnaires d'erreur, défini avec <code>async def</code>. Les vues en mode synchrone continuent de fonctionner comme avant. Les fonctionnalités <a href="https://asgi.readthedocs.io/en/latest/">ASGI</a> comme les "web sockets" ne sont pas encore prises en charge.</p>
<p>Les modèles de projets peuvent être imbriqués dans d'autres modèles de projets, permettant une approche par couche plus fine pour organiser les applications.</p>
<p>Ajout de décorateurs de chemins pour les méthodes HTTP communes. Par exemple, <code>@app.post("/login")</code> est un raccourci pour <code>@app.route("/login", methods=["POST"])</code>.</p>
<p>En ligne de commande, les erreurs sont plus explicites lorsqu'une application ne peut être chargée. Le serveur de développement fait apparaître les erreurs immédiatement, elles ne sont reportées que lors des rechargements.</p>
<p>Il existe maintenant une fonction <code>Config.from_file</code> qui permet de charger n'importe quel format de fichier de configuration.</p>
<p>La commande <code>flask shell</code> permet maintenant la complétion par tabulation, comme le shell <code>python</code> basique.</p>
<p>Les navigateurs géreront dorénavant le cache des fichiers statiques en fonction du contenu et non plus pendant 12 heures. Cela signifie que des changements sur du contenu statique (comme des styles <a href="https://fr.wikipedia.org/wiki/Feuilles_de_style_en_cascade">CSS</a>) seront visibles immédiatement au rechargement sans nécessiter de vider le cache du navigateur.</p>
<h3 id="toc-démonstration-de-flask">Démonstration de Flask</h3>
<p>Il est nécessaire d'avoir <a href="https://www.python.org/downloads/">Python (3.6 au minimum) d'installé</a> au préalable.</p>
<pre><code class="python"><span class="c1"># Enregistrer ça dans un fichier nommé app.py</span>
<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">escape</span><span class="p">,</span> <span class="n">request</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="nd">@app.route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">hello</span><span class="p">():</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"name"</span><span class="p">,</span> <span class="s2">"les moules"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">f</span><span class="s1">'Bonjour, {escape(name)} !'</span></code></pre>
<h4 id="toc-installation-ou-mise-à-jour-et-exécution">Installation (ou mise à jour) et exécution</h4>
<p>Les commandes précédées d'un <code>$</code> sont à exécuter dans un terminal dans le même dossier que le fichier précédemment enregistré.</p>
<h5 id="toc-installation-ou-mise-à-jour">Installation ou mise à jour</h5>
<pre><code class="shell">$ pip install -U Flask</code></pre>
<h5 id="toc-exécution-de-lexemple-de-code">Exécution de l’exemple de code</h5>
<pre><code class="shell">$ flask run</code></pre>
<h5 id="toc-ouvrir-un-navigateur-à-ladresse-suivante">Ouvrir un navigateur à l'adresse suivante</h5>
<p><a href="http://127.0.0.1:5000/">http://127.0.0.1:5000/</a></p>
<h4 id="toc-arrêter-la-démonstration">Arrêter la démonstration</h4>
<p>Appuyer sur <code>CTRL+c</code> dans le terminal pour arrêter le serveur</p>
<h3 id="toc-renommage-de-la-branche-par-défaut-dans-le-gestionnaire-de-version-de-code">Renommage de la branche par défaut dans le gestionnaire de version de code</h3>
<p>Comme d'autres projets (notamment PSF, CPython, Django mais aussi bien d'autres), le projet Flask a renommé sa branche principale dans <a href="https://github.com/pallets/flask">son dépôt de code</a> de "<em>master</em>" (maître) vers "<em>main</em>" (principal). GitHub simplifie cette transition, voir leur <a href="https://docs.github.com/en/github/administering-a-repository/renaming-a-branch">documentation pour les mainteneurs et les utilisateurs</a>.</p>
<p>Si vous avez une copie locale du dépôt Flask, vous devez renommer votre branche principale de <code>master</code> vers <code>main</code>.</p>
<pre><code class="shell">$ git branch -m master main
$ git fetch origin
$ git branch -u origin/main main
$ git remote set-head origin -a</code></pre>
<p>Si vous faites une installation depuis l'adresse d'une archive GitHub comme <code>https://github.com/pallets/flask/archive/refs/heads/master.zip</code>, vous devrez renommer ce lien pour utiliser "main".</p>
<h3 id="toc-utilisation-de-flask">Utilisation de Flask</h3>
<p>Et vous, vous l'utilisez comment Flask ? Où peut-être lui préférez-vous un autre cadriciel web ? Où peut-être trouvez-vous, vous aussi, ce mot "cadriciel" très moche ? Que vous manque t-il dans Flask pour l'utiliser si ce n'est pas déjà le cas ?</p>
</div><div><a href="https://linuxfr.org/news/sortie-de-flask-2-0-0.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/124253/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/sortie-de-flask-2-0-0#comments">ouvrir dans le navigateur</a>
</p>
dovikBenoît Sibaudhttps://linuxfr.org/nodes/124253/comments.atomtag:linuxfr.org,2005:News/394582021-05-12T12:32:03+02:002021-12-21T18:12:11+01:00Python — partie 10 — EntretiensLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Pour cette dépêche, nous donnons la parole à celles et ceux qui pratiquent le langage de programmation Python : des développeuses et développeurs de différents domaines, mais aussi d’autres métiers comme les scientifiques des données <em>(data scientists)</em>, les scientifiques de l’apprentissage automatique <em>(machine learning)</em>, les analystes quantitatifs <em>(quant)</em>… et bien d’autres…</p>
<p><a href="https://github.com/olibre/GreatPractices/tree/master/python"><img src="//img.linuxfr.org/img/687474703a2f2f6f6c696272652e6769746875622e696f2f4772656174546970732f707974686f6e2f6e6577732f7061727469652d31332e706e67/partie-13.png" alt="Un barbu se tien derrière un écran d'ordinateur qui affiche « partie = 13, "Entretiens" \n print(partie) » et à droite le logo de Python (deux serpents stylisés) entouré de petites icônes symbolisant la variété des domaines où s'applique Python." title="Source : http://olibre.github.io/GreatTips/python/news/partie-13.png"></a></p>
</div><ul><li>lien nᵒ 1 : <a title="https://www.lemonde.fr/pixels/article/2018/07/25/je-n-imaginais-pas-que-python-connaitrait-un-tel-succes_5335917_4408996.html" hreflang="fr" href="https://linuxfr.org/redirect/108408">Entretien avec Guido van Rossum (2018)</a></li><li>lien nᵒ 2 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-1" hreflang="fr" href="https://linuxfr.org/redirect/108491">Python — partie 1 ― Popularité</a></li><li>lien nᵒ 3 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-2" hreflang="fr" href="https://linuxfr.org/redirect/108492">Python — partie 2 ― Python 2</a></li><li>lien nᵒ 4 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets" hreflang="fr" href="https://linuxfr.org/redirect/108493">Python — partie 3 — Installation de Python et de paquets</a></li><li>lien nᵒ 5 : <a title="https://linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv" hreflang="fr" href="https://linuxfr.org/redirect/108494">Python — partie 4 — Py Pyenv </a></li><li>lien nᵒ 6 : <a title="https://linuxfr.org/news/python-partie-5-nix-et-guix" hreflang="fr" href="https://linuxfr.org/redirect/108495">Python — partie 5 — Nix (et Guix)</a></li><li>lien nᵒ 7 : <a title="https://linuxfr.org/news/python-pour-noel-202x-partie-7-environnements-virtuels" hreflang="fr" href="https://linuxfr.org/redirect/108496">Python — partie 7 — Environnements virtuels</a></li><li>lien nᵒ 8 : <a title="https://linuxfr.org/news/python-partie-8-pipenv" hreflang="fr" href="https://linuxfr.org/redirect/108497">Python— partie 8 — Pipenv</a></li><li>lien nᵒ 9 : <a title="https://linuxfr.org/news/python-partie-10-formateur-de-code-analyse-statique" hreflang="fr" href="https://linuxfr.org/redirect/109685">Python ― partie 9 ― Formateur de code, analyse statique</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-entretien-avec-tisaac">Entretien avec <strong>tisaac</strong></a></li>
<li><a href="#toc-entretien-avec-serge-sans-paille">Entretien avec <strong>serge sans paille</strong></a></li>
<li><a href="#toc-entretien-avec-papoteur">Entretien avec <strong>Papoteur</strong></a></li>
<li>
<a href="#toc-entretien-avec-liberforce">Entretien avec </a><a href="//linuxfr.org/users/liberf0rce"><strong>liberforce</strong></a>
</li>
<li>
<a href="#toc-entretien-avec-frague">Entretien avec </a><a href="//linuxfr.org/users/frague"><strong>frague</strong></a>
</li>
<li>
<a href="#toc-entretien-avec-jehan">Entretien avec </a><a href="//linuxfr.org/users/jehan"><strong>Jehan</strong></a>
</li>
<li>
<a href="#toc-entretien-avec-bluebird75">Entretien avec </a><a href="//linuxfr.org/users/blueBird"><strong>BlueBird75</strong></a>
</li>
</ul>
<h2 id="toc-entretien-avec-tisaac">Entretien avec <strong>tisaac</strong>
</h2>
<p><img src="https://img.linuxfr.org/avatars/325/081/000/avatar.png" alt="Avatar de tisaac sur linuxfr"></p>
<ol>
<li>
<p><strong>Une petite présentation de ton parcours ?</strong></p>
<p>J’ai un double background, d’une part d’ingénieur en mathématiques appliquées et d’autre part en économie. Je suis sans doute plus économiste qu’ingénieur.</p>
<p>Dans le cadre de mes études et de mes expériences professionnelles, j’ai été amené à étudier/utiliser une série de langages et de logiciels informatique (Java, C++, matlab, <a href="https://www.dynare.org/">Dynare</a> [qqn connaît Dynare ?], Stata, R, Biogeme). Pour les langages informatiques cela s’est presque exclusivement limité aux études.</p>
<p>Aujourd’hui, on pourrait me définir comme modélisateur des transports avec un accent particulier sur les transports publics. En gros, j’interviens à différentes étapes dans l’établissement et l’exploitation de modèles à 4 étapes ou similaires (voir le <a href="https://fr.wikipedia.org/wiki/Mod%C3%A8le_de_d%C3%A9placements_MODUS">modèle à 4 étapes</a> spécifique mais bien représentatif de ce genre de modèle). Je suis aussi amené à faire des études plus ad-hoc, pudiquement appelée à dire d’expert. Par ailleurs, j’ai dans un poste précédent assumé un rôle de <a href="https://en.wikipedia.org/wiki/Price_analysis"><em>pricing analyst</em></a>.</p>
<p>Dans les deux cas (modélisation transport et pricing analyst), il y a pas mal d’analyses de données nécessaires et à force de rencontrer des Data Scientists, j’en viens à penser que je n’en suis pas très éloigné en tout cas des moins pointus.</p>
</li>
<li>
<p><strong>Comment as-tu commencé à utiliser Python ? Pourquoi ce langage ?</strong></p>
<p>La première fois que j’ai croisé Python c’est durant un truc un peu bizarre, un atelier d’écriture musicale : en gros de la composition par des gens pas forcément musiciens. Les compositions étaient en lien avec le background initial des participants. J’ai donc mis en <em>musique</em> de l’économie. Je ne me souviens plus trop mais en pratique, j’ai utilisé <a href="https://csound.com/">Csound</a> qui faisait intervenir des scripts Python mais en gros cela s’est limité à des boucles for.</p>
<p>Sinon fin de l’année dernière, je suis tombé sur un article de presse au sujet du site de formation en ligne <a href="https://www.datacamp.com/">DataCamp</a> orienté Data Sciences. Je devais un peu m’ennuyer ce soir-là et j’ai testé l’un ou l’autre module et en victime du marketing, j’ai payé pour avoir accès à tous les cours. Une fois que j’ai payé, je me suis dit que je devais rentabiliser.</p>
<p>Par ailleurs, en termes de motivation, à ce moment-là, on parlait pas mal de Data Sciences avec un collègue comme étant un truc que nous devions connaître si on voulait rester un peu employable. Finalement, je savais que j’allais avoir bientôt un projet d’estimation de modèle de choix discret un peu atypique et qui allait m’obliger à réécrire des scripts moi-même et pas seulement compter sur <a href="http://biogeme.epfl.ch/">Biogeme</a>.</p>
<p><em>DataCamp offre des formations en R et en Python, pourquoi avoir privilégié le second ?</em> pourriez-vous me demander. Je vois trois raisons. Premièrement, quand on regarde les offres d’emploi pour Data Scientist, Python me semble beaucoup plus souvent mentionné que R. Deuxièmement, le fait que Python soit un langage informatique n’est pas pour me déplaire dans la mesure où je pense parfois à me remettre à la programmation dans mes temps libres. Troisièmement, nous utilisons <a href="https://www.ptvgroup.com/fr/solutions/produits/ptv-visum/">Visum</a> et <a href="https://www.esrifrance.fr/arcgis.aspx">ArcGis</a> dans lesquels il y a moyen de faire des choses customisées en écrivant des scripts Python.</p>
</li>
<li>
<p><strong>Qu’est-ce que tu as trouvé de facile ? As-tu eu des frustrations ?</strong></p>
<p>Ce qui est impressionnant, c’est la puissance de certains paquets et leur nombre. En termes de frustrations, cela reste des scripts informatiques avec des bugs que tu mets parfois pas mal de temps à comprendre. À certains moments, j’aimerais avoir un meilleur background en informatique parce que je sens bien que sur certains trucs, nous ne sommes pas très efficaces.</p>
<p>Ce qui est compliqué, c’est de comprendre les manières d’écrire de telle sorte que le programme soit le plus rapide possible. Dans certains cas, c’est assez simple à comprendre pourquoi une manière est plus efficace qu’une autre mais dans d’autres situations, c’est vachement moins évident et tu n’as en fait rien à comprendre. Tu dois juste retenir que certaines implémentations/paquets sont plus rapides que d’autres. Cet élément fait que Python a une part quand même pas du tout intuitive.</p>
</li>
<li>
<p><strong>Qu’est-ce que Python t’apporte ? Comment ferais-tu sans ?</strong></p>
<p>Sans Python, j’utiliserais sans doute R et des bases de données SQL.</p>
</li>
<li>
<p><strong>Quelle a été ta progression dans la maîtrise de Python ?</strong></p>
<p>C’est un peu comme la vision que ma mère a de l’anglais. Elle dit que c’est simple et assez rapide d’arriver à communiquer en anglais mais quasi impossible pour un non-natif de maîtriser toutes les subtilités de la langue. Pour sortir un truc qui fonctionne en Python, c’est vraiment pas compliqué par contre pour bien exploiter toutes ses possibilités, c’est vachement plus long. Malheureusement pour moi, je ne passe pas assez de temps à scripter en Python (j’ai plus un rôle de supervision dans l’équipe), donc je suis et je reste plutôt à un niveau assez basique.</p>
</li>
<li>
<p><strong>Tes conseils pour les débutants ?</strong></p>
<p>Apprendre un peu l’esprit de Python. La manière d’écrire de manière pythonique. J’ai l’impression que pour un béotien, cela peut aider à faire des trucs plus propres et lisibles.</p>
<p>Ne pas vouloir réinventer la roue. Il y a plein de paquets qui implémentent énormément de chose. La perte de temps de chercher les paquets pertinents pour ton problème est largement compensée par le fait de ne pas devoir écrire (et déboguer) soi-même le script.</p>
<p>Un peu contradictoire avec le conseil précédent, fais attention à ne pas te perdre dans la myriade de paquets existants. Au départ, il est sans doute bon de se concentrer sur un nombre limité de paquets et apprendre à bien les maîtriser. <a href="https://www.scipy.org/">SciPy</a> est dans mon domaine sans doute une porte d’entrée incontournable.</p>
<p><s>Google</s> Internet est ton ami. Il y a plein de documentations en ligne. Sans doute aussi plein de forum de discussion mais jusqu’à présent mes problèmes ont à chaque fois été résolus avec la documentation.</p>
</li>
</ol>
<h2 id="toc-entretien-avec-serge-sans-paille">Entretien avec <strong>serge sans paille</strong>
</h2>
<p><a href="//linuxfr.org/users/serge_ss_paille"><strong>serge sans paille</strong></a> est un habitué sur LinuxFr.org et également l’auteur du fabuleux logiciel <a href="https://pythran.readthedocs.io/en/latest/">pythran</a> souvent présenté dans nos <a href="//linuxfr.org/users/serge_ss_paille/journaux/pythran-0-9-3-a-une-fedora-sur-la-tete">colonnes</a> qui permet de <a href="https://fr.wikipedia.org/wiki/Compilateur_source_%C3%A0_source">transpiler</a> (convertir) le Python en C++ pour booster les performances.</p>
<ol>
<li>
<p><strong>Une petite présentation de ton parcours ?</strong></p>
<p>Alors au début je suis allé à droite, puis bas, diagonale bas-droite, droite + punch… je m’égare.</p>
<p>Je suis ce qu’on appelle un dev C++, c’était le cas à l’époque et c’est toujours le cas. À l’époque dans ma boîte à outil script, il n’y avait que <code>/bin/sh</code>. </p>
</li>
<li>
<p><strong>Comment as-tu commencé à utiliser Python ? Pourquoi ce langage ?</strong></p>
<p>Le hasard :-) Une personne chère à mon cœur s’était inscrite à une formation, elle n’a pas pu y aller, j’y suis allé à sa place… C’était au début de ma thèse donc… vers 2008.</p>
</li>
<li>
<p><strong>Qu’est-ce que tu as trouvé de facile ? As-tu eu des frustrations ?</strong></p>
<p>Les structures de données de base sont là, le flot de contrôle est simple, la bibliothèque de base est riche… Le typage est implicite, c’est facile et rapide à prendre en main.</p>
<p>L’absence de <strong>possibilité</strong> d’écrire un vérificateur de type qui soit complet est une grosse frustration. Ou plutôt c’est très frustrant de voir les efforts qui sont mis pour essayer de calquer un système de type partiel sur un langage pas prévu pour.</p>
</li>
<li>
<p><strong>Qu’est-ce que Python t’apporte ? Comment ferais-tu sans ?</strong></p>
<p>Un magnifique jouet, il y a beaucoup de subtilité dans ce langage qui parait pourtant simple :-) C’est un chouette sujet d’étude. Au quotidien, pour être productif sur des scripts de petite à moyenne taille, c’est mon choix par défaut. Sans lui… je ferais ça en shell, ce qui étale ma faiblesse linguistique :-/</p>
</li>
<li>
<p><strong>Quelle a été ta progression dans la maîtrise de Python ?</strong></p>
<pre><code>compétence
^
| ____-----
| ____----
| ____-----
| /
| /
|/
+---------------------------------> temps
</code></pre>
<p>Maintenant, à travers mon investissement dans le (magnifique, mais vous le savez déjà) projet <a href="https://github.com/serge-sans-paille/pythran">pythran</a>, j’ai une bonne compréhension de ce qui tourne autour du langage et de ses performances, et de l’interface native. Il m’est arrivé de présenter à PyConFR voire de proposer des patchs mineurs (c’était il y a moins de 18 ans) à CPython.</p>
</li>
<li>
<p><strong>Qu’est ce qui te plaît le plus ?</strong></p>
<p>L’accessibilité de l’implémentation de référence, cpython (le code source reste appréhendable). La communauté pas trop prise de tête. La diversité de l’écosystème. L’accent mis sur l’outillage. La lib standard.</p>
</li>
<li>
<p><strong>Tes conseils pour les débutants ?</strong></p>
<p>Comme dans toute activité : pratiquer, essayer, se faire relire, s’intéresser, discuter. L’informatique peut être une activité très sociale !</p>
</li>
<li>
<p><strong>Que faudrait-il améliorer ?</strong></p>
<p>Il faut savoir s’arrêter, consolider l’existant plutôt que toujours aller de l’avant. Par exemple l’ajout d’un champ <code>type_comment</code> dans de nombreux nœuds de l'<a href="https://fr.wikipedia.org/wiki/Arbre_de_la_syntaxe_abstraite">AST</a> en 3.8, champs mis par défaut à <code>None</code>, à moins de demander explicitement le traitement des informations de type, donne une impression de cul entre deux chaises.</p>
<p>La gestion des dépendances natives, qui sont au cœur du langage (et oui, Python est quand même un langage de glu à la base), mais reléguées au rang de citoyen de seconde zone du système d’empaquetage, est un vrai problème (et un problème complexe hein).</p>
</li>
<li>
<p><strong>Quels sujets souhaiterais-tu lire dans les prochaines dépêches ?</strong></p>
<p>Euhhh… Python et performances ? Python et typage ?</p>
</li>
</ol>
<h2 id="toc-entretien-avec-papoteur">Entretien avec <strong>Papoteur</strong>
</h2>
<p><a href="https://github.com/papoteur-mga"><strong>Papoteur</strong></a> est un important contributeur du projet <a href="https://fr.wikipedia.org/wiki/Mageia">Mageia</a>. Il a développé pour Mageia l’application <a href="https://wiki.mageia.org/en/IsoDumper_:_%C3%A9crire_une_image_ISO_sur_une_clef_USB-fr">IsoDumper</a> qui permet de faire facilement des clés bootables.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f7777772e6d61676569612e6f72672f672f6d656469612f6c6f676f2f6d61676569612d323031332e737667/mageia-2013.svg" alt="Mageia" title="Source : https://www.mageia.org/g/media/logo/mageia-2013.svg"></p>
<ol>
<li>
<p><strong>Comment as-tu connu le monde des ordinateurs, en quelle année ?</strong></p>
<p>Ça devait être un Digital Equipement en 1980, quelque chose comme ça.</p>
</li>
<li>
<p><strong>Quelles études as-tu faites, étaient-ce dans le domaine informatique ?</strong></p>
<p>Des études technologiques, mais pas orientées informatiques. Donc avec un peu de Fortran, IV il me semble.<br>
Autodidacte pour l’essentiel. Avec, à côté, ma femme qui, elle, est diplômée en informatique.</p>
</li>
<li>
<p><strong>Comment as-tu commencé à utiliser Python ? Pourquoi ce langage ?</strong></p>
<p>J’avais utilisé du Basic et des macros Excel en milieu pro. Je n’avais pas l’intention de devenir un pro de la programmation, donc je voulais me concentrer sur un seul langage, qui soit suffisamment souple d’utilisation et universel. C et C++ me semblaient beaucoup moins abordables. PHP spécialisé sur le Web.</p>
<p>Mon premier programme public est un outil d’écriture d’images ISO sur clé USB, à partir d’un fork. Puis quelques autres utilitaires pour Linux/Mageia.</p>
</li>
<li>
<p><strong>Qu’est-ce que tu as trouvé de facile ?</strong></p>
<p>L’indentation qui représente l’imbrication facilite l’écriture (pas de contrainte sur les crochets de fermeture oubliés, décalés), même si la coexistence des espaces et des tabulations est source d’énervement. J’apprécie également les formes itératives et la souplesse d’utilisation des variables sans typage.</p>
</li>
<li>
<p><strong>As-tu eu des frustrations ?</strong></p>
<p>Ce qui m’a le plus contrarié : les formes d’import de mes parties en module selon le contexte de développement ou d’installation.</p>
</li>
<li>
<p><strong>Qu’est-ce que Python t’apporte ? Comment ferais-tu sans ?</strong></p>
<p>Nul ne peut avoir la prétention d’être irremplaçable. J’aurais regardé du côté de Java, C++, PHP pour ce qui est Web.</p>
</li>
<li>
<p><strong>Quel est le truc le plus balaise avec Python ?</strong></p>
<p>Faire un backend qui communique en Dbus avec l’application frontale pour exécuter des actions avec droits root.</p>
</li>
<li>
<p><strong>Est-ce que tu en apprends encore tous les jours ?</strong></p>
<p>Le sujet est tellement vaste qu’il est inépuisable.</p>
</li>
<li>
<p><strong>Qu’est ce qui te plaît le plus ?</strong></p>
<p>En premier, c’est le langage lui-même, complété par un vaste champ de modules complémentaires. Je ne me souviens pas d’avoir sollicité la communauté, mais par contre consulté nombre de ressources pour trouver des exemples. Open source, évidemment, je n’imagine pas utiliser autre chose.</p>
</li>
<li>
<p><strong>Quels sujets souhaiterais-tu lire dans les prochaines dépêches ?</strong> </p>
<p>Le choix et l’utilisation d’un IDE (ou pas), en particulier pour le débogage.</p>
</li>
</ol>
<h2 id="toc-entretien-avec-liberforce">Entretien avec <a href="//linuxfr.org/users/liberf0rce"><strong>liberforce</strong></a>
</h2>
<p><img src="https://img.linuxfr.org/avatars/624/019/000/avatar.png" alt="Avatar de liberforce sur linuxfr"></p>
<ol>
<li>
<p><strong>Une petite présentation de ton parcours ?</strong></p>
<p>Mon cousin a 10 ans de plus que moi, et a fait des études d’électronique et informatique, il a eu un Amstrad puis un Amiga 2000, machine assez fascinante. J’ai eu un Amiga 500 à Noël pour mes 12 ans, et même si je l’ai surtout utilisé pour jouer, cela a été mon premier vrai contact avec un shell. Amigaïste un jour, Amigaïste toujours.</p>
<p>Toujours sur les conseils de mon cousin, j’ai découvert les calculatrices HP48 en classe de première, et fait mes premiers pas en programmation sur le bestiau. Je me suis fadé le manuel raccourci, puis le gros de 500 pages… J’ai encore un émulateur HP48 sur mon smartphone quand il s’agit de faire quelques calculs.</p>
<p>J’ai ensuite fait un DUT génie électrique et informatique industrielle option électronique (toujours sur les traces de mon cousin, passé par là 10 ans avant). Big up à l’IUT de Cachan, ce furent deux années extraordinaires. J’ai ensuite suivi ma propre voie en faisant une école d’ingénieur par apprentissage (pour gagner des sous vs en dépenser pour une école privée).</p>
</li>
<li>
<p><strong>Comment as-tu commencé à utiliser Python ? Pourquoi ce langage ?</strong></p>
<p>Je crois bien que c’est sur les conseils de LinuxFr qui parlait pas mal de ce langage qui montait. J’avais une interface graphique déportée à faire pour le boulot, je m’intéressais à GNOME et GTK+. Le python avait cette réputation de facilité et surtout de lisibilité, on en parlait déjà en bien sur LinuxFr. Alors je me suis naturellement tourné vers pyGTK pour réaliser ce projet. Cela a été mon premier contact concret avec python.</p>
<p>Je tiens à préciser aussi que j’avais (à la même époque ? Quelques années avant ?) gagné un livre sur LinuxFr dans les récompenses aux meilleurs contributeurs, et j’avais choisi un livre sur Python, c’est donc un peu grâce à vous que j’en suis là ;-).</p>
</li>
<li>
<p><strong>Qu’est-ce que tu as trouvé de facile ? As-tu eu des frustrations ?</strong></p>
<p>Pfiou, ça date. 2006 ? 2007 ? Venant du C/C++, je me rappelle avoir été un désarçonné par le rôle de l’indentation dans le langage. Tous mes fichiers de l’époque sont comme mes fichiers C, indentés avec des tabulations ☺. J’avais aussi un peu effleuré perl et, sans vouloir troller, c’était le jour et la nuit tellement le code python me semblait plus lisible.</p>
<p>Dans les frustrations, en plus de l’indentation, je crois que les args et kwargs étaient un peu obscurs au début, surtout à cause de leur notation à base d’étoiles assez différentes du C. </p>
</li>
<li>
<p><strong>Qu’est-ce que Python t’apporte ? Comment ferais-tu sans ?</strong></p>
<p>Déjà, il m’apporte des sous ☺. Je fais du python à temps plein depuis un peu plus de 2 ans. Sans ? Je ferais du Rust :-D ! Prochaine étape sans doute, quand j’aurai l’occasion.</p>
</li>
<li>
<p><strong>Quelle a été ta progression dans la maîtrise de Python ?</strong></p>
<p>Je ne fais pas franchement de truc de haut vol, je reste un (très) modeste développeur loin d’avoir encore appris l’essentiel.</p>
<p>Ma plus grande fierté en python ? Mon premier projet, une interface graphique qui montrait en « temps réel » et de manière très fluide l’état de mon système pour le boulot. J’avais pas mal bossé sur l’amélioration des performances, l’utilisation de cairo, et ça rendait du feu de dieu.</p>
<p>Le truc le plus balaise ? Je vous le dirai quand je l’aurai écrit :-)</p>
<p>Est-ce que j’en apprends tous les jours ? Clairement, développeur c’est un métier sans fin. Et même le python avec sa réputation de facilité a quand même des parties un peu cryptiques. Le typage statique et mypy, les évolutions du langage pour la gestion asynchrone, les subtilités des dataclass… Oui, je pense que j’en apprends encore régulièrement, surtout que le langage change et que je dois avoir en cumulé 3 ans ½ d’expérience.</p>
</li>
<li>
<p><strong>Qu’est ce qui te plaît le plus ?</strong></p>
<p>La philosophie de Python, le Zen qui m’a permis de mieux comprendre certains concepts que j’avais assimilés empiriquement. « Explicit is better than implicit » est certainement ce qui me sert le plus dans la vie de tous les jours, car beaucoup de gens sont trop implicites dans leur communication. J’essaie de me mettre à la place de quelqu’un qui ne connait pas le sujet quand j’explique des choses, pour m’assurer que mon message reste compréhensible.</p>
</li>
<li>
<p><strong>Tes conseils pour les débutants ?</strong></p>
<p>De nos jours il y a plein de ressources pour apprendre le python. J’avais utilisé l’application enki d’enki.com sur Android pendant un moment, c’était pas mal pour apprendre de manière progressive un petit peu chaque jour.</p>
</li>
<li>
<p><strong>Que faudrait-il améliorer ?</strong></p>
<p>Unifier la partie packaging, les solutions pullulant entre les pip-tools, pipenv, poetry et consors.</p>
</li>
<li>
<p><strong>Quels sujets souhaiterais-tu lire dans les prochaines dépêches ?</strong></p>
<p>Le typage et comment bien l’utiliser est un sujet hautement intéressant pour un langage qui a la réputation d’être typé dynamiquement.</p>
<p>Les stratégies d’analyse statique aussi seraient intéressantes (quelle combinaison d’outils).</p>
<p>Enfin, les stratégies de test, mocking, test doubles (doublures de test en VF) avec pytest par exemple.</p>
<p>En gros, tout ce qui permet de faire du code de qualité afin de pouvoir coder sereinement.</p>
</li>
</ol>
<h2 id="toc-entretien-avec-frague">Entretien avec <a href="//linuxfr.org/users/frague"><strong>frague</strong></a>
</h2>
<p><img src="https://img.linuxfr.org/avatars/928/030/000/avatar.png" alt="Avatar de frague sur linuxfr"></p>
<ol>
<li>
<p><strong>Une petite présentation de ton parcours ?</strong></p>
<p>J'ai touché un ordinateur la première fois en classe de 3ême, c'était un <a href="https://fr.wikipedia.org/wiki/Commodore_64">Commodore 64</a>… Çà ne date pas d'hier (83 ?), mais j'ai été mordu.</p>
<p>J'ai alors découvert qu'on pouvait afficher des trucs colorée sur un écran, mais surtout qu'on pouvait demander à l'ordinateur de faire des trucs, via un langage de programmation - du BASIC.</p>
<p>Mon premier ordi perso était un <a href="http://hectorvictor.free.fr/index.php?page=tEHwE7/blZIJ.O">Hector HR-MX</a>, acheté avec la paye de mon premier job d'été. Basic et Forth !</p>
<p>Je n'ai pas fait d'études d'informatique, j'ai travaillé dans le dessin industriel, le meuble… mais l'informatique m'a toujours titillé. J'ai fini par faire une reconversion professionnelle, et je suis maintenant développeur depuis une vingtaine d'année. J'ai fini par valider mon expérience par une Licence Pro Da2I à Lille obtenue en 2004.</p>
<p>J'ai fait un petit peu de C, du VBA, du C#… Je n'ai quasiment fait que du Web.</p>
</li>
<li>
<p><strong>Comment as tu commencé à utiliser Python ? Pourquoi ce langage ?</strong></p>
<p>Vers 2011, j'avais besoin d'un cadriciel pour faire des applis Web. Mon employeur n'avait pas de préférences quant aux outils / langages de développement. Il est à noter que je travaille dans la fonction publique territoriale. </p>
<p>J'avais alors un petit peu touché à Php, mais je n'était pas emballé - c'était à l'époque php 5, avec ses incohérences criantes.</p>
<p>J'étais à la recherche d'un cadriciel pas trop de haut niveau, mais qui proposait un ORM et une interface d'admin "out of the box". Je sortais d'un truc où le seul endroit où je pouvais taper du code, c'était dans les requêtes SQL, dans un client lourd qui flinguait les indentations à chaque fois que tu sauvegardais la page…<br><br>
J'ai fait une tour du marché des cadriciels de l'époque, et je suis tombé sur Django. </p>
</li>
<li>
<p><strong>Qu'est ce que tu as trouvé de facile ? As-tu eu des frustrations ?</strong></p>
<p>Mon premier projet avec Django était un système de calcul de facturation annuelles, basé sur des périodes mensuelles. C'était assez technique, avec des calculs en fenêtres glissantes…</p>
<p>Et bien cà a marche en 3 mois de dev, découvrant en même temps le python (2.7) et Django (1.3). Les deux m'ont séduit !</p>
<p>D'ailleurs je l'ai ré-écrit 1 an plus tard avec l'arrivé des CBV des django 1.4…</p>
<p>Depuis, je continue à faire du dév en python, je fais toujours du Django et un peu de Flask aussi. La richesse de l'écosystème autour de Python me fascine - Il y a toujours quelqu'un qui a fait une lib qui réponds en partie à tes besoins, tu n'a que de la colle à ajouter, et la partie métier, qui est la valeur ajoutée du truc ! </p>
</li>
<li>
<p><strong>Qu'est ce que Python t'apporte ? Comment ferais-tu sans ?</strong></p>
<p>Python est simple à lire et à relire. J'ai des application qui ont été commencé en 2012, et qui tournent et évoluent toujours. Je suis le seul développeur de ma collectivité, je m'occupe de tout "du sol au plafond". J'ai mes utilisateurs à disposition (mes collègues), et ils sont la plupart du temps contents de m'avoir sous la main, que ce soit pour des dévs rapides de 3 / 4 jours (Flask / DB−less) ou pour des projets plus conséquents (Django). </p>
</li>
<li>
<p><strong>Quelle a été ta progression dans la maîtrise de Python ?</strong></p>
<p>Comme expliqué plus haut, j'ai appris Python "sur le tas". Ma plus grande fierté, c'est que mes applis, grandes ou petites, fonctionnent et rendent service ! J'apprends encore des trucs tous les jours…</p>
</li>
<li><p><strong>Qu'est ce qui te plaît le plus ?</strong></p></li>
</ol>
<ul>
<li>Le langage [x]</li>
<li>La diversité des paquets [x]</li>
<li>La communauté et l'entraide [x]</li>
<li>
<p>La gratuité ? L'accès aux sources ? Le partage <em>licence libre</em> [x]</p>
<p>Je coche tout ! J'ai eu l'occasion d'aller à des PyConFR, la qualité et le sympathie des gens rencontrés là-bas était tout bonnement extraordinaire. Et j'apprends tout les jours, ce qui est super !</p>
</li>
</ul>
<ol>
<li>
<p><strong>Tes conseils pour les débutants ?</strong></p>
<p>Plongez ! Il y a plein de tutos sur internet ! </p>
<p>Plus sérieusement, pensez à vos algos, le langage de programmation est une façon de les écrire, pas un fin en soi. Mais, par contre Python apporte une vrai lisibilité et une vraie communicabilité de ces algos, je le recommande toujours chaudement aux jeunes gens qui viennent me parler d'informatique.</p>
<p>Sinon, aussi, utiliser un bon débogueur pas-à-pas permet de comprendre finement ce qui se passe quand un script s'exécute, çà a de vraies vertus pédagogiques. Utilisez-en un - çà permet de démystifier la magie opérante…</p>
</li>
<li><p><strong>Que faudrait il améliorer ?</strong></p></li>
</ol>
<ul>
<li>Packaging / environnement virtuels (c'est la grosse question en ce moment autour de python)</li>
<li>setup.cfg vs. pyproject.toml vs. dot files… C'est carrément la jungle, le dossier racine des projets…</li>
</ul>
<ol>
<li>
<p><strong>Quels sujets souhaiterais tu lire dans les prochaines dépêches ?</strong></p>
<p>Pareil que Liberforce : typing / tests / mocking / qualitay !</p>
</li>
</ol>
<h2 id="toc-entretien-avec-jehan">Entretien avec <a href="//linuxfr.org/users/jehan"><strong>Jehan</strong></a>
</h2>
<p><img src="https://img.linuxfr.org/avatars/621/031/000/avatar.jpg" alt="Avatar de Jehan sur linuxfr"></p>
<ol>
<li>
<p><strong>Une petite présentation de ton parcours ?</strong></p>
<p>Bien que j’ai eu des contacts intermittents avec l’informatique avant, c’est vraiment au lycée qu’un ami me traîne au club informatique régulièrement. C’est aussi vers le milieu du lycée que j’ai eu mon premier ordinateur (Pentium 2?).</p>
<p>Néanmoins même si l’idée du développement me plaît, c’est surtout les jeux vidéos et notamment en réseau que je découvre à ce moment là. Contrairement à beaucoup de développeurs qui ont de la famille ingénieur et ont commencé très tôt, ma famille est dans le milieu artistique et toutes les capacités d’un ordinateur et les possibilités d’interagir avec me passaient un peu à côté.</p>
<p>Pendant une prépa maths, un ami me vante Linux pour la première fois. À cet époque, je le rembarre gentillement, puisque ce système ne m’aurait plus permis de jouer à mes jeux vidéos préférés. Pourtant lorsque je bifurque en licence informatique (directement en troisième année), je découvre que tous les ordinateurs des salles d’informatique de l’université sont sous Linux, je demande donc "Linux" à mon ami : il me fournit un CD d’installation de <a href="https://fr.wikipedia.org/wiki/Mandriva_Linux#Le_nom_et_le_logo">Mandrake</a>. Comme j’ai toujours le même ordinateur depuis le lycée et que le disque dur est plutôt petit, je supprime Windows et installe Mandrake Linux par dessus (pas de dual boot). Me voici donc pour la première fois sur un OS Libre. Plus jamais je n’ai réinstallé Windows pour mon utilisation perso depuis (presque 20 ans). À l’université, j’ai été forcé d’apprendre à maîtriser cet OS et ses logiciels rapidement, sans même avoir les bases (ligne de commande, scripts, organisation des fichiers… tout était nouveau). Même en développement, en troisième année, on nous apprenait Java et Ada95, tout en supposant qu’on savait le C (je n’en avais jamais fait, j’ai dû apprendre sur le tas en lisant du code). À ce jour, je n’ai jamais eu le moindre cours pour ce langage, mais force est de constater que cela importe peu au final! 🙂</p>
<p>Après cela, j’ai progressivement commencé à m’intéresser au concept de logiciel libre qui m’a vite séduit. L’un des premiers projets majeurs auquel j’ai beaucoup contribué et sur lequel on m’a donné des droits d’écriture puis la maintenance fut l’émulateur de terminal <a href="https://gitlab.com/Jehan_ZeMarmot/mrxvt">mrxvt</a> que j’utilisais à l’époque car j’avais besoin d’un terminal rapide sur ma machine peu puissante (les terminaux plus répandus étaient tous super lents). J’ai utilisé ce terminal de nombreuses années, surtout qu’à une époque, j’ai vagabondé et ai travaillé pendant des années sur des mini-machines type <em>EeePC</em> où j’avais vraiment besoin d’un terminal réactif. De nos jours, je n’utilise plus ce terminal dont la maintenance est malheureusement abandonnée.</p>
<p>L’autre gros projet qui m’intéressa fut <a href="https://xmpp.org/">XMPP</a>, au point que j’ai été membre quelques années de la XSF (<em>XMPP Standards Foundation</em>) et qu’on retrouve mon nom dans les sections <em>Acknowledgements</em> des RFCs <a href="https://tools.ietf.org/html/rfc6120#appendix-E">6120</a>, <a href="https://tools.ietf.org/html/rfc6121#appendix-F">6121</a> et <a href="https://tools.ietf.org/html/rfc6122#appendix-D">6122</a> (la première vague de mises-à-jour du protocole vers 2011). J’ai cependant mis de côté mon implication sur ce protocole après de nombreuses années à aider à la standardisation, à la communication (j’avais notamment organisé un <a href="//linuxfr.org/news/anniversaire-decennal-de-jabberxmpp-le-28-fevrier">évènement à la Cité des Sciences pour les 10 ans du protocole</a>; et d’ailleurs si maintenant les habitués de Linuxfr pensent peut-être à moi pour mes commentaires sur GIMP ou le droit d’auteur, il fut un temps où j’étais <a href="//linuxfr.org/news/xmpp-au-printemps-le-grand-rafraichissement">un de ceux</a> qui <a href="//linuxfr.org/news/petit-etat-de-lart-de-quelques-aspects-de-la-messagerie-instantanee">parlaient beaucoup de XMPP</a> ici) et avec du développement. Notamment j’avais développé des plug-ins Wordpress. L’un d’eux permettait de se <a href="https://wordpress.org/plugins/xmpp-auth/">connecter sur son compte Wordpress</a> sans mot de passe, avec un échange de token via XMPP (on recevait une popup sur son client XMPP — potentiellement sur une autre machine — qui nous informait que quelqu’un essayait de se connecter, on pouvait alors accepter ou refuser, et la connexion se faisait automatique sur canal web en fonction du choix sur canal IM). De même ce plug-in permettait de commenter avec validation XMPP (similairement au fait qu’on mette une adresse email pour commenter sur un blog, sauf que le champs XMPP était réellement utilisé pour vérifier que ce n’était pas de l’usurpation d’identité et que l’adresse était utilisée, diminuant ainsi le spam). Personnellement je trouve toujours que c’était une idée cool et j’aurais aimé que ça prenne mais à l’époque les gens n’étaient pas si intéressés par ce type d’usage. Des années plus tard seulement, les concepts d’identification par canal séparé sont devenus classiques (à l’époque, je m’imaginais qu’un jour on pourrait s’identifier sur plein de sites avec son compte XMPP), sauf que ça a divergé par de l’identification GAFAM à la place, comme on le sait 😢. Gros loupé! Enfin bon, toute une époque où j’avais énormément d’idées et de projets sur base de XMPP, mais c’est le passé 🕰️.</p>
<p>Le dernier gros projet pour lequel on me connaît mieux est donc <a href="https://www.gimp.org/">GIMP</a> 🖌️🖼️, sur lequel j’ai commencé à contribuer fin 2012 avec des patchs mineurs, puis majeurs, et de plus en plus… jusqu’à en être devenu mainteneur récemment, grâce au projet <a href="https://film.zemarmot.net/">ZeMarmot</a> pour lequel on fait du <a href="https://liberapay.com/ZeMarmot/">financement participatif</a> depuis quelques années afin d’essayer de vivre de développement sur GIMP!</p>
</li>
<li>
<p><strong>Comment as tu commencé à utiliser Python ? Pourquoi ce langage ?</strong></p>
<p>J’ai commencé à utiliser Python en 2011, je pense. À l’époque, je travaillais pour une startup à Tokyo (en télé-travail depuis la Corée du Sud) dont la plateforme web utilisait PHP. Comme Python était le langage à la mode, certains ont poussé pour un port du site vers Python (sans bonne raison d’après moi, hormis que le PHP était pas "hype" alors que Python était à la mode), puis le projet a été tué par les managers après 6 mois… 6 mois de travail perdu! C’est à ce moment là que j’ai vraiment appris le Python.</p>
<p>Personnellement je suis technologie-agnostique, c’est à dire que je me fiche un peu des chipoteries sur les langages ou autres guerres de chapelle. J’aime bien Python, c’est un fait. Mais j’ai aussi bien aimé Objective Caml (même si je l’ai trouvé un peu lourd d’usage sur des projets plus poussés), de même que Ada95 (un des langages qui m’a le plus épaté et j’adorais sa rigueur; malheureusement je n’ai pas eu l’occasion d’énormément l’utiliser par la suite)… Même PHP, j’aimais bien (le langage a certes ses petites incohérences et on sent bien qu’il est né de façon un peu bordélique, mais il n’en reste pas moins agréable et j’ai fait des trucs très cool avec. J’avais même écrit un parseur XMPP entièrement en PHP, simpliste mais entièrement fonctionnel, rapidement et en très peu de code, pour mes plug-ins Wordpress/XMPP; ça marchait très bien! 🙂). De nos jours, j’utilise majoritairement du C (comme on peut se l’imaginer pour GIMP), et pas mal de Python sur des projets à côté, ou dès que je veux faire des petits scripts rapides et que <code>bash</code> atteint quelques limites (trop gros scripts <code>bash</code> qui deviennent alors difficiles à lire et maintenir).</p>
<p>En gros, ma logique lorsque je veux automatiser un processus pour moi (ma raison principale de programmer), c’est d’essayer d’abord de faire un script shell POSIX, puis je passe en bash lorsque ça commence à être compliqué de faire du généraliste, et enfin en Python lorsque le code bash devient trop complexe et peu lisible. C’est souvent mon processus de choix de langage pour tout ce que j’écris de zéro pour mes besoins personnels.</p>
</li>
<li>
<p><strong>Qu’est ce que tu as trouvé de facile ? As-tu eu des frustrations ?</strong></p>
<p>J’aime bien l’indentation significative de Python, ce qui permet de forcer au code propre (dans un projet comme GIMP en C, on est forcé de beaucoup re-demander aux nouveaux contributeurs de bien suivre nos règles de style de code, car la qualité tout comme le style du code sont très importants pour du code maintenable; dans un projet en Python, c’est un peu moins nécessaire d’imposer certaines règles puisqu’elles viennent avec la syntaxe du langage, même si ce n’est pas entièrement vrai et il reste des choix de style à faire).</p>
<p>J’aime aussi bien ses capacités d’introspection, ce qui permet très facilement de créer du code modulable et auto-augmentable, capacités que j’ai utilisé sur plusieurs projets personnels comme professionnels.</p>
<p>J’étais moins fan de fonctionnalités plus avancés de Python, car comme beaucoup de fonctionnalités avancées dans la plupart des langages, elles rendent le code plus dur à lire. Parfois quand je lis du code d’autrui qui aime utiliser les fonctionnalités les plus avancées, j’ai l’impression que certains utilisent ces syntaxes juste parce qu’elles permettent de faire du code plus petit (mais pas plus compréhensible pour autant) ou de montrer sa grande connaissance du langage, et ce aux dépends de la lisibilité et de la maintenabilité. Pour moi, c’est un problème. Mais avec le temps, je suis peut-être moi-même devenu un de ceux qui utilisent des syntaxes avancées un peu absconses au final. Je suis pas sûr. Aussi peut-être que je suis juste paresseux!</p>
<p>Sinon j’aime beaucoup le typage fort (c’est pour que j’adore le langage Ada), même si parfois ça peut aussi rendre certains types de code problématique (notamment les conversions entre type — ce n’est pas forcément un problème conceptuel d’architecture de code ! — peuvent être parfois laborieuses dans certains langages à typage fort). Cela peut rendre un code bien plus robuste et aider à détecter certains types de bugs rapidement, à un niveau plus conceptuel. Donc on peut se demander pourquoi utiliser Python (typiquement le type de langage de script qui cache plein de types d’erreurs de par son typage faible). Néanmoins je ne suis pas sûr qu’il faille changer Python dans un tel sens pour autant. Ce qui fait sa force est aussi sa simplicité pour du code rapide à écrire. Chacun son truc…</p>
</li>
<li>
<p><strong>Qu’est ce que Python t’apporte ? Comment ferais-tu sans ?</strong></p>
<p>Ça m’apporte juste le fait que ça existe et que je sais l’écrire, donc je m’embête pas. J’utilise. En fait, autant je suis technologie-agnostique, autant je ne suis pas intéressé par apprendre tous les langages informatiques possibles ou bien toutes les fonctionnalités les plus avancées de ces langages. Juste apprendre pour apprendre (attention, c’est très bien d’apprendre pour la beauté du geste, mais au bout d’un moment, on fatigue; perso je préfère apprendre pour arriver à un résultat et parce que ça m’est utile) ne m’intéresse plus autant. Être agnostique signifie juste que je m’en fiche un peu dans quel langage est écrit un logiciel auquel je veux contribuer. Je m’adapte. Bon, bien sûr, si c’est un langage que je connais bien, c’est plus simple, donc je peux me lancer plus facilement, sinon je fais avec, et je lis juste le code lentement pour comprendre ce qu’il fait et comment ce langage fonctionne (on découvre rapidement que ce n’est pas si compliqué).</p>
<p>Par contre, pour mes projets, je me simplifie la vie et prend ce que je connais bien, donc Python. C’est tout! 😛</p>
<p>Et donc sans? J’utiliserais un autre langage, c’est tout!</p>
</li>
<li>
<p><strong>Quelle a été ta progression dans la maîtrise de Python ?</strong></p>
<p>Rapide au début, puis assez stable pendant des années, je dirais. Je cherche pas forcément à faire les trucs les plus compliqués, comme je disais plus haut, car je privilégie un code lisible et simple. J’apprends bien sûr encore de temps en temps des trucs nouveaux, mais pas tant que cela, et je ne cherche pas particulièrement.</p>
<p>Mon projet le plus important avec Python est probablement <a href="https://pypi.org/project/crossroad/">Crossroad</a>, mon outil d’aide à la cross-compilation.</p>
</li>
<li>
<p><strong>Qu’est ce qui te plaît le plus ?</strong></p>
<p>L’API standard est plutôt bien remplie et sa documentation est bien faite. D’ailleurs j’ai un mot clé pour la doc Python qui m’amène automatiquement sur un module dont je veux l’information. Par exemple, si je tape "<em>py3 time</em>" dans ma barre d’URL de Firefox, ça m’emmène à la documentation du module <code>time</code> pour Python 3 (j’ai aussi un mot clé <code>py2</code> mais comme on peut se l’imaginer, je n’ai plus énormément de raisons de l’utiliser).</p>
<p>J’aime bien aussi le dépôt communautaire <a href="https://pypi.org/">Pypi</a> car on y trouve énormément de modules supplémentaires utiles, même si je sais qu’il y a une bonne dose de divergences d’opinion à son sujet, notamment à cause des risques de tels dépôts moins contrôlés. Ces critiques sont d’ailleurs fondées: il ne faut pas installer n’importe quoi. Néanmoins cela n’enlève pas l’utilité du dépôt si on fait attention et si on est conscient des problèmes potentiels. Je fais toujours des recherches préalables à l’installation d’un paquet pip, notamment sur les auteurs, la licence bien sûr, et quelques vérifications du code.</p>
<p>Ensuite j’aime beaucoup le shell interactif <code>ipython</code>, super utile pour tester rapidement du code. C’est le type d’outil qui apporte énormément au confort de développement.</p>
</li>
<li>
<p><strong>Tes conseils pour les débutants ?</strong></p>
<p>Mes conseils pour les débutants ne dépendent pas du langage: corrigez/contribuez à ce qui vous intéresse. Si vous rencontrez un bug, ou si vous avez besoin d’une fonctionnalité, regardez le code et contribuez un patch. C’est le meilleur moyen d’apprendre et de progresser.</p>
<p>C’est bien simple: les débutants qui viennent nous voir et nous demandent « <em>qu’est-ce que je peux faire si je veux apprendre/contribuer ?</em> », même si on les redirige vers des bugs simples, on ne les revoit quasi jamais. Par contre ceux qui ont rencontré un problème et veulent juste le corriger, ou ont un besoin et veulent le remplir, ceux-ci n’ont pas besoin de demander. Et même s’ils sont débutants, ils progressent toujours le plus vite. Typiquement essayez de développer <strong>pour vous</strong>, pour vos propres besoins, parce que vous en avez envie. Pas « pour apprendre » ou « pour remplir votre CV » ou « parce que ça fait bien » ou « parce qu’on vous a dit que c’est ce qu’il faut faire », ce qui serait la recette pour un échec sûr. Faire un code qui vous est réellement utile et qui ensuite vous sert au quotidien est tellement plus gratifiant donc ça se fait tout seul ! 😃</p>
<p>Personnellement tous mes développements personnels depuis les ~15 ans que je développe sont juste des choses qui me tenaient à cœur ou des problèmes ou manques que je rencontrais, tout simplement (en plus des contributions sur GIMP et associés, j’ai des petits patchs épars sur des projets aussi divers que Firefox, Blender, mplayer puis mpv, Entangle, Linux Stopmotion, et des dizaines d’autres, notamment des bibliothèques utilisées partout; à chaque fois pour des besoins persos). C’est le meilleur moyen d’apprendre. C’est donc un conseil générique, sans rapport à Python, parce que c’est ce qui a marché pour moi et comme ça que j’ai toujours agi de manière naturelle (sans jamais me forcer à « apprendre coûte que coûte »).</p>
</li>
<li>
<p><strong>Que faudrait-il améliorer ?</strong></p>
<p>Je ne sais pas trop. C’est pas que Python est parfait, mais je suis plus du genre à m’adapter et à accepter un langage tel qu’il est (tant qu’il n’a pas d’énormes erreurs de conceptions ou de design, bien entendu, mais ce n’est pas le cas de Python).</p>
</li>
<li>
<p><strong>Quels sujets souhaiterais-tu lire dans les prochaines dépêches ?</strong></p>
<p>Autour de Python, vous voulez dire ? Peu importe, juste des trucs divers intéressants, non? J’apprécie lire les diverses dépêches sur un peu tout et je n’ai pas d’attente en particulier. Ce serait un peu triste si je ne lisais ou ne voulais lire que les trucs que je connais ou qui m’intéressent! Je préfère lire sur des trucs que je connais pas, voire dont je n’ai même pas idée, et donc qui pourraient m’intéresser (mais pour ça faut déjà savoir que ça existe).</p>
<p>Donc étonnez-moi ! Je connais pas tout et suis ouvert à tout. 🤗</p>
</li>
</ol>
<h2 id="toc-entretien-avec-bluebird75">Entretien avec <a href="//linuxfr.org/users/blueBird"><strong>BlueBird75</strong></a>
</h2>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f617661746172732e67697468756275736572636f6e74656e742e636f6d2f752f3231363635333f763d34/216653?v=4" alt="Avatar de BlueBird sur GitHub" title="Source : https://avatars.githubusercontent.com/u/216653?v=4"></p>
<ol>
<li>
<p><strong>Une petite présentation de ton parcours ?</strong></p>
<p>J’ai découvert les ordinateurs vers la 5e, en 1987, avec un MSX et son langage de programmation intégré : Microsoft Basic. Le fait de pouvoir donner des instructions à un ordinateur et de le voir les exécuter m’a fasciné. Et me fascine encore.</p>
<p>Au Lycée, je suis passé au PC et à <em>Borland Turbo Pascal</em>. En Math Sup, j’ai appris le C et Linux (70 disquettes pour installer une Slackware). Je suis ensuite arrivé dans une école d’ingénieur où les geeks étaient tous fondus de Unix. Si tu parlais pas <code>shell</code> et <code>sed</code> couramment, tu pouvais difficilement intervenir sur le forum de discussion. Ils programmaient presque tous en <em>Perl</em> avec <em>Emacs</em>. J’ai essayé les deux mais ça ne correspondait pas à ma vision de l’informatique : Perl était bien trop enclin à laisser passer des erreurs (langage Write-Only) et Emacs m’a rebuté. J’ai persisté en C et j’ai découvert <em>Vim</em> à la place.</p>
<p>J’ai eu mon diplôme d’ingénieur en télécommunications, mais j’ai rapidement laissé tomber tout ce que j’avais appris en cours pour ne faire que ce que je faisais déjà avant : du développement logiciel ! C’est mon métier et ma passion.</p>
</li>
<li>
<p><strong>Comment as-tu commencé à utiliser Python ? Pourquoi ce langage ?</strong></p>
<p>Vers les années 2000, Perl était au sommet de sa gloire mais un petit langage commençait à pousser : Python avec sa version 1.5.2 . Puisque Perl m’avait déçu, j’ai voulu voir ce que donnait la concurrence. J’ai immédiatement adoré ce langage qui capturait la bonne manière de programmer : le code est extrêmement lisible, le langage essaie d’être intelligent et quand il doute, il te propose une belle erreur bien claire (par rapport au C++ ou bash que j’utilisais à l’époque par exemple). Et surtout, on est productif rapidement : les structures clés (dictionnaires, listes, tuple, string) sont disponibles immédiatement et de façon très pratiques. Contrairement au C++ où la syntaxe est extrêmement lourde pour obtenir un résultat moins confortable.</p>
<p>Dans mon travail ou pour le logiciel libre, j’ai donc commencé à utiliser discrètement Python.</p>
</li>
<li>
<p><strong>Qu’est ce que tu as trouvé de facile ? As-tu eu des frustrations ?</strong></p>
<p>Le langage est bien conçu pour le programmeur. A l’époque, n’importe quel développeur C/C++/Java pouvait lire du code Python et le comprendre. Il y a une concision et une clarté qui à mon avis, a permis au langage de décoller. On a un peu perdu cet aspect lisibilité aujourd’hui avec l’ajout de constructions plus complexes dans le langage.</p>
<p>J’ai longtemps été frustré par l’absence de moyen de distribuer facilement du code Python, puis setuptools et pypi.org sont arrivés. Ensuite, programmant sous Windows, j’ai été frustré par la difficulté de livrer une application écrite en Python. Mais py2exe est arrivé et j’ai pu faire des applications pour le bureau de qualité professionnelle, s’intégrant très bien à Windows, sans que personne ne soupçonne que j’utilisais un langage dit de script. J’ai aussi souffert un moment du manque de bibliothèque graphique de qualité, tkinter donnant un look trop passéiste à mon goût aux applications. Puis Phil Thompson a sorti l’excellent binding PyQt et je n’ai plus jamais ralé.</p>
<p>Ces dernières années, j’ai commencé à être frustré par le typage dynamique de Python : dans des grosses applications, on perd vraiment pas mal en compréhension du code et fiabilité. Mais Guido est arrivé avec les annotations de type et mypy pour la vérification et ma vie est redevenue rose.</p>
<p>Avec Python, je suis un développeur comblé. Il rattrape ses faiblesses au fur à mesure des années.</p>
</li>
<li>
<p><strong>Qu’est ce que Python t’apporte ? Comment ferais-tu sans ?</strong></p>
<p>Mon travail, c’est du développement en C et assembleur pour la carte à puce. Python intervient en tant qu’outil, d’une part pour écrire des tests élaborés, d’autre part, pour résoudre rapidement des problématiques d’automatisation spécifique à notre environnement.</p>
<p>Sans Python, je ferai comme la plupart de mes collègues : j’automatiserai beaucoup moins de tâches et je galèrerai beaucoup plus. Même si en théorie, on pourrait remplacer notre usage de Python par du Visual Basic ou autre, dans les faits, seul les gens qui font du Python semble passer du temps en automatisation. Cela confirme de mon point de vue sa “rentabilité” en terme de temps de développement.</p>
</li>
<li>
<p><strong>Quelle a été ta progression dans la maîtrise de Python ?</strong></p>
<p>J’aime bien aller au fond des choses, donc assez vite, j’ai acheté un bouquin de référence (merci O'Reilly) pour tout connaitre du langage. C’était plus facile il y a 20 ans, il y avait quand même beaucoup moins de subtilités. J’ai ensuite suivi toutes les évolutions, il n’y a guère que l’API C que je ne suis pas allé voir.</p>
<p>Dans un boulot précédent, l’utilisation de Python couplé à une politique de test intelligente a permis de réduire le temps de test d’un boitier électronique de trois semaines à deux heures. Plus personnes n’a questionné l’utilisation de Python dans mon équipe plutôt que de C#. </p>
<p>Ma plus grande fierté est à venir pour bientôt : je suis en train de pousser mon entreprise à mettre en Open Source un logiciel en Python que j’ai écrit. Une vraie révolution culturelle !</p>
</li>
<li>
<p><strong>Qu’est ce qui te plaît le plus ?</strong></p>
<p>La lisibilité du code et la facilité à faire des manipulations un peu complexes en quelques lignes. Ça m’arrive souvent de faire des mini-parseur pour des formats textes dans le cadre de tâches d’automatisation. Très vite, je me retrouve avec des listes de dictionnaires de listes de chaînes de caractères et je suis heureux que les structures de bases du langage me permettent ça facilement.</p>
<p>Sur le long terme, j’apprécie la solidité de la communauté Python et le travail de fond qu’a fait Guido Van Rossum pour la construire. Hormis quelques cas où Guido a utilisé son pouvoir de « Dictateur bienveillant », le langage avance avant tout avec un effort communautaire d’entraide. Une licence libre est bien sur essentielle pour ce travail, mais loin d’être suffisant. Faire prospérer une communauté demande un effort important. Par exemple, récemment, un core-développeur du langage a été exclu en raison de sa non coopération et de son agressivité envers ses pairs. C’est un choix difficile à faire, mais essentiel pour garder une communauté saine.</p>
<p>J’apprécie aussi beaucoup les efforts de ces dernières années de la communauté Python pour être plus inclusif, avec un choix délibéré d’avoir<br>
une représentation géographique plus diverses et des initiatives pour faciliter l’accueil de ceux qui ne sont pas des informaticiens masculins blancs.</p>
</li>
<li>
<p><strong>Tes conseils pour les débutants ?</strong></p>
<p>Je ne suis pas trop en contact avec des débutants, mon public, c’est plutôt les programmeurs intermédiaires ou experts. Mon conseil principal, ne jamais hésiter à refactoriser : renommer pour les rendre le code plus parlant, unifier des traitements, déplacer des traitements dans des fonctions. Tout cela pour un ultime but : rendre du code plus lisible et compréhensible. Une ligne de code va être écrite une fois mais lue des milliers de fois !</p>
<p>Pour du code Python, si le code que vous écrivez fait plus de deux cents lignes, ou est utilisé dans une équipe, ou a une durée de vie de plus d’un mois, ajoutez des annotations de type. Cela le rendra dix fois plus compréhensible. C’est encore mieux si vous vérifier effectivement vos types avec <a href="https://mypy.readthedocs.io/en/stable/">mypy</a> mais rien que le fait de documenter les types rajoute un gros plus en termes de clarté.</p>
<p>Si vous vous lancez dans la vérification de type avec mypy, commencez modeste. Le démarrage est assez chronophage. Et certains cas complexes demandent de lire pas mal de doc pour pouvoir être décrit avec des types précis. Par contre, le gain est vraiment appréciable !</p>
<p>Pour ceux que ça intéresse, j’ai eu la chance d’en parler <a href="https://www.youtube.com/watch?v=URP2e7hEUFw&list=PLzjFI0G5nSsry3cm_k1tPOi9SRaAXsZAt&index=7">en conférence à PyParis 2018</a>.</p>
</li>
<li>
<p><strong>Que faudrait il améliorer ?</strong></p>
<p>Il n’y pas assez de développeurs payés pour travailler sur Python lui-même. Victor Stinner parlait d’un développeur et demi au total payé pour travailler dessus alors que le langage est téléchargé des millions de fois par mois ! On sent bien que les exigences d’un projet de la taille de Python demanderaient plus de temps rémunéré ; ça met une grosse pression sur les volontaires (dont plusieurs ont fait des burnouts). Si votre entreprise dépend de Python, faites des dons à la PSF, cela améliorera la situation.</p>
<p>La gestion de paquets Python va aussi bientôt rencontrer un gros écueil sur la gestion des dépendances. Le nombre de paquets et de dépendances augmente rapidement, et on va tomber de plus en plus souvent sur le cas où une application dépend de deux packages A et B, qui, chacun, dépendent d’une version différente du package C. Des évolutions récentes sur pip permettent de mieux gérer le cas où il existe une version du package C qui résoud toutes les contraintes. Mais à terme, je pense qu’il faudra trouver une solution pour que les package A et B puissent chacun utiliser la version du package C qui leur convient. Ça va pas être trivial !</p>
</li>
<li>
<p><strong>Quels sujets souhaiterais-tu lire dans les prochaines dépêches ?</strong></p>
<p>J’aime beaucoup les retours d’expérience professionnelle. Surtout si c’est atypique !</p>
</li>
</ol>
</div><div><a href="https://linuxfr.org/news/python-partie-10-entretiens.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118174/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/python-partie-10-entretiens#comments">ouvrir dans le navigateur</a>
</p>
tisaacOliverliberforceYsabeau 🧶François GUÉRINserge_sans_paillePhilippe FJehanBenoît SibaudMaderiosE3Ms6vyXgusterhackdovikhttps://linuxfr.org/nodes/118174/comments.atomtag:linuxfr.org,2005:News/394662021-05-11T11:40:41+02:002021-05-12T09:14:26+02:00Python — partie 8 — PipenvLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Cette dépêche est la suite d’une série sur Python initiée en septembre 2019. Après un sommeil cryogénique de un an et demi, on repart en forme avec d’autre contenu Python à vous proposer: actualité, bonnes pratiques, astuces, témoignages…</p>
<p>Cette huitième partie présente <code>pipenv</code>, un outil pour s’abstraire de <code>pip</code> et <code>virtualenv</code> qui est mis en valeur par la PyPA <em>(Python Packaging Autority)</em>. Puis nous finirons la dépêche par un cas pratique avec conteneurisation via Docker, le tout avec plein d’astuces et de conseils pour bien s’en sortir. 🚀 🐍</p>
<p>Pour rappel, les dépêches précédentes :</p>
<ul>
<li>
<a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-1">Python - partie 1</a> parlait de la popularité explosive du langage Python ;</li>
<li>
<a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-2">Python - partie 2</a> évoquait la fin du support de Python 2 ;</li>
<li>
<a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets">Python - partie 3</a> parlait des différentes façons d’installer Python et des gestionnaires de paquets Python ;</li>
<li>
<a href="//linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv">Python - partie 4</a> vous présentaient <code>py</code> et <code>pyenv</code> pour faciliter la gestion de plusieurs versions de Python en parallèle sur un poste ;</li>
<li>
<a href="//linuxfr.org/news/python-partie-5-nix-et-guix">Python — partie 5</a> qui dissertait de Nix (et Guix) ;</li>
<li>
<a href="//linuxfr.org/news/python-pour-noel-202x-partie-7-environnements-virtuels">Python — partie 7</a> évoquait les environnements virtuels Python et ses alternatives comme la conteneurisation, le tout avec plein d’astuces et de conseils pour bien s’en sortir.</li>
</ul>
<p><a href="https://github.com/olibre/GreatPractices/tree/master/python"><img src="//img.linuxfr.org/img/687474703a2f2f6f6c696272652e6769746875622e696f2f4772656174546970732f707974686f6e2f6e6577732f7061727469652d382e706e67/partie-8.png" alt='Le logo de Python est entouré de petites icônes symbolisant la variété des domaines où s’applique Python, et, à droite, un joyeux barbu se tient derrière un écran d’ordinateur qui affiche « partie = 8, "Pipenv" \n print (partie) »' title="Source : http://olibre.github.io/GreatTips/python/news/partie-8.png"></a></p>
</div><ul><li>lien nᵒ 1 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-1" hreflang="fr" href="https://linuxfr.org/redirect/108453">Python — partie 1 ― Popularité</a></li><li>lien nᵒ 2 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-2" hreflang="fr" href="https://linuxfr.org/redirect/108454">Python — partie 2 ― Python 2</a></li><li>lien nᵒ 3 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets" hreflang="fr" href="https://linuxfr.org/redirect/108455">Python — partie 3 — Installation de Python et de paquets</a></li><li>lien nᵒ 4 : <a title="https://linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv" hreflang="fr" href="https://linuxfr.org/redirect/108456">Python — partie 4 — Py Pyenv – LinuxFr.org</a></li><li>lien nᵒ 5 : <a title="https://linuxfr.org/news/python-partie-5-nix-et-guix" hreflang="fr" href="https://linuxfr.org/redirect/108457">Python — partie 5 — Nix (et Guix) </a></li><li>lien nᵒ 6 : <a title="https://linuxfr.org/news/python-partie-6-pip-et-pipx" hreflang="fr" href="https://linuxfr.org/redirect/109673">Python — partie 6 — Pip et Pipx</a></li><li>lien nᵒ 7 : <a title="https://linuxfr.org/news/python-pour-noel-202x-partie-7-environnements-virtuels" hreflang="fr" href="https://linuxfr.org/redirect/109674">Python — partie 7 — Environnements virtuels</a></li><li>lien nᵒ 8 : <a title="https://linuxfr.org/news/python-partie-10-formateur-de-code-analyse-statique" hreflang="fr" href="https://linuxfr.org/redirect/109675">Python ― partie 9 ― Formateur de code, analyse statique</a></li><li>lien nᵒ 9 : <a title="https://linuxfr.org/news/python-partie-11-entretiens" hreflang="fr" href="https://linuxfr.org/redirect/109676">Python — partie 10 — Entretiens</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#toc-pipenv-encapsule-pip-et--virtualenv"><code>pipenv</code> encapsule <code>pip</code> et <code>virtualenv</code></a><ul>
<li><a href="#toc-historique">Historique</a></li>
<li><a href="#toc-installation">Installation</a></li>
<li><a href="#toc-exemple-dutilisation">Exemple d’utilisation</a></li>
<li><a href="#toc-index-pypi-priv%C3%A9">Index PyPi privé</a></li>
</ul>
</li>
<li>
<a href="#toc-dockeriser-python">Dockeriser Python</a><ul>
<li><a href="#toc-docker-avec-pip">Docker avec <code>pip</code></a></li>
<li><a href="#toc-docker-avec-pipenv">Docker avec <code>pipenv</code></a></li>
</ul>
</li>
<li><a href="#toc-remerciements">Remerciements</a></li>
<li><a href="#toc-licence">Licence</a></li>
<li><a href="#toc-tes-astuces">Tes astuces ?</a></li>
</ul>
<h2 id="toc-pipenv-encapsule-pip-et--virtualenv">
<code>pipenv</code> encapsule <code>pip</code> et <code>virtualenv</code>
</h2>
<p>L’outil <code>pipenv</code> est présenté par l’équipe PyPA (Python Packaging Authority) dans son tutoriel en anglais :<br>
<a href="https://packaging.python.org/tutorials/managing-dependencies/">https://packaging.python.org/tutorials/managing-dependencies/</a></p>
<p>À l’origine, il y a un constat simple : l’ensemble des paquets Python installés sur votre système par votre distribution a bien été testé par cette dernière. Il est délicat d’en rajouter des nouveaux avec <code>pip</code> au niveau système ou même utilisateur car vous risquez de casser cet écosystème bien testé. Si vous avez besoin d’ajouter des paquets, c’est sûrement pour un projet particulier. Donc cela a plus de sens de ne les ajouter que dans ce projet, via un environnement virtuel. Surtout que si vous avez plusieurs projets, ceux-ci n’ont peut-être pas exactement les mêmes besoins.</p>
<p>La conclusion immédiate, c’est que <code>pip</code> et <code>venv</code> s’utilisent conjointement. Cela a un sens de les rassembler. Une autre motivation de Kenneth Reitz pour se lancer dans l’aventure <code>pipenv</code> était son souhait de capturer les dépendances générales d’un projet dans un fichier, et de capturer toutes les dépendances précises en parallèle. Il explique cela mieux dans <a href="https://speakerdeck.com/kennethreitz/the-future-of-python-dependency-management?slide=24">une présentation sur <code>pipenv</code></a> que je vous recommande .</p>
<h3 id="toc-historique">Historique</h3>
<ul>
<li>2014, la communauté discute sur le futur format <a href="https://github.com/pypa/pip/issues/1795"><code>requirements.txt</code> 2.0</a> ;</li>
<li>2015, nouvelle fonctionnalité demandée pour <code>pip</code> : <a href="https://github.com/pypa/pip/issues/2488">gérer les environnements virtuels</a> ;</li>
<li>2016, ébauche de <code>pip2</code> basé sur le format <code>Pipfile</code>, la <strong>v 2.0</strong> du format <code>requirements.txt</code> ;</li>
<li>2017, le projet <code>pip2</code> est renommé <code>pipenv</code> et le <a href="https://github.com/pypa/pipenv">dépôt de code source</a> est créé ;</li>
<li>2019, Goldy nous <a href="//linuxfr.org/users/oliver_h/journaux/quelques-bonnes-pratiques-python-pour-2019#comment-1766395">recommande <code>pipenv</code></a>.</li>
</ul>
<h3 id="toc-installation">Installation</h3>
<p>L’installation de <code>pipenv</code> peut se faire avec le gestionnaire de paquets de la distribution :</p>
<pre><code class="cpp"><span class="err">$</span> <span class="n">sudo</span> <span class="n">apt</span> <span class="n">search</span> <span class="n">pipenv</span>
<span class="n">Sorting</span><span class="p">...</span> <span class="n">Done</span>
<span class="n">Full</span> <span class="n">Text</span> <span class="n">Search</span><span class="p">...</span> <span class="n">Done</span>
<span class="n">pipenv</span><span class="o">/</span><span class="n">disco</span><span class="p">,</span><span class="n">disco</span> <span class="mf">11.9.0</span><span class="o">-</span><span class="mi">1</span> <span class="n">all</span>
<span class="n">Python</span><span class="err">'</span><span class="n">s</span> <span class="n">officially</span> <span class="n">recommended</span> <span class="n">packaging</span> <span class="n">tool</span>
<span class="err">$</span> <span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">pipenv</span>
<span class="n">Reading</span> <span class="n">package</span> <span class="n">lists</span><span class="p">...</span> <span class="n">Done</span>
<span class="n">Building</span> <span class="n">dependency</span> <span class="n">tree</span>
<span class="n">Reading</span> <span class="n">state</span> <span class="n">information</span><span class="p">...</span> <span class="n">Done</span>
<span class="n">The</span> <span class="n">following</span> <span class="n">additional</span> <span class="n">packages</span> <span class="n">will</span> <span class="n">be</span> <span class="nl">installed</span><span class="p">:</span>
<span class="n">python3</span><span class="o">-</span><span class="n">virtualenv</span> <span class="n">python3</span><span class="o">-</span><span class="n">virtualenv</span><span class="o">-</span><span class="n">clone</span>
<span class="n">The</span> <span class="n">following</span> <span class="n">NEW</span> <span class="n">packages</span> <span class="n">will</span> <span class="n">be</span> <span class="nl">installed</span><span class="p">:</span>
<span class="n">pipenv</span> <span class="n">python3</span><span class="o">-</span><span class="n">virtualenv</span> <span class="n">python3</span><span class="o">-</span><span class="n">virtualenv</span><span class="o">-</span><span class="n">clone</span>
<span class="p">[...]</span></code></pre>
<p>L’installation peut aussi se faire avec <code>pip</code> ce qui permet d’avoir une version plus à jour :</p>
<pre><code class="cpp"><span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="n">pipenv</span> <span class="o">--</span><span class="n">user</span> <span class="o">--</span><span class="n">upgrade</span> <span class="o">--</span><span class="n">progress</span><span class="o">-</span><span class="n">bar</span> <span class="n">emoji</span>
<span class="p">[...]</span>
<span class="n">Successfully</span> <span class="n">installed</span> <span class="n">pipenv</span><span class="o">-</span><span class="mf">2018.11.26</span></code></pre>
<p>Dans, mon cas, j’ai installé <code>pipenv</code> avec les deux possibilités, j’ai donc deux exécutables <code>pipenv</code>. Vérifions les versions installées :</p>
<pre><code class="cpp"><span class="err">$</span> <span class="n">type</span> <span class="o">-</span><span class="n">a</span> <span class="n">pipenv</span>
<span class="n">pipenv</span> <span class="n">is</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">oliver</span><span class="o">/</span><span class="p">.</span><span class="n">local</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">pipenv</span>
<span class="n">pipenv</span> <span class="n">is</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">pipenv</span>
<span class="err">$</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">oliver</span><span class="o">/</span><span class="p">.</span><span class="n">local</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">pipenv</span> <span class="o">--</span><span class="n">version</span>
<span class="n">pipenv</span><span class="p">,</span> <span class="n">version</span> <span class="mf">2018.11.26</span>
<span class="err">$</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">pipenv</span> <span class="o">--</span><span class="n">version</span>
<span class="n">pipenv</span><span class="p">,</span> <span class="n">version</span> <span class="mf">11.9.0</span></code></pre>
<p>En fait, la version <a href="https://github.com/pypa/pipenv/releases/tag/v11.9.0"><code>11.9.0</code></a> date du 21 mars 2018, puis, <a href="https://github.com/pypa/pipenv/releases/tag/v2018.05.18">deux mois après</a>, la numérotation des versions est modifiée. La version la plus récente est bien celle installée avec <code>pip</code>.</p>
<p>Rappels :</p>
<ol>
<li>Installer <code>pipenv</code> avec <code>pip</code> pourrait casser le fonctionnement d’une autre application. Par exemple, la mise à jour de la dépendance <code>virtualenv</code> peut rentrer en conflit avec la version nécessaire à cette autre application. L’installation recommandée est celle avec le gestionnaire de la distribution. Utiliser <code>pip</code> si cela est vraiment nécessaire et que les risques sont maîtrisés.</li>
<li>Ou alors, vous avez lu la dépêche qui parle de <code>pip</code> et <code>pipx</code> et vous installez <code>pipenv</code> avec <code>pipx</code>. Là, vous êtes sûr d’éviter plein de problèmes.</li>
</ol>
<h3 id="toc-exemple-dutilisation">Exemple d’utilisation</h3>
<p>Se positionner à la base de son projet et installer les dépendances <code>flask</code> et <code>autopep8</code> avec <code>pipenv</code> :</p>
<pre><code class="cpp"><span class="err">$</span> <span class="n">cd</span> <span class="o">/</span><span class="n">chemin</span><span class="o">/</span><span class="n">vers</span><span class="o">/</span><span class="n">mon</span><span class="o">/</span><span class="n">projet</span><span class="o">/</span><span class="n">chouette</span></code></pre>
<pre><code class="python"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">install</span> <span class="n">flask</span>
<span class="n">Creating</span> <span class="n">a</span> <span class="n">virtualenv</span> <span class="k">for</span> <span class="n">this</span> <span class="n">project</span><span class="err">…</span>
<span class="n">Pipfile</span><span class="p">:</span> <span class="o">/</span><span class="n">chemin</span><span class="o">/</span><span class="n">vers</span><span class="o">/</span><span class="n">mon</span><span class="o">/</span><span class="n">projet</span><span class="o">/</span><span class="n">chouette</span><span class="o">/</span><span class="n">Pipfile</span>
<span class="n">Using</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">python3</span> <span class="p">(</span><span class="mf">3.7</span><span class="o">.</span><span class="mi">3</span><span class="p">)</span> <span class="n">to</span> <span class="n">create</span> <span class="n">virtualenv</span><span class="err">…</span>
<span class="err">⠸</span> <span class="n">Creating</span> <span class="n">virtual</span> <span class="n">environment</span><span class="o">...</span><span class="n">Already</span> <span class="n">using</span> <span class="n">interpreter</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">python3</span>
<span class="n">Using</span> <span class="n">base</span> <span class="n">prefix</span> <span class="s1">'/usr'</span>
<span class="n">New</span> <span class="n">python</span> <span class="n">executable</span> <span class="ow">in</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">tux</span><span class="o">/.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">virtualenvs</span><span class="o">/</span><span class="n">chouette</span><span class="o">-</span><span class="mi">4</span><span class="n">m</span><span class="o">-</span><span class="n">Hw0ap</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">python3</span>
<span class="n">Also</span> <span class="n">creating</span> <span class="n">executable</span> <span class="ow">in</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">tux</span><span class="o">/.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">virtualenvs</span><span class="o">/</span><span class="n">chouette</span><span class="o">-</span><span class="mi">4</span><span class="n">m</span><span class="o">-</span><span class="n">Hw0ap</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">python</span>
<span class="n">Installing</span> <span class="n">setuptools</span><span class="p">,</span> <span class="n">pip</span><span class="p">,</span> <span class="n">wheel</span><span class="o">...</span>
<span class="n">done</span><span class="o">.</span>
<span class="err">✔</span> <span class="n">Successfully</span> <span class="n">created</span> <span class="n">virtual</span> <span class="n">environment</span><span class="err">!</span>
<span class="n">Virtualenv</span> <span class="n">location</span><span class="p">:</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">tux</span><span class="o">/.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">virtualenvs</span><span class="o">/</span><span class="n">chouette</span><span class="o">-</span><span class="mi">4</span><span class="n">m</span><span class="o">-</span><span class="n">Hw0ap</span>
<span class="n">Creating</span> <span class="n">a</span> <span class="n">Pipfile</span> <span class="k">for</span> <span class="n">this</span> <span class="n">project</span><span class="err">…</span>
<span class="n">Installing</span> <span class="n">flask</span><span class="err">…</span>
<span class="n">Adding</span> <span class="n">flask</span> <span class="n">to</span> <span class="n">Pipfile</span><span class="s1">'s [packages]…</span>
<span class="err">✔</span> <span class="n">Installation</span> <span class="n">Succeeded</span>
<span class="n">Pipfile</span><span class="o">.</span><span class="n">lock</span> <span class="ow">not</span> <span class="n">found</span><span class="p">,</span> <span class="n">creating</span><span class="err">…</span>
<span class="n">Locking</span> <span class="p">[</span><span class="n">dev</span><span class="o">-</span><span class="n">packages</span><span class="p">]</span> <span class="n">dependencies</span><span class="err">…</span>
<span class="n">Locking</span> <span class="p">[</span><span class="n">packages</span><span class="p">]</span> <span class="n">dependencies</span><span class="err">…</span>
<span class="err">✔</span> <span class="n">Success</span><span class="err">!</span>
<span class="n">Updated</span> <span class="n">Pipfile</span><span class="o">.</span><span class="n">lock</span> <span class="p">(</span><span class="mi">662286</span><span class="p">)</span><span class="err">!</span>
<span class="n">Installing</span> <span class="n">dependencies</span> <span class="kn">from</span> <span class="nn">Pipfile.lock</span> <span class="p">(</span><span class="mi">662286</span><span class="p">)</span><span class="err">…</span>
<span class="err">🐍</span> <span class="err">▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉</span> <span class="mi">6</span><span class="o">/</span><span class="mi">6</span> <span class="err">—</span> <span class="mo">00</span><span class="p">:</span><span class="mo">00</span><span class="p">:</span><span class="mo">01</span>
<span class="n">To</span> <span class="n">activate</span> <span class="n">this</span> <span class="n">project</span><span class="s1">'s virtualenv, run pipenv shell.</span>
<span class="n">Alternatively</span><span class="p">,</span> <span class="n">run</span> <span class="n">a</span> <span class="n">command</span> <span class="n">inside</span> <span class="n">the</span> <span class="n">virtualenv</span> <span class="k">with</span> <span class="n">pipenv</span> <span class="n">run</span><span class="o">.</span></code></pre>
<pre><code class="rust"><span class="cp">$</span><span class="w"> </span><span class="n">pipenv</span><span class="w"> </span><span class="n">install</span><span class="w"> </span><span class="o">--</span><span class="n">dev</span><span class="w"> </span><span class="n">autopep8</span><span class="w"></span>
<span class="n">Installing</span><span class="w"> </span><span class="n">autopep8</span><span class="err">…</span><span class="w"></span>
<span class="n">Adding</span><span class="w"> </span><span class="n">autopep8</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">Pipfile</span><span class="na">'s</span><span class="w"> </span><span class="p">[</span><span class="n">dev</span><span class="o">-</span><span class="n">packages</span><span class="p">]</span><span class="err">…</span><span class="w"></span>
<span class="err">✔</span><span class="w"> </span><span class="n">Installation</span><span class="w"> </span><span class="n">Succeeded</span><span class="w"> </span>
<span class="n">Pipfile</span><span class="p">.</span><span class="n">lock</span><span class="w"> </span><span class="p">(</span><span class="n">b0a650</span><span class="p">)</span><span class="w"> </span><span class="n">out</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">date</span><span class="p">,</span><span class="w"> </span><span class="n">updating</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="p">(</span><span class="mi">662286</span><span class="p">)</span><span class="err">…</span><span class="w"></span>
<span class="n">Locking</span><span class="w"> </span><span class="p">[</span><span class="n">dev</span><span class="o">-</span><span class="n">packages</span><span class="p">]</span><span class="w"> </span><span class="n">dependencies</span><span class="err">…</span><span class="w"></span>
<span class="err">✔</span><span class="w"> </span><span class="n">Success</span><span class="o">!</span><span class="w"> </span>
<span class="n">Locking</span><span class="w"> </span><span class="p">[</span><span class="n">packages</span><span class="p">]</span><span class="w"> </span><span class="n">dependencies</span><span class="err">…</span><span class="w"></span>
<span class="err">✔</span><span class="w"> </span><span class="n">Success</span><span class="o">!</span><span class="w"> </span>
<span class="n">Updated</span><span class="w"> </span><span class="n">Pipfile</span><span class="p">.</span><span class="n">lock</span><span class="w"> </span><span class="p">(</span><span class="n">b0a650</span><span class="p">)</span><span class="o">!</span><span class="w"></span>
<span class="n">Installing</span><span class="w"> </span><span class="n">dependencies</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">Pipfile</span><span class="p">.</span><span class="n">lock</span><span class="w"> </span><span class="p">(</span><span class="n">b0a650</span><span class="p">)</span><span class="err">…</span><span class="w"></span>
<span class="w"> </span><span class="err">🐍</span><span class="w"> </span><span class="err">▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉</span><span class="w"> </span><span class="mi">8</span><span class="o">/</span><span class="mi">8</span><span class="w"> </span><span class="err">—</span><span class="w"> </span><span class="mi">00</span>:<span class="mi">00</span>:<span class="mi">01</span><span class="w"></span>
<span class="n">To</span><span class="w"> </span><span class="n">activate</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="n">project</span><span class="na">'s</span><span class="w"> </span><span class="n">virtualenv</span><span class="p">,</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">pipenv</span><span class="w"> </span><span class="n">shell</span><span class="p">.</span><span class="w"></span>
<span class="n">Alternatively</span><span class="p">,</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">command</span><span class="w"> </span><span class="n">inside</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">virtualenv</span><span class="w"> </span><span class="n">with</span><span class="w"> </span><span class="n">pipenv</span><span class="w"> </span><span class="n">run</span><span class="p">.</span><span class="w"></span></code></pre>
<p>Nous avons deux nouveaux fichiers :</p>
<ul>
<li>
<code>Pipfile</code>, au format <a href="https://fr.wikipedia.org/wiki/TOML">TOML</a>, spécifie les règles générales ;</li>
<li>
<code>Pipfile.lock</code>, au format <a href="https://fr.wikipedia.org/wiki/JavaScript_Object_Notation">JSON</a>, verrouille <em>(lock)</em> les versions de chacune des dépendances.</li>
</ul>
<pre><code class="ada"><span class="err">$</span> <span class="n">ls</span> <span class="o">-</span><span class="n">gG</span>
<span class="n">total</span> <span class="mi">12</span><span class="n">K</span>
<span class="o">-</span><span class="n">rw</span><span class="o">-</span><span class="n">r</span><span class="c1">--r-- 1 4,5K août 31 15:42 Pipfile.lock</span>
<span class="o">-</span><span class="n">rw</span><span class="o">-</span><span class="n">r</span><span class="c1">--r-- 1 150 août 31 15:42 Pipfile</span></code></pre>
<p>Le fichier <code>Pipfile</code> sépare les dépendances en deux sections et peut remplacer les fichiers <code>requirements.txt</code> et <code>requirements-dev.txt</code> que nous rencontrons souvent à la base des projets Python. C'est aussi une alternative aux sections <code>install_requires</code> et <code>extras_require</code> du fichier <code>setup.py</code>.</p>
<pre><code class="yaml"><span class="l l-Scalar l-Scalar-Plain">$ head -n 33 Pipfile*</span>
<span class="l l-Scalar l-Scalar-Plain">==> Pipfile <==</span>
<span class="l l-Scalar l-Scalar-Plain">[[source]]</span>
<span class="l l-Scalar l-Scalar-Plain">name = "pypi"</span>
<span class="l l-Scalar l-Scalar-Plain">url = "https://pypi.org/simple"</span>
<span class="l l-Scalar l-Scalar-Plain">verify_ssl = true</span>
<span class="l l-Scalar l-Scalar-Plain">[dev-packages]</span>
<span class="l l-Scalar l-Scalar-Plain">autopep8 = "*"</span>
<span class="l l-Scalar l-Scalar-Plain">[packages]</span>
<span class="l l-Scalar l-Scalar-Plain">flask = "*"</span>
<span class="l l-Scalar l-Scalar-Plain">[requires]</span>
<span class="l l-Scalar l-Scalar-Plain">python_version = "3.7"</span>
<span class="l l-Scalar l-Scalar-Plain">==> Pipfile.lock <==</span>
<span class="l l-Scalar l-Scalar-Plain">{</span>
<span class="l l-Scalar l-Scalar-Plain">"_meta"</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">{</span>
<span class="s">"hash"</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">{</span>
<span class="s">"sha256"</span><span class="p p-Indicator">:</span> <span class="s">"be66bf67d2a9a5d1606be53501e0b029c8c465fc0660151ea0b5c780f7b0a650"</span>
<span class="p p-Indicator">},</span>
<span class="s">"pipfile-spec"</span><span class="p p-Indicator">:</span> <span class="nv">6</span><span class="p p-Indicator">,</span>
<span class="s">"requires"</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">{</span>
<span class="s">"python_version"</span><span class="p p-Indicator">:</span> <span class="s">"3.7"</span>
<span class="p p-Indicator">},</span>
<span class="s">"sources"</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">[</span>
<span class="p p-Indicator">{</span>
<span class="s">"name"</span><span class="p p-Indicator">:</span> <span class="s">"pypi"</span><span class="p p-Indicator">,</span>
<span class="s">"url"</span><span class="p p-Indicator">:</span> <span class="s">"https://pypi.org/simple"</span><span class="p p-Indicator">,</span>
<span class="s">"verify_ssl"</span><span class="p p-Indicator">:</span> <span class="nv">true</span>
<span class="p p-Indicator">}</span>
<span class="p p-Indicator">]</span>
<span class="p p-Indicator">}</span><span class="err">,</span>
<span class="s">"default"</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">{</span>
<span class="s">"click"</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">{</span>
<span class="s">"hashes"</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">[</span>
<span class="s">"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13"</span><span class="p p-Indicator">,</span>
<span class="s">"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"</span>
<span class="p p-Indicator">],</span>
<span class="s">"version"</span><span class="p p-Indicator">:</span> <span class="s">"==7.0"</span>
<span class="p p-Indicator">},</span>
<span class="s">"flask"</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">{</span>
<span class="s">"hashes"</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">[</span>
<span class="s">"sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52"</span><span class="p p-Indicator">,</span>
<span class="s">"sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6"</span>
<span class="p p-Indicator">],</span>
<span class="s">"index"</span><span class="p p-Indicator">:</span> <span class="s">"pypi"</span><span class="p p-Indicator">,</span>
<span class="s">"version"</span><span class="p p-Indicator">:</span> <span class="s">"==1.1.1"</span>
<span class="p p-Indicator">},</span></code></pre>
<p>Le fichier <code>Pipfile.lock</code> n’a pas besoin d’être versionné, car <code>pipenv</code> le génère automatiquement à partir du fichier <code>Pipfile</code>. Néanmoins, je recommande de le versionner afin de suivre l’évolution des versions des dépendances et de pouvoir reproduire un même fonctionnement tout au long du cycle : développement, intégration, production…</p>
<p>La commande 'pipenv lock' est l’équivalent de 'pip freeze'. Voir aussi le projet <a href="https://pypi.org/project/lock-requirements/">lock-requirements</a>. </p>
<p>Visitons notre environnement virtualisé avec la commande <code>pipenv shell</code> :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">shell</span>
<span class="n">Launching</span> <span class="n">subshell</span> <span class="n">in</span> <span class="n">virtual</span> <span class="n">environment</span><span class="err">…</span>
<span class="p">.</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">tux</span><span class="o">/</span><span class="p">.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">virtualenvs</span><span class="o">/</span><span class="n">chouette</span><span class="o">-</span><span class="mi">4</span><span class="n">m</span><span class="o">-</span><span class="n">Hw0ap</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">activate</span></code></pre>
<pre><code class="cpp"><span class="err">$</span> <span class="n">pip</span> <span class="n">list</span>
<span class="n">Package</span> <span class="n">Version</span>
<span class="o">------------</span> <span class="o">-------</span>
<span class="n">autopep8</span> <span class="mf">1.4.4</span>
<span class="n">Click</span> <span class="mf">7.0</span>
<span class="n">Flask</span> <span class="mf">1.1.1</span>
<span class="n">itsdangerous</span> <span class="mf">1.1.0</span>
<span class="n">Jinja2</span> <span class="mf">2.10.1</span>
<span class="n">MarkupSafe</span> <span class="mf">1.1.1</span>
<span class="n">pip</span> <span class="mf">19.2.3</span>
<span class="n">pycodestyle</span> <span class="mf">2.5.0</span>
<span class="n">setuptools</span> <span class="mf">41.2.0</span>
<span class="n">Werkzeug</span> <span class="mf">0.15.5</span>
<span class="n">wheel</span> <span class="mf">0.33.6</span></code></pre>
<p>Utiliser <code>[Ctrl]</code> + <code>[D]</code> ou <code>exit</code> pour sortir du <code>pipenv shell</code>.</p>
<p>La commande <code>pipenv run</code> est aussi bien pratique :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">run</span> <span class="n">pip</span> <span class="n">list</span>
<span class="n">Package</span> <span class="n">Version</span>
<span class="o">------------</span> <span class="o">-------</span>
<span class="n">autopep8</span> <span class="mf">1.4.4</span>
<span class="n">Click</span> <span class="mf">7.0</span>
<span class="n">Flask</span> <span class="mf">1.1.1</span>
<span class="n">itsdangerous</span> <span class="mf">1.1.0</span>
<span class="n">Jinja2</span> <span class="mf">2.10.1</span>
<span class="n">MarkupSafe</span> <span class="mf">1.1.1</span>
<span class="n">pip</span> <span class="mf">19.2.3</span>
<span class="n">pycodestyle</span> <span class="mf">2.5.0</span>
<span class="n">setuptools</span> <span class="mf">41.2.0</span>
<span class="n">Werkzeug</span> <span class="mf">0.15.5</span>
<span class="n">wheel</span> <span class="mf">0.33.6</span></code></pre>
<p>Mais pour visualiser ses dépendances, <code>pipenv graph</code> est mieux que <code>pip list</code> :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">graph</span>
<span class="n">autopep8</span><span class="o">==</span><span class="mf">1.4.4</span>
<span class="o">-</span> <span class="n">pycodestyle</span> <span class="p">[</span><span class="nl">required</span><span class="p">:</span> <span class="o">>=</span><span class="mf">2.4.0</span><span class="p">,</span> <span class="nl">installed</span><span class="p">:</span> <span class="mf">2.5.0</span><span class="p">]</span>
<span class="n">Flask</span><span class="o">==</span><span class="mf">1.1.1</span>
<span class="o">-</span> <span class="n">click</span> <span class="p">[</span><span class="nl">required</span><span class="p">:</span> <span class="o">>=</span><span class="mf">5.1</span><span class="p">,</span> <span class="nl">installed</span><span class="p">:</span> <span class="mf">7.0</span><span class="p">]</span>
<span class="o">-</span> <span class="n">itsdangerous</span> <span class="p">[</span><span class="nl">required</span><span class="p">:</span> <span class="o">>=</span><span class="mf">0.24</span><span class="p">,</span> <span class="nl">installed</span><span class="p">:</span> <span class="mf">1.1.0</span><span class="p">]</span>
<span class="o">-</span> <span class="n">Jinja2</span> <span class="p">[</span><span class="nl">required</span><span class="p">:</span> <span class="o">>=</span><span class="mf">2.10.1</span><span class="p">,</span> <span class="nl">installed</span><span class="p">:</span> <span class="mf">2.10.1</span><span class="p">]</span>
<span class="o">-</span> <span class="n">MarkupSafe</span> <span class="p">[</span><span class="nl">required</span><span class="p">:</span> <span class="o">>=</span><span class="mf">0.23</span><span class="p">,</span> <span class="nl">installed</span><span class="p">:</span> <span class="mf">1.1.1</span><span class="p">]</span>
<span class="o">-</span> <span class="n">Werkzeug</span> <span class="p">[</span><span class="nl">required</span><span class="p">:</span> <span class="o">>=</span><span class="mf">0.15</span><span class="p">,</span> <span class="nl">installed</span><span class="p">:</span> <span class="mf">0.15.5</span><span class="p">]</span></code></pre>
<p>Supprimer son environnement virtualisé avec <code>pipenv --rm</code> :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="o">--</span><span class="n">rm</span>
<span class="n">Removing</span> <span class="n">virtualenv</span> <span class="p">(</span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">tux</span><span class="o">/</span><span class="p">.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">virtualenvs</span><span class="o">/</span><span class="n">chouette</span><span class="o">-</span><span class="mi">4</span><span class="n">m</span><span class="o">-</span><span class="n">Hw0ap</span><span class="p">)</span><span class="err">…</span></code></pre>
<p>Vérifions notre environnement virtualisé :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">run</span> <span class="n">pip</span> <span class="n">list</span>
<span class="n">Creating</span> <span class="n">a</span> <span class="n">virtualenv</span> <span class="k">for</span> <span class="n">this</span> <span class="n">project</span><span class="err">…</span>
<span class="nl">Pipfile</span><span class="p">:</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">tux</span><span class="o">/</span><span class="n">chouette</span><span class="o">/</span><span class="n">Pipfile</span>
<span class="n">Using</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="n">m</span> <span class="p">(</span><span class="mf">3.7.3</span><span class="p">)</span> <span class="n">to</span> <span class="n">create</span> <span class="n">virtualenv</span><span class="err">…</span>
<span class="err">⠼</span> <span class="n">Creating</span> <span class="n">virtual</span> <span class="n">environment</span><span class="p">...</span><span class="n">Already</span> <span class="n">using</span> <span class="n">interpreter</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="n">m</span>
<span class="n">Using</span> <span class="n">base</span> <span class="n">prefix</span> <span class="err">'</span><span class="o">/</span><span class="n">usr</span><span class="err">'</span>
<span class="n">New</span> <span class="n">python</span> <span class="n">executable</span> <span class="n">in</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">tux</span><span class="o">/</span><span class="p">.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">virtualenvs</span><span class="o">/</span><span class="n">chouette</span><span class="o">-</span><span class="mi">4</span><span class="n">m</span><span class="o">-</span><span class="n">Hw0ap</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="n">m</span>
<span class="n">Also</span> <span class="n">creating</span> <span class="n">executable</span> <span class="n">in</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">tux</span><span class="o">/</span><span class="p">.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">virtualenvs</span><span class="o">/</span><span class="n">chouette</span><span class="o">-</span><span class="mi">4</span><span class="n">m</span><span class="o">-</span><span class="n">Hw0ap</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python</span>
<span class="n">Installing</span> <span class="n">setuptools</span><span class="p">,</span> <span class="n">pip</span><span class="p">,</span> <span class="n">wheel</span><span class="p">...</span>
<span class="n">done</span><span class="p">.</span>
<span class="n">Running</span> <span class="n">virtualenv</span> <span class="n">with</span> <span class="n">interpreter</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="n">m</span>
<span class="err">✔</span> <span class="n">Successfully</span> <span class="n">created</span> <span class="n">virtual</span> <span class="n">environment</span><span class="o">!</span>
<span class="n">Virtualenv</span> <span class="nl">location</span><span class="p">:</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">tux</span><span class="o">/</span><span class="p">.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">virtualenvs</span><span class="o">/</span><span class="n">chouette</span><span class="o">-</span><span class="mi">4</span><span class="n">m</span><span class="o">-</span><span class="n">Hw0ap</span>
<span class="n">Package</span> <span class="n">Version</span>
<span class="o">----------</span> <span class="o">-------</span>
<span class="n">pip</span> <span class="mf">19.2.3</span>
<span class="n">setuptools</span> <span class="mf">41.2.0</span>
<span class="n">wheel</span> <span class="mf">0.33.6</span></code></pre>
<p>Installons les modules nécessaires en prod avec <code>pipenv install</code> ou <code>pipenv sync</code> :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">install</span>
<span class="n">Installing</span> <span class="n">dependencies</span> <span class="n">from</span> <span class="n">Pipfile</span><span class="p">.</span><span class="n">lock</span> <span class="p">(</span><span class="n">b0a650</span><span class="p">)</span><span class="err">…</span>
<span class="err">🐍</span> <span class="err">▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉</span> <span class="mi">6</span><span class="o">/</span><span class="mi">6</span> <span class="err">—</span> <span class="mo">00</span><span class="o">:</span><span class="mo">00</span><span class="o">:</span><span class="mo">01</span>
<span class="n">To</span> <span class="n">activate</span> <span class="n">this</span> <span class="n">project</span><span class="err">'</span><span class="n">s</span> <span class="n">virtualenv</span><span class="p">,</span> <span class="n">run</span> <span class="n">pipenv</span> <span class="n">shell</span><span class="p">.</span>
<span class="n">Alternatively</span><span class="p">,</span> <span class="n">run</span> <span class="n">a</span> <span class="n">command</span> <span class="n">inside</span> <span class="n">the</span> <span class="n">virtualenv</span> <span class="n">with</span> <span class="n">pipenv</span> <span class="n">run</span><span class="p">.</span></code></pre>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">run</span> <span class="n">pip</span> <span class="n">list</span>
<span class="n">Package</span> <span class="n">Version</span>
<span class="o">------------</span> <span class="o">-------</span>
<span class="n">Click</span> <span class="mf">7.0</span>
<span class="n">Flask</span> <span class="mf">1.1.1</span>
<span class="n">itsdangerous</span> <span class="mf">1.1.0</span>
<span class="n">Jinja2</span> <span class="mf">2.10.1</span>
<span class="n">MarkupSafe</span> <span class="mf">1.1.1</span>
<span class="n">pip</span> <span class="mf">19.2.3</span>
<span class="n">setuptools</span> <span class="mf">41.2.0</span>
<span class="n">Werkzeug</span> <span class="mf">0.15.5</span>
<span class="n">wheel</span> <span class="mf">0.33.6</span></code></pre>
<p>Enfin, installons les modules nécessaires au développement avec l’option <code>--dev</code> :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">install</span> <span class="o">--</span><span class="n">dev</span>
<span class="n">Installing</span> <span class="n">dependencies</span> <span class="n">from</span> <span class="n">Pipfile</span><span class="p">.</span><span class="n">lock</span> <span class="p">(</span><span class="n">b0a650</span><span class="p">)</span><span class="err">…</span>
<span class="err">🐍</span> <span class="err">▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉</span> <span class="mi">8</span><span class="o">/</span><span class="mi">8</span> <span class="err">—</span> <span class="mo">00</span><span class="o">:</span><span class="mo">00</span><span class="o">:</span><span class="mo">02</span>
<span class="n">To</span> <span class="n">activate</span> <span class="n">this</span> <span class="n">project</span><span class="err">'</span><span class="n">s</span> <span class="n">virtualenv</span><span class="p">,</span> <span class="n">run</span> <span class="n">pipenv</span> <span class="n">shell</span><span class="p">.</span>
<span class="n">Alternatively</span><span class="p">,</span> <span class="n">run</span> <span class="n">a</span> <span class="n">command</span> <span class="n">inside</span> <span class="n">the</span> <span class="n">virtualenv</span> <span class="n">with</span> <span class="n">pipenv</span> <span class="n">run</span><span class="p">.</span></code></pre>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">run</span> <span class="n">pip</span> <span class="n">list</span>
<span class="n">Package</span> <span class="n">Version</span>
<span class="o">------------</span> <span class="o">-------</span>
<span class="n">autopep8</span> <span class="mf">1.4.4</span>
<span class="n">Click</span> <span class="mf">7.0</span>
<span class="n">Flask</span> <span class="mf">1.1.1</span>
<span class="n">itsdangerous</span> <span class="mf">1.1.0</span>
<span class="n">Jinja2</span> <span class="mf">2.10.1</span>
<span class="n">MarkupSafe</span> <span class="mf">1.1.1</span>
<span class="n">pip</span> <span class="mf">19.2.3</span>
<span class="n">pycodestyle</span> <span class="mf">2.5.0</span>
<span class="n">setuptools</span> <span class="mf">41.2.0</span>
<span class="n">Werkzeug</span> <span class="mf">0.15.5</span>
<span class="n">wheel</span> <span class="mf">0.33.6</span></code></pre>
<p>L’installation de <a href="https://github.com/psf/black"><code>black</code></a> (formateur de code Python) nécessite d’autoriser les versions bêta car celui-ci n’est livré que en <em>prerelease</em> (cf <a href="https://pypi.org/project/black/#history">sa page sur pypi.org</a>):</p>
<pre><code>$ pipenv install --dev --pre black
Installing black…
Adding black to Pipfile's [dev-packages]…
✔ Installation Succeeded
Pipfile.lock (d00da7) out of date, updating to (b0a650)…
Locking [dev-packages] dependencies…
✔ Success!
Locking [packages] dependencies…
✔ Success!
Updated Pipfile.lock (d00da7)!
Installing dependencies from Pipfile.lock (d00da7)…
🐍 ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 12/12 — 00:00:02
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
</code></pre>
<p>Sans l’option <code>--dev</code> nous obtenons l’erreur peu explicite <code>"ERROR : Could not find a version that matches black"</code> au milieu d’un tas de logs peu intuitifs. Maintenenant, notre fichier <code>Pipfile</code> contient une section <code>allow_prereleases = true</code> :</p>
<pre><code class="ini"><span class="na">$ cat Pipfile</span>
<span class="k">[[source]]</span>
<span class="na">name</span> <span class="o">=</span> <span class="s">"pypi"</span>
<span class="na">url</span> <span class="o">=</span> <span class="s">"https://pypi.org/simple"</span>
<span class="na">verify_ssl</span> <span class="o">=</span> <span class="s">true</span>
<span class="k">[dev-packages]</span>
<span class="na">autopep8</span> <span class="o">=</span> <span class="s">"*"</span>
<span class="na">black</span> <span class="o">=</span> <span class="s">"*"</span>
<span class="k">[packages]</span>
<span class="na">flask</span> <span class="o">=</span> <span class="s">"*"</span>
<span class="k">[requires]</span>
<span class="na">python_version</span> <span class="o">=</span> <span class="s">"3.7"</span>
<span class="k">[pipenv]</span>
<span class="na">allow_prereleases</span> <span class="o">=</span> <span class="s">true</span></code></pre>
<p>C’est une mauvaise idée d’installer <code>black</code> avec <code>pipenv</code> car cela nous force à activer <code>allow_prereleases = true</code> qui s’applique aussi aux versions utilisées pour l’exécution de l’application. Je recommande plutôt d’installer <code>black</code> avec <code>pip install --user</code>.</p>
<p>Nous avons <code>black</code>, alors désinstallons <code>autopep8</code> :</p>
<pre><code>$ pipenv uninstall autopep8
Uninstalling autopep8…
Uninstalling autopep8-1.4.4:
Successfully uninstalled autopep8-1.4.4
Removing autopep8 from Pipfile…
Locking [dev-packages] dependencies…
✔ Success!
Locking [packages] dependencies…
✔ Success!
Updated Pipfile.lock (a5c3f0)!
</code></pre>
<p>Faisons le ménage, désinstallons les dépendances qui ne sont plus nécessaires :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">pipenv</span> <span class="n">clean</span>
<span class="n">Uninstalling</span> <span class="n">pycodestyle</span><span class="err">…</span></code></pre>
<p>Pipenv est vraiment un outil facile à utiliser et très pratique. Personnellement, j’édite directement le fichier <code>Pipfile</code> qui reste simple, et du coup, je n’utilise plus les commandes <code>pipenv install</code> et <code>pipenv uninstall</code>. En fait, la commande <code>pipenv install</code> est pratique pour ajouter d’une dépendance avec des paramètres compliqués. Dans ce cas, j’applique la ligne de commande de <code>pip install</code> à <code>pipenv install</code>.</p>
<p>Exemple, la documentation dit :</p>
<pre><code class="lisp"><span class="nv">pip</span> <span class="nv">install</span> <span class="nv">git+ssh://[email protected]/chemin/cool.git#egg=cool</span></code></pre>
<p>Donc nous remplaçons <code>pip</code> par <code>pipenv</code> :</p>
<pre><code class="lisp"><span class="nv">python3</span> <span class="nv">-m</span> <span class="nv">pipenv</span> <span class="nv">install</span> <span class="nv">git+ssh://[email protected]/chemin/cool.git#egg=cool</span></code></pre>
<p>Et nous obtenons une nouvelle ligne dans notre fichier <code>Pipfile</code> :</p>
<pre><code class="ini"><span class="k">[packages]</span>
<span class="na">cool</span> <span class="o">=</span> <span class="s">{git = "ssh://[email protected]/chemin/cool.git"}</span></code></pre>
<p>Un autre exemple, avec la documentation qui dit :</p>
<pre><code class="css"><span class="nt">pip</span> <span class="nt">install</span> <span class="nt">prospector</span><span class="o">[</span><span class="nt">with_everything</span><span class="o">]</span></code></pre>
<p>Nous remplaçons <code>pip</code> par <code>pipenv</code> et n’oublions pas l’option <code>--dev</code> :</p>
<pre><code class="css"><span class="nt">python3</span> <span class="nt">-m</span> <span class="nt">pipenv</span> <span class="nt">install</span> <span class="nt">--dev</span> <span class="nt">prospector</span><span class="o">[</span><span class="nt">with_everything</span><span class="o">]</span></code></pre>
<p>Cela rajoute dans notre fichier <code>Pipfile</code> :</p>
<pre><code class="ini"><span class="k">[dev-packages]</span>
<span class="na">prospector</span> <span class="o">=</span> <span class="s">{extras = ["with_everything"],version = "*"}</span></code></pre>
<h3 id="toc-index-pypi-privé">Index PyPi privé</h3>
<p>Souvent, les entreprises ont un index PyPi privé pour permettre de livrer ses propres packages Python en interne. Pour gérer proprement ce cas de figure, voici mes recommandations. Dans le fichier <code>Pipfile</code>, déclarer les deux sources : </p>
<pre><code class="ini"><span class="k">[[source]]</span>
<span class="na">source</span> <span class="o">=</span> <span class="s">"pypi"</span>
<span class="na">url</span> <span class="o">=</span> <span class="s">"https://pypi.org/simple"</span>
<span class="na">verify_ssl</span> <span class="o">=</span> <span class="s">true</span>
<span class="k">[[source]]</span>
<span class="na">source</span> <span class="o">=</span> <span class="s">"private"</span>
<span class="na">url</span> <span class="o">=</span> <span class="s">"https://pypi.monserveur.com/simple"</span>
<span class="na">verify_ssl</span> <span class="o">=</span> <span class="s">true</span></code></pre>
<p>Si l’index PyPi privé est protégé par un {nom}/{mdp}, chaque utilisateur crée un fichier <code>~/.netrc</code> avec le minimum de permissions <code>chmod 0600 ~/.netrc</code> et contenant :</p>
<pre><code class="ini"><span class="na">machine pypi.monserveur.com</span>
<span class="na">login {nom}</span>
<span class="na">password {mdp}</span></code></pre>
<p>Pipenv va chercher automatiquement les paquets dans les deux index PyPi. Pour éviter tout conflit, il est possible d’indiquer l’index PyPi concerné :</p>
<pre><code class="css"><span class="nt">pipenv</span> <span class="nt">install</span> <span class="nt">cool</span> <span class="nt">--index</span> <span class="nt">https</span><span class="o">://</span><span class="nt">pypi</span><span class="p">.</span><span class="nc">monserveur</span><span class="p">.</span><span class="nc">com</span><span class="o">/</span><span class="nt">simple</span></code></pre>
<p>Ce qui rajoute dans le fichier <code>Pipfile</code> :</p>
<pre><code class="ini"><span class="k">[packages]</span>
<span class="na">cool</span> <span class="o">=</span> <span class="s">{index = "https://pypi.monserveur.com/simple",version = "*"}</span></code></pre>
<p>😘✨ Merci aux contributrices et contributeurs de <code>pipenv</code> (dont environ <a href="https://github.com/pypa/pipenv/graphs/contributors">300 personnes listées sur GitHub</a>).</p>
<h2 id="toc-dockeriser-python">Dockeriser Python</h2>
<h3 id="toc-docker-avec-pip">Docker avec <code>pip</code>
</h3>
<p>Prenons un projet Python très simple ayant <code>dateutil</code> comme seule dépendance pour l’exécution <em>(runtime)</em> :</p>
<pre><code>├── mon_app/
│ ├── mon_script.py
│ └── ...
│
├── Dockerfile
│ ║
│ ╚═╡ FROM python:3.7-slim
│ │ COPY requirements.txt /tmp
│ │ RUN pip3 install -r /tmp/requirements.txt
│ │ COPY mon_app /
│ │ CMD ["python3", "/mon_app/mon_script.py"]
│
├── requirements.txt
│ ║
│ ╚═╡ dateutil
└── requirements-dev.txt
║
╚═╡ black
</code></pre>
<p>La première ligne de notre <code>Dockerfile</code> est <code>FROM python:3.7-slim</code> ce qui est un raccourci vers <code>FROM python:3.7.4-slim-buster</code> qui référence un <a href="https://github.com/docker-library/python/blob/master/3.7/buster/slim/Dockerfile">autre Dockerfile sur GitHub</a>. L'image <code>python:3.7-slim</code> correspond à la plus petite image nécessaire pour faire tourner la dernière version Python 3.7 fournie par <a href="https://wiki.debian.org/fr/DebianStable">Debian stable</a> (Buster).</p>
<p>L’ordre des commandes du Dockerfile est important. On commence par ce qui a le moins de probabilité de changer afin de permettre à <code>docker</code> d’utiliser son cache et éviter de perdre du temps à exécuter à nouveau toutes les commandes. Ici, dans notre exemple, changer le contenu de <code>mon_script.py</code> va permettre à <code>docker</code> de réutiliser son cache pour les trois premières lignes du fichier <code>Dockerfile</code>. Si la ligne <code>COPY mon_app /mon_app</code> avait été la toute première, <code>docker</code> aurait alors du ré-exécuter toutes les commandes du <code>Dockerfile</code> pour chaque changement du fichier <code>mon_script.py</code>. Ceci est la raison pour laquelle gérer ses dépendances Python dans le fichier <code>setup.py</code> (<code>install_requires</code>) n’est pas une bonne idée comme <a href="https://pythonspeed.com/articles/pipenv-docker/">expliqué</a> par Itamar Turner-Trauring (en anglais).</p>
<p>Séparer les dépendances pour la production (<code>requirements.txt</code>) est pour le développement (<code>requirements-dev.txt</code>) est une bonne pratique afin d’augmenter la sécurité de notre script (moins de surface d’attaque) et de réduire la taille de notre image Docker.</p>
<p>Le souci de notre fichier <code>requirements.txt</code> est l’absence des versions des dépendances. Nous risquons d’avoir une régression due à une nouvelle version d’une dépendance sans s’en apercevoir. Une solution avec <code>pip</code> est d’utiliser <code>pip-compile</code> pour générer un fichier <code>requirements-lock.txt</code> et de versionner celui-ci avec le code source:</p>
<pre><code class="sh">pip-compile requirements.txt > requirements-lock.txt</code></pre>
<h3 id="toc-docker-avec-pipenv">Docker avec <code>pipenv</code>
</h3>
<p>Migrons notre projet simpliste vers <code>pipenv</code> :</p>
<pre><code>├── mon_app/
│ ├── mon_script.py
│ └── ...
│
├── Dockerfile
│ ║
│ ╚═╡ FROM python:3.7-slim
│ │ RUN pip3 install pipenv
│ │ COPY Pipfile* /tmp
│ │ RUN cd /tmp && pipenv lock --requirements > requirements.txt
│ │ RUN pip3 install -r /tmp/requirements.txt
│ │ RUN pip3 uninstall pipenv
│ │ COPY mon_app /
│ │ CMD ["python3", "/mon_app/mon_script.py"]
│
├── Pipfile
│ ║
│ ╚═╡ [[source]]
│ │ url = "https://pypi.python.org/simple"
│ │ verify_ssl = true
│ │
│ │ [packages]
│ │ dateutil = "*"
│ │
│ │ [dev-packages]
│ │ black = "*"
│ │
│ │ [requires]
│ │ python_version = "3.7"
│
└── Pipfile.lock
</code></pre>
<p>La commande <code>pip</code> ne sait pas installer des modules Python à partir d’un fichier <code>Pipfile</code>, et nous devons installer <code>pipenv</code>. Une demande pour avoir <code>pip install --pipfile Pipfile</code> est en cours de <a href="https://github.com/pypa/pip/issues/6925">discussion sur GitHub</a>.</p>
<p>Une alternative est de lancer l’application conteneurisée dans l’environnement virtualisé avec ce Dockerfile :</p>
<pre><code class="dockerfile"><span class="k">FROM</span><span class="s"> python:3.7-slim</span>
<span class="k">RUN</span> pip3 install pipenv
MKDIR /mon_projet
COPY Pipfile* /mon_projet
<span class="k">WORKDIR</span><span class="s"> /mon_projet</span>
<span class="k">RUN</span> pipenv sync
COPY mon_app /mon_projet
<span class="k">CMD</span><span class="s"> ["pipenv", "run", "./mon_app/mon_script.py"]</span></code></pre>
<h2 id="toc-remerciements">Remerciements</h2>
<p>Merci à tous les contributeurs de cette dépêche, et notamment à <a href="http://sametmax.com">Sam & Max</a>, <a href="//linuxfr.org/users/lolop">lolop</a> et <a href="//linuxfr.org/users/ysabeau">Ysabeau</a>. Sam m’autorise à copier les articles du site <a href="http://sametmax.com">http://sametmax.com</a> 🤩 📄📝 lolop s’est beaucoup investit dans l’organisation de la dépêche précédente et cette dépêche 🤩 🎛🎚 et Ysabeau a initié les illustrations de cette série de dépêches 🤩 🎨🖌. </p>
<p>Extrait du courrier de Sam :</p>
<blockquote>
<p>Bonjour Oliver,</p>
<p>Je te donne explicitement l’autorisation de copier, modifier et publier les articles, partiellement, ou en totalité, et de mettre le résultat sous licence CC0. Cette autorisation s’étend à l’écriture de posts inspirés d’articles du blog par Sam ou Max, avec ou sans reprise de contenu, et te laisse libre de piocher dans le texte, les codes et contenus graphiques que nous avons produit, mais ne peut concerner les articles invités ou les images piochées sur le net sur lesquels nous n’avons pas les droits. Les articles invités peuvent néanmoins être repris sous CC-BY, bien entendu.</p>
<p>[…]</p>
</blockquote>
<h2 id="toc-licence">Licence</h2>
<p>Cette dépêche est publiée sous <a href="https://fr.m.wikipedia.org/wiki/Licence_CC0">licence CC0</a> (sous domaine publique dans les pays où cela est possible) pour permettre de la recopier, modifier, réutiliser et republier sans obligation de citer ses auteurs. Sauf la loi de certains pays, comme la France, qui oblige quand même à citer les auteurs. Au moins, cela montre l’intention des auteurs à autoriser le plagiat.</p>
<h2 id="toc-tes-astuces">Tes astuces ?</h2>
<p>N’hésite pas à partager tes expériences, tes astuces et tes interrogations.</p>
<p>Et n’oublie pas de nous donner un coup de main pour les autres dépêches sur Python.</p>
</div><div><a href="https://linuxfr.org/news/python-partie-8-pipenv.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118210/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/python-partie-8-pipenv#comments">ouvrir dans le navigateur</a>
</p>
Philippe FOliverYsabeau 🧶gusterhackBenoît Sibaudtisaachttps://linuxfr.org/nodes/118210/comments.atomtag:linuxfr.org,2005:News/395002021-05-09T09:16:00+02:002021-05-09T09:16:00+02:00Python — partie 5 — Nix (et Guix)Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Dans les précédentes dépêches, nous avons discuté de la <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-1">popularité de Python</a>, la <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-2">fin de la maintenance de Python 2</a>, les <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets">différentes variantes</a> de Python, comment les faire <a href="//linuxfr.org/news/python-pour-la-rentree-2019-partie-4-py-pyenv">cohabiter avec Py et Pipenv</a>…</p>
<p>Dans la continuité de la précédente dépêche, cette dépêche présente une autre approche pour faire cohabiter différentes versions de Python sur un même ordinateur : <a href="https://fr.wikipedia.org/wiki/Nix">Nix</a>. 🚀 🐍 💫 <a href="https://github.com/olibre/GreatPractices/tree/master/python"><img src="//img.linuxfr.org/img/687474703a2f2f6f6c696272652e6769746875622e696f2f4772656174546970732f707974686f6e2f6e6577732f7061727469652d352e706e67/partie-5.png" alt='Le logo de Python entouré de petites icônes symbolisant la variété des domaines où s’applique Python, et à droite, un joyeux barbu se tient derrière un écran d’ordinateur qui affiche « partie = 5, "Conda Docker" \n print(partie) »' title="Source : http://olibre.github.io/GreatTips/python/news/partie-5.png"></a></p>
</div><ul><li>lien nᵒ 1 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-1" hreflang="fr" href="https://linuxfr.org/redirect/108432">Python ― partie 1 ― Popularité</a></li><li>lien nᵒ 2 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-2" hreflang="fr" href="https://linuxfr.org/redirect/108433">Python ― partie 2 ― Python 2</a></li><li>lien nᵒ 3 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets" hreflang="fr" href="https://linuxfr.org/redirect/108434">Python ― partie 3 ― Installation de Python et de paquets</a></li><li>lien nᵒ 4 : <a title="https://linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv" hreflang="fr" href="https://linuxfr.org/redirect/108435">Python ― partie 4 ― Py Pyenv</a></li><li>lien nᵒ 5 : <a title="https://linuxfr.org/news/python-pour-noel-202x-partie-7-environnements-virtuels" hreflang="fr" href="https://linuxfr.org/redirect/108436">Python — partie 7 — Environnements virtuels</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#toc-gestionnaires-de-paquets-%C3%A9volu%C3%A9s-nix-guix">Gestionnaires de paquets évolués (Nix, Guix)</a><ul>
<li>
<a href="#toc-avec-nix">Avec Nix</a><ul>
<li><a href="#toc-cr%C3%A9er-un-environnement-python-simple">Créer un environnement Python simple</a></li>
<li><a href="#toc-empaqueter-un-projet-python-avec-toutes-ses-d%C3%A9pendances">Empaqueter un projet Python, avec toutes ses dépendances</a></li>
<li><a href="#toc-modifier-lenvironnement-python-standard">Modifier l’environnement Python standard</a></li>
</ul>
</li>
<li><a href="#toc-avec-guix">Avec Guix</a></li>
</ul>
</li>
<li><a href="#toc-remerciements">Remerciements</a></li>
<li><a href="#toc-licence">Licence</a></li>
<li><a href="#toc-tes-astuces">Tes astuces ?</a></li>
</ul>
<h2 id="toc-gestionnaires-de-paquets-évolués-nix-guix">Gestionnaires de paquets évolués (Nix, Guix)</h2>
<p><a href="https://fr.wikipedia.org/wiki/Nix">Nix</a> et <a href="https://fr.wikipedia.org/wiki/GNU_Guix">Guix</a> sont des gestionnaires de paquets généralistes mais très pratiques pour développer en Python. L’idée de base est d’appliquer les principes de la programmation fonctionnelle à la gestion de paquets. Ainsi, un paquet est vu comme une fonction qui prend l’environnement courant et le code source d’un logiciel, et retourne un nouvel environnement où le nouveau logiciel est disponible. Cette approche simpifie la reproduction, la réutilisation et l’isolation des environnements logiciels créés. </p>
<h3 id="toc-avec-nix">Avec Nix</h3>
<h4 id="toc-créer-un-environnement-python-simple">Créer un environnement Python simple</h4>
<p>Nix permet de créer des environnements temporaires. Par exemple, la commande suivante lance l’interpréteur Python en incluant les modules Numpy et Pandas :</p>
<pre><code>$ nix-shell -p 'python3.withPackages(ps: with ps; [ numpy pandas ])' --run python
Python 3.7.5 (default, Oct 14 2019, 23:08:55)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
</code></pre>
<p>Ici, Python et les modules Numpy et Pandas sont fournis par le dépôt de paquets associé à Nix. Le dépôt standard <code>nixpkgs</code> propose une version de Python 3 et une version de Python 2, avec les modules correspondants. Il est également possible d’utiliser son propre dépôt personnalisé ou de personnaliser spécifiquement quelques paquets du dépôt standard (comme expliqué dans la suite de cette dépêche).</p>
<p>Lorsqu’on travaille sur un projet particulier, on préfère généralement ajouter un fichier <code>shell.nix</code>, qui décrit l’environnement à créer pour le projet :</p>
<pre><code class="nix"><span class="k">with</span> <span class="nb">import</span> <span class="l"><nixpkgs></span> <span class="p">{};</span>
<span class="p">(</span>python3<span class="o">.</span>withPackages <span class="p">(</span>ps<span class="p">:</span> <span class="k">with</span> ps<span class="p">;</span> <span class="p">[</span>
numpy
pandas
<span class="p">]))</span><span class="o">.</span>env</code></pre>
<p>Une fois ce fichier créé, il suffit de lancer la commande <code>nix-shell</code> pour créer l’environnement correspondant.</p>
<pre><code>$ nix-shell --run python
Python 3.7.5 (default, Oct 14 2019, 23:08:55)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
</code></pre>
<p>Ce simple fichier <code>shell.nix</code> est déjà très pratique car il permet de définir de façon précise et homogène l’environnement logiciel d’un projet Python. De plus, les dépendances sont mutualisées : si un autre projet nécessite Numpy, ce sont exactement les mêmes fichiers Numpy qui seront utilisés. Enfin, Nix permet de gérer d’autres types de paquets que Python et de personnaliser finement l’environnement, si besoin.</p>
<h4 id="toc-empaqueter-un-projet-python-avec-toutes-ses-dépendances">Empaqueter un projet Python, avec toutes ses dépendances</h4>
<p>Pour construire un projet Python, on écrit généralement un fichier <code>setup.py</code>. Nix possède une fonction <code>buildPythonPackage</code> pour créer un paquet Nix à partir d’un fichier <code>setup.py</code>. L’intérêt d’utiliser Nix en plus du fichier Python est que l’on peut définir précisément toutes les dépendances (Python ou non) et intégrer notre projet au système Nix (et donc de pouvoir le réutiliser directement dans un autre projet).</p>
<p>Par exemple, imaginons que nous avons un projet Python "myapp" qui utilise le paquet Python Numpy, le paquet système zlib et une bibliothèque interne C++ "mylibcpp" (elle-même empaquetée via Nix). Alors, on peut empaqueter notre projet avec le fichier <code>default.nix</code> suivant :</p>
<pre><code class="nix"><span class="p">{</span> pkgs <span class="o">?</span> <span class="nb">import</span> <span class="l"><nixpkgs></span> <span class="p">{}</span> <span class="p">}:</span>
<span class="k">let</span>
<span class="ss">mylibcpp =</span> pkgs<span class="o">.</span>callPackage <span class="o">.</span><span class="l">/mylibcpp/default.nix</span> <span class="p">{};</span>
<span class="k">in</span>
pkgs<span class="o">.</span>python3Packages<span class="o">.</span>buildPythonPackage <span class="p">{</span>
<span class="ss">name =</span> <span class="s2">"myapp"</span><span class="p">;</span>
<span class="ss">src =</span> <span class="o">.</span><span class="l">/.</span><span class="p">;</span>
<span class="ss">propagatedBuildInputs =</span> <span class="p">[</span>
pkgs<span class="o">.</span>python3Packages<span class="o">.</span>numpy <span class="c1"># paquet python</span>
pkgs<span class="o">.</span>zlib <span class="c1"># paquet non python</span>
mylibcpp <span class="c1"># paquet perso</span>
<span class="p">];</span>
<span class="p">}</span></code></pre>
<p>Ce fichier <code>default.nix</code> nous permet de :</p>
<ul>
<li>lancer un <code>nix-shell</code> et avoir toutes les dépendances nécessaires pour travailler sur notre projet Python;</li>
<li>lancer un <code>nix-build</code> pour construire et exécuter une version finale de notre projet;</li>
<li>lancer un <code>nix-env -i -f .</code> pour installer notre projet sur le système, comme n’importe quel autre paquet de la logithèque de Nix;</li>
<li>réutiliser notre projet dans un autre projet utilisant Nix;</li>
<li>et même, de soumettre notre projet pour une intégration dans la logithèque de Nix.</li>
</ul>
<h4 id="toc-modifier-lenvironnement-python-standard">Modifier l’environnement Python standard</h4>
<p>Nix permet de configurer complètement l’environnement logiciel, notamment d’ajouter des paquets Python supplémentaires. Par exemple, si on veut récupérer le paquet Cma sur Pypi et l’intégrer à l’environnement, on peut écrire le fichier <code>shell.nix</code> suivant :</p>
<pre><code class="nix"><span class="k">with</span> <span class="nb">import</span> <span class="l"><nixpkgs></span> <span class="p">{};</span>
<span class="p">(</span> <span class="k">let</span>
<span class="ss">cma =</span> python3Packages<span class="o">.</span>buildPythonPackage <span class="k">rec</span> <span class="p">{</span>
<span class="ss">pname =</span> <span class="s2">"cma"</span><span class="p">;</span>
<span class="ss">version =</span> <span class="s2">"2.7.0"</span><span class="p">;</span>
<span class="ss">src =</span> python3Packages<span class="o">.</span>fetchPypi <span class="p">{</span>
<span class="k">inherit</span> pname version<span class="p">;</span>
<span class="ss">sha256 =</span> <span class="s2">"0nfdq7qznfqcnqwbk04812xiddq8azzbvdj2pa2lvgzxbr00sqzl"</span><span class="p">;</span>
<span class="p">};</span>
<span class="ss">propagatedBuildInputs =</span> <span class="p">[</span>
python3Packages<span class="o">.</span>numpy
<span class="p">];</span>
<span class="ss">doCheck =</span> <span class="no">false</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">in</span> python3<span class="o">.</span>withPackages <span class="p">(</span>ps<span class="p">:</span> <span class="p">[</span> cma <span class="p">])</span>
<span class="p">)</span><span class="o">.</span>env</code></pre>
<p>Il est également possible de redéfinir l’environnement courant. Par exemple, le fichier <code>shell.nix</code> suivant fixe Numpy à la version 1.16.4 et utilise cette version dans le nouvel environnement, notamment pour le paquet Cma, également ajouté.</p>
<pre><code class="nix"><span class="k">with</span> <span class="nb">import</span> <span class="l"><nixpkgs></span> <span class="p">{};</span>
<span class="p">(</span><span class="k">let</span>
<span class="ss">python =</span> <span class="k">let</span>
<span class="ss">packageOverrides =</span> self<span class="p">:</span> super<span class="p">:</span> <span class="p">{</span>
<span class="ss">numpy =</span> self<span class="o">.</span>buildPythonPackage <span class="k">rec</span> <span class="p">{</span>
<span class="ss">pname =</span> <span class="s2">"numpy"</span><span class="p">;</span>
<span class="ss">version =</span> <span class="s2">"1.16.4"</span><span class="p">;</span>
<span class="ss">src =</span> fetchTarball <span class="p">{</span>
<span class="ss">url =</span> <span class="s2">"https://github.com/numpy/numpy/releases/download/v</span><span class="si">${</span>version<span class="si">}</span><span class="s2">/numpy-</span><span class="si">${</span>version<span class="si">}</span><span class="s2">.tar.gz"</span><span class="p">;</span>
<span class="ss">sha256 =</span> <span class="s2">"1wparj9r5cnh9290s2ikp28yvhx95mainnnlxjgw5p6mppk5zkix"</span><span class="p">;</span>
<span class="p">};</span>
<span class="ss">doCheck =</span> <span class="no">false</span><span class="p">;</span>
<span class="p">};</span>
<span class="ss">cma =</span> self<span class="o">.</span>buildPythonPackage <span class="k">rec</span> <span class="p">{</span>
<span class="ss">pname =</span> <span class="s2">"cma"</span><span class="p">;</span>
<span class="ss">version =</span> <span class="s2">"2.7.0"</span><span class="p">;</span>
<span class="ss">src =</span> self<span class="o">.</span>fetchPypi <span class="p">{</span>
<span class="k">inherit</span> pname version<span class="p">;</span>
<span class="ss">sha256 =</span> <span class="s2">"0nfdq7qznfqcnqwbk04812xiddq8azzbvdj2pa2lvgzxbr00sqzl"</span><span class="p">;</span>
<span class="p">};</span>
<span class="ss">propagatedBuildInputs =</span> <span class="p">[</span>
self<span class="o">.</span>numpy
<span class="p">];</span>
<span class="ss">doCheck =</span> <span class="no">false</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">};</span>
<span class="k">in</span> pkgs<span class="o">.</span>python3<span class="o">.</span>override <span class="p">{</span>
<span class="k">inherit</span> packageOverrides<span class="p">;</span>
<span class="ss">self =</span> python<span class="p">;</span>
<span class="p">};</span>
<span class="k">in</span> python<span class="o">.</span>withPackages<span class="p">(</span>ps<span class="p">:</span> <span class="p">[</span> ps<span class="o">.</span>cma <span class="p">]))</span><span class="o">.</span>env</code></pre>
<h3 id="toc-avec-guix">Avec Guix</h3>
<p>Le gestionnaire de paquets <a href="https://fr.wikipedia.org/wiki/GNU_Guix">GNU Guix</a>, inspiré de Nix, offre une solution similaire avec la commande <a href="https://guix.gnu.org/manual/en/html_node/Invoking-guix-environment.html"><code>guix environment</code></a>.</p>
<h2 id="toc-remerciements">Remerciements</h2>
<p>Merci à tou⋅te⋅s les contribut⋅rices⋅eurs de cette dépêche, et notamment à <a href="//linuxfr.org/users/oliver">Oliver</a> pour l’avoir initiée. 👏 Continuons à participer aux prochaines dépêches. 👍</p>
<h2 id="toc-licence">Licence</h2>
<p>Cette dépêche est publiée sous <a href="https://fr.m.wikipedia.org/wiki/Licence_CC0">licence CC0</a> (dans le domaine public dans les pays où c’est possible) pour permettre de la recopier, modifier, réutiliser et republier sans obligation de citer ses auteurs quelle que soit la loi de certains pays comme la France, qui oblige quand même à citer les auteurs. Au moins, cela montre l’intention des auteurs à autoriser le plagiat.</p>
<h2 id="toc-tes-astuces">Tes astuces ?</h2>
<p>N’hésite pas à partager tes expériences, tes astuces et tes interrogations.</p>
<p>Donne-nous un coup de main pour la rédaction des dépêches à venir.</p>
</div><div><a href="https://linuxfr.org/news/python-partie-5-nix-et-guix.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118405/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/python-partie-5-nix-et-guix#comments">ouvrir dans le navigateur</a>
</p>
nokomprendoOliverYsabeau 🧶palm123tisaacgusterhackhttps://linuxfr.org/nodes/118405/comments.atomtag:linuxfr.org,2005:News/394392021-05-06T13:11:15+02:002021-05-07T18:09:11+02:00Python — partie 7 — Environnements virtuelsLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Cette septième dépêche présente les environnements virtuels Python et ses alternatives comme la conteneurisation, le tout avec plein d’astuces et de conseils pour bien s’en sortir. 🚀 🐍</p>
<p><a href="https://github.com/olibre/GreatPractices/tree/master/python"><img src="//img.linuxfr.org/img/687474703a2f2f6f6c696272652e6769746875622e696f2f4772656174546970732f707974686f6e2f6e6577732f7061727469652d372e706e67/partie-7.png" alt='Le logo de Python est entouré de petites icônes symbolisant la variété des domaines où s’applique Python, et, à droite, un joyeux barbu se tient derrière un écran d’ordinateur qui affiche « partie = 7, "Env. Virtuels" \n print(partie) »' title="Source : http://olibre.github.io/GreatTips/python/news/partie-7.png"></a></p>
</div><ul><li>lien nᵒ 1 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-1" hreflang="fr" href="https://linuxfr.org/redirect/104843">Python — partie 1 ― Popularité de Python</a></li><li>lien nᵒ 2 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-2" hreflang="fr" href="https://linuxfr.org/redirect/104844">Python — partie 2 ― Python 2</a></li><li>lien nᵒ 3 : <a title="https://linuxfr.org/news/python-pour-la-rentree-2019-partie-3-installation-de-python-et-de-paquets" hreflang="fr" href="https://linuxfr.org/redirect/104845">Python ―Partie 3 ― Installation</a></li><li>lien nᵒ 4 : <a title="https://linuxfr.org/users/oliver_h/journaux/python-pour-la-rentree-2019-hors-serie-python-revient-dans-la-course-face-a-node-js-8fa9db9e-c045-4136-8618-9044e69f56e0" hreflang="fr" href="https://linuxfr.org/redirect/104846">Hors série : Python revient dans la course face à JS & TS</a></li><li>lien nᵒ 5 : <a title="https://linuxfr.org/news/python-pour-noel-2019-partie-4-py-pyenv" hreflang="fr" href="https://linuxfr.org/redirect/104923">Python — partie 4 ― Py Pyenv</a></li><li>lien nᵒ 6 : <a title="https://linuxfr.org/news/python-pour-noel-202x-partie-5-nix-et-guix" hreflang="fr" href="https://linuxfr.org/redirect/104926">Python — partie 5 ― Nix (et Guix)</a></li><li>lien nᵒ 7 : <a title="https://lwn.net/Articles/757354/" hreflang="en" href="https://linuxfr.org/redirect/106546">Critique des environments virtuels</a></li><li>lien nᵒ 8 : <a title="http://sametmax.com/les-environnement-virtuels-python-virtualenv-et-virtualenvwrapper/" hreflang="fr" href="https://linuxfr.org/redirect/106548">Sam&Max - Les environnements virtuels Python : venv, virtualenv et virtualenvwrapper</a></li><li>lien nᵒ 9 : <a title="http://sametmax.com/pipenv-solution-moderne-pour-remplacer-pip-et-virtualenv/" hreflang="fr" href="https://linuxfr.org/redirect/106549">Sam&Max - pipenv, solution moderne pour remplacer pip et virtualenv</a></li><li>lien nᵒ 10 : <a title="http://sametmax.com/pipenv-solution-moderne-pour-remplacer-pip-et-virtualenv/" hreflang="fr" href="https://linuxfr.org/redirect/106550">Sam&Max - Mieux que Python virtualenvwrapper : pew</a></li><li>lien nᵒ 11 : <a title="https://linuxfr.org/news/python-partie-6-pip-et-pipx" hreflang="fr" href="https://linuxfr.org/redirect/109669">Python — partie 6 — Pip et Pipx</a></li><li>lien nᵒ 12 : <a title="https://linuxfr.org/news/python-partie-8-pipenv" hreflang="fr" href="https://linuxfr.org/redirect/109670">Python— partie 8 — Pipenv </a></li><li>lien nᵒ 13 : <a title="https://linuxfr.org/news/python-partie-10-formateur-de-code-analyse-statique" hreflang="fr" href="https://linuxfr.org/redirect/109671">Python ― partie 9 ― Formateur de code, analyse statique </a></li><li>lien nᵒ 14 : <a title="https://linuxfr.org/news/python-partie-11-entretiens" hreflang="fr" href="https://linuxfr.org/redirect/109672">Python — partie 10 — Entretiens </a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-environnement-python-virtualis%C3%A9-qu%C3%A9saco">Environnement Python virtualisé, quésaco</a></li>
<li><a href="#toc-lanc%C3%AAtre-virtualenv">L'ancêtre <code>virtualenv</code></a></li>
<li><a href="#toc-venv-est-fourni-de-base-avec-python"><code>venv</code> est fourni de base avec Python</a></li>
<li><a href="#toc-virtualenvwrapper-pour-%C3%A9tendre-virtualenv"><code>virtualenvwrapper</code> pour étendre <code>virtualenv</code></a></li>
<li><a href="#toc-pew-d%C3%A9passe-virtualenvwrapper"><code>pew</code> dépasse <code>virtualenvwrapper</code></a></li>
<li><a href="#toc-conda-les-g%C3%A8re-les-aussi"><code>conda</code> les gère les aussi</a></li>
<li>
<a href="#toc-cest-nul-les-environnements-virtuels">C'est nul les environnements virtuels !</a><ul>
<li><a href="#toc-pythonuserbase"><code>PYTHONUSERBASE</code></a></li>
<li><a href="#toc-conteneur">Conteneur</a></li>
<li><a href="#toc-machine-virtuelle">Machine Virtuelle</a></li>
<li><a href="#toc-appvm">AppVM</a></li>
<li><a href="#toc-gestionnaire-de-paquet-de-nouvelle-g%C3%A9n%C3%A9ration">Gestionnaire de paquet de nouvelle génération</a></li>
</ul>
</li>
<li><a href="#toc-remerciements">Remerciements</a></li>
<li><a href="#toc-licence">Licence</a></li>
<li><a href="#toc-tes-astuces">Tes astuces ?</a></li>
</ul>
<h2 id="toc-environnement-python-virtualisé-quésaco">Environnement Python virtualisé, quésaco</h2>
<p>L’idée est d’avoir une installation d'une version de Python et de paquets spécifiques pour chacun de ses projets, avec une maîtrise des versions et des dépendances, et qui n'interfère pas avec l'installation standard des paquets par le système. Et le tout versionné avec le code source de son projet. Ainsi nous avons une installation équivalente pour les différents développeurs et la production.</p>
<p>Quelques outils pour créer des environnements virtuels :</p>
<ul>
<li>
<a href="https://github.com/pypa/virtualenv"><strong><code>virtualenv</code></strong></a>, pour Linux / macOS / Windows, est l’ancêtre, continue d’évoluer et toujours très utilisé (<a href="https://python-guide-pt-br.readthedocs.io/fr/latest/dev/virtualenvs.html">article en français</a>) ;</li>
<li>
<a href="https://docs.python.org/fr/3.9/library/venv.html"><strong><code>venv</code></strong></a> est l'intégration de <em><code>virtualenv</code></em> dans le cœur de Python, mais il est paradoxalement moins utilisé que son ancêtre <em><code>virtualenv</code></em> car <em><code>venv</code></em> est plus basique ;</li>
<li>
<a href="https://virtualenvwrapper.readthedocs.io/en/latest/"><strong><code>virtualenvwrapper</code></strong></a> est un ensemble de commandes bien pratiques pour étendre les possibilités de <code>virtualenv</code> tout en le gardant simple d'usage, avec des portages pour Windows (<a href="https://pypi.org/project/virtualenvwrapper-win/">shell cmd</a> et <a href="https://pypi.org/project/virtualenvwrapper-powershell/2.7.1/">PowerShell</a>) ;</li>
<li>
<a href="https://github.com/berdario/pew"><strong><code>pew</code></strong></a> est aussi complet et plus simple que <code>virtualenvwrapper</code> ;</li>
<li>
<a href="https://github.com/pypa/pipenv"><strong><code>pipenv</code></strong></a> (Linux / macOS / Windows) unifie <code>pip</code> et <code>virtualenv</code> afin de simplifier d'avantage la gestion des environnements virtuels et la gestion des dépendances (versions) et sera abordé dans la prochaine dépêche ;</li>
<li>
<a href="https://en.wikipedia.org/wiki/Conda_(package_manager)"><strong><code>conda</code></strong></a>, pour Linux / macOS / Windows, gère aussi des environnements virtuels, et propose un certain nombre de bibliothèques pré-paquagées en supplément à <code>pip</code>.</li>
</ul>
<p>Nous allons également présenter <a href="https://github.com/pyenv/pyenv"><strong><code>pyenv</code></strong></a> pour l'installation de différentes versions de Python et l'activation rapide d'une des différentes installations de Python pour un même projet.</p>
<h2 id="toc-lancêtre-virtualenv">L'ancêtre <code>virtualenv</code>
</h2>
<p>L'outil historique donc, encore très utilisé et qui sert de base pour certaines surcouches. Il est généralement déjà paquagé dans les distributions Linux, et est donc installable par votre gestionnaire de paquets. Sinon, à installer avec <code>pip</code> :</p>
<pre><code class="sh">/usr/bin/python3 -m pip install --user --upgrade virtualenv</code></pre>
<p>L'environnement virtuel se crée dans un sous-répertoire, nommé <code>.env-truc</code> dans l'exemple ci-dessous :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">cd</span> <span class="o">/</span><span class="n">chemin</span><span class="o">/</span><span class="n">vers</span><span class="o">/</span><span class="n">le</span><span class="o">/</span><span class="n">projet</span>
<span class="err">$</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">virtualenv</span> <span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span>
<span class="n">No</span> <span class="n">LICENSE</span><span class="p">.</span><span class="n">txt</span> <span class="o">/</span> <span class="n">LICENSE</span> <span class="n">found</span> <span class="n">in</span> <span class="n">source</span>
<span class="n">New</span> <span class="n">python</span> <span class="n">executable</span> <span class="n">in</span> <span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">truc</span><span class="o">/</span><span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span>
<span class="n">Also</span> <span class="n">creating</span> <span class="n">executable</span> <span class="n">in</span> <span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">truc</span><span class="o">/</span><span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python</span>
<span class="n">Installing</span> <span class="n">setuptools</span><span class="p">,</span> <span class="n">pip</span><span class="p">,</span> <span class="n">wheel</span><span class="p">...</span>
<span class="n">done</span><span class="p">.</span></code></pre>
<p>ou directement :</p>
<pre><code class="sh">/usr/bin/python3 -m virtualenv /chemin/vers/le/projet/.env-truc</code></pre>
<p>Le répertoire <code>.env-truc</code> contient tout ce qui est nécessaire à l’interpréteur Python avec ses commandes et ses bibliothèques. Cet environnement de travail est indépendant des paquets déjà installés sur la machine :</p>
<pre><code class="c"><span class="err">$</span> <span class="err">$</span> <span class="n">tree</span> <span class="o">-</span><span class="n">d</span> <span class="o">-</span><span class="n">L</span> <span class="mi">4</span> <span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="o">/</span>
<span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="o">/</span>
<span class="err">├──</span> <span class="n">bin</span>
<span class="err">├──</span> <span class="n">include</span>
<span class="err">│ </span> <span class="err">└──</span> <span class="n">python3</span><span class="mf">.7</span><span class="n">m</span> <span class="o">-></span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">include</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="n">m</span>
<span class="err">├──</span> <span class="n">lib</span>
<span class="err">│ </span> <span class="err">└──</span> <span class="n">python3</span><span class="mf">.7</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">collections</span> <span class="o">-></span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib64</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="o">/</span><span class="n">collections</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">config</span><span class="o">-</span><span class="mf">3.7</span><span class="n">m</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span> <span class="o">-></span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib64</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="o">/</span><span class="n">config</span><span class="o">-</span><span class="mf">3.7</span><span class="n">m</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">distutils</span>
<span class="err">│ </span> <span class="err">│ </span> <span class="err">└──</span> <span class="n">__pycache__</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">encodings</span> <span class="o">-></span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib64</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="o">/</span><span class="n">encodings</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">importlib</span> <span class="o">-></span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib64</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="o">/</span><span class="n">importlib</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">lib</span><span class="o">-</span><span class="n">dynload</span> <span class="o">-></span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib64</span><span class="o">/</span><span class="n">python3</span><span class="mf">.7</span><span class="o">/</span><span class="n">lib</span><span class="o">-</span><span class="n">dynload</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">__pycache__</span>
<span class="err">│ </span> <span class="err">└──</span> <span class="n">site</span><span class="o">-</span><span class="n">packages</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">pip</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">pip</span><span class="o">-</span><span class="mf">19.2.3</span><span class="p">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">pkg_resources</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">__pycache__</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">setuptools</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">setuptools</span><span class="o">-</span><span class="mf">41.2.0</span><span class="p">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span>
<span class="err">│ </span> <span class="err">├──</span> <span class="n">wheel</span>
<span class="err">│ </span> <span class="err">└──</span> <span class="n">wheel</span><span class="o">-</span><span class="mf">0.33.6</span><span class="p">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span>
<span class="err">└──</span> <span class="n">lib64</span> <span class="o">-></span> <span class="n">lib</span></code></pre>
<p>Pour entrer dans le shell de cet environnement isolé :</p>
<ul>
<li>Sous GNU/Linux et macOS : <code>source /chemin/vers/le/projet/.env-truc/bin/activate</code>
</li>
<li>Sous Windows : <code>C:\\chemin\vers\le\projet\.env\bin-truc\activate\Scripts\activate.bat</code>
</li>
</ul>
<p>Le prompt indique le nom de l'environnement virtuel:</p>
<pre><code class="c"><span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span> <span class="err">$</span> <span class="n">cd</span> <span class="o">/</span><span class="n">chemin</span><span class="o">/</span><span class="n">vers</span><span class="o">/</span><span class="n">le</span><span class="o">/</span><span class="n">projet</span>
<span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span> <span class="err">$</span> <span class="n">source</span> <span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">activate</span>
<span class="p">(.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="p">)</span> <span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span> <span class="err">$</span> <span class="n">type</span> <span class="o">-</span><span class="n">a</span> <span class="n">python3</span>
<span class="n">python3</span> <span class="n">is</span> <span class="o">/</span><span class="n">chemin</span><span class="o">/</span><span class="n">vers</span><span class="o">/</span><span class="n">le</span><span class="o">/</span><span class="n">projet</span><span class="o">/</span><span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span>
<span class="n">python3</span> <span class="n">is</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span></code></pre>
<p>Installer les dépendances avec <code>pip</code> sans affecter les autres installations Python de la machine. Attention à bien utiliser <code>python3</code> et non pas <code>/bin/usr/python3</code> :</p>
<pre><code class="c"><span class="p">(.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="p">)</span> <span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span> <span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="n">requests</span>
<span class="n">Collecting</span> <span class="n">requests</span>
<span class="n">Downloading</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/requests-2.22.0-py2.py3-none-any.whl (57kB)</span>
<span class="o">|</span><span class="err">████████████████████████████████</span><span class="o">|</span> <span class="mi">61</span><span class="n">kB</span> <span class="mf">5.4</span><span class="n">MB</span><span class="o">/</span><span class="n">s</span>
<span class="n">Collecting</span> <span class="n">urllib3</span><span class="o">!=</span><span class="mf">1.25.0</span><span class="p">,</span><span class="o">!=</span><span class="mf">1.25.1</span><span class="p">,</span><span class="o"><</span><span class="mf">1.26</span><span class="p">,</span><span class="o">>=</span><span class="mf">1.21.1</span> <span class="p">(</span><span class="n">from</span> <span class="n">requests</span><span class="p">)</span>
<span class="n">Downloading</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/e0/da/55f51ea951e1b7c63a579c09dd7db825bb730ec1fe9c0180fc77bfb31448/urllib3-1.25.6-py2.py3-none-any.whl (125kB)</span>
<span class="o">|</span><span class="err">████████████████████████████████</span><span class="o">|</span> <span class="mi">133</span><span class="n">kB</span> <span class="mf">10.0</span><span class="n">MB</span><span class="o">/</span><span class="n">s</span>
<span class="n">Collecting</span> <span class="n">chardet</span><span class="o"><</span><span class="mf">3.1.0</span><span class="p">,</span><span class="o">>=</span><span class="mf">3.0.2</span> <span class="p">(</span><span class="n">from</span> <span class="n">requests</span><span class="p">)</span>
<span class="n">Downloading</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl (133kB)</span>
<span class="o">|</span><span class="err">████████████████████████████████</span><span class="o">|</span> <span class="mi">143</span><span class="n">kB</span> <span class="mf">10.5</span><span class="n">MB</span><span class="o">/</span><span class="n">s</span>
<span class="n">Collecting</span> <span class="n">idna</span><span class="o"><</span><span class="mf">2.9</span><span class="p">,</span><span class="o">>=</span><span class="mf">2.5</span> <span class="p">(</span><span class="n">from</span> <span class="n">requests</span><span class="p">)</span>
<span class="n">Downloading</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl (58kB)</span>
<span class="o">|</span><span class="err">████████████████████████████████</span><span class="o">|</span> <span class="mi">61</span><span class="n">kB</span> <span class="mf">9.4</span><span class="n">MB</span><span class="o">/</span><span class="n">s</span>
<span class="n">Collecting</span> <span class="n">certifi</span><span class="o">>=</span><span class="mf">2017.4.17</span> <span class="p">(</span><span class="n">from</span> <span class="n">requests</span><span class="p">)</span>
<span class="n">Using</span> <span class="n">cached</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/18/b0/8146a4f8dd402f60744fa380bc73ca47303cccf8b9190fd16a827281eac2/certifi-2019.9.11-py2.py3-none-any.whl</span>
<span class="n">Installing</span> <span class="n">collected</span> <span class="nl">packages</span><span class="p">:</span> <span class="n">urllib3</span><span class="p">,</span> <span class="n">chardet</span><span class="p">,</span> <span class="n">idna</span><span class="p">,</span> <span class="n">certifi</span><span class="p">,</span> <span class="n">requests</span>
<span class="n">Successfully</span> <span class="n">installed</span> <span class="n">certifi</span><span class="o">-</span><span class="mf">2019.9.11</span> <span class="n">chardet</span><span class="o">-</span><span class="mf">3.0.4</span> <span class="n">idna</span><span class="o">-</span><span class="mf">2.8</span> <span class="n">requests</span><span class="o">-</span><span class="mf">2.22.0</span> <span class="n">urllib3</span><span class="o">-</span><span class="mf">1.25.6</span></code></pre>
<p>Sortir de l’environnement virtuel :</p>
<pre><code class="c"><span class="p">(.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="p">)</span> <span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span> <span class="err">$</span> <span class="n">deactivate</span>
<span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span> <span class="err">$</span></code></pre>
<p>On peut donc créer un environnement virtuel pour chacun des projets, ou même plusieurs environnements virtuels pour un projet : un pour être iso prod, un pour les tests de nouvelles libs…</p>
<p>Cela prend un peu de place, surtout dans <code>.env-truc/lib64/python3.7/site-packages</code> (les dépendances) car pour le reste ce sont des liens symboliques.</p>
<pre><code>$ du -hsc .env-truc/* | sort -h
0 .env-truc/include
0 .env-truc/lib64
76K .env-truc/bin
16M .env-truc/lib
16M total
</code></pre>
<h2 id="toc-venv-est-fourni-de-base-avec-python">
<code>venv</code> est fourni de base avec Python</h2>
<p>L'outil <code>venv</code> est similaire à son ancêtre et s'utilise avec la même approche. Création :</p>
<pre><code class="c"><span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span><span class="err">$</span> <span class="n">cd</span> <span class="o">/</span><span class="n">chemin</span><span class="o">/</span><span class="n">mon</span><span class="o">/</span><span class="n">projet</span>
<span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span><span class="err">$</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">venv</span> <span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span></code></pre>
<p>Activation :</p>
<pre><code class="c"><span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span><span class="err">$</span> <span class="p">.</span> <span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">activate</span>
<span class="p">(.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="p">)</span> <span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span><span class="err">$</span> <span class="n">type</span> <span class="o">-</span><span class="n">a</span> <span class="n">python3</span>
<span class="n">python3</span> <span class="n">is</span> <span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">truc</span><span class="o">/</span><span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span>
<span class="n">python3</span> <span class="n">is</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span></code></pre>
<p>Installation des dépendances :</p>
<pre><code class="c"><span class="p">(.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="p">)</span> <span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span><span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="n">requests</span>
<span class="n">Collecting</span> <span class="n">requests</span>
<span class="n">Using</span> <span class="n">cached</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/requests-2.22.0-py2.py3-none-any.whl</span>
<span class="n">Collecting</span> <span class="n">chardet</span><span class="o"><</span><span class="mf">3.1.0</span><span class="p">,</span><span class="o">>=</span><span class="mf">3.0.2</span> <span class="p">(</span><span class="n">from</span> <span class="n">requests</span><span class="p">)</span>
<span class="n">Using</span> <span class="n">cached</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl</span>
<span class="n">Collecting</span> <span class="n">certifi</span><span class="o">>=</span><span class="mf">2017.4.17</span> <span class="p">(</span><span class="n">from</span> <span class="n">requests</span><span class="p">)</span>
<span class="n">Using</span> <span class="n">cached</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/18/b0/8146a4f8dd402f60744fa380bc73ca47303cccf8b9190fd16a827281eac2/certifi-2019.9.11-py2.py3-none-any.whl</span>
<span class="n">Collecting</span> <span class="n">idna</span><span class="o"><</span><span class="mf">2.9</span><span class="p">,</span><span class="o">>=</span><span class="mf">2.5</span> <span class="p">(</span><span class="n">from</span> <span class="n">requests</span><span class="p">)</span>
<span class="n">Using</span> <span class="n">cached</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl</span>
<span class="n">Collecting</span> <span class="n">urllib3</span><span class="o">!=</span><span class="mf">1.25.0</span><span class="p">,</span><span class="o">!=</span><span class="mf">1.25.1</span><span class="p">,</span><span class="o"><</span><span class="mf">1.26</span><span class="p">,</span><span class="o">>=</span><span class="mf">1.21.1</span> <span class="p">(</span><span class="n">from</span> <span class="n">requests</span><span class="p">)</span>
<span class="n">Using</span> <span class="n">cached</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//files.pythonhosted.org/packages/e0/da/55f51ea951e1b7c63a579c09dd7db825bb730ec1fe9c0180fc77bfb31448/urllib3-1.25.6-py2.py3-none-any.whl</span>
<span class="n">Installing</span> <span class="n">collected</span> <span class="nl">packages</span><span class="p">:</span> <span class="n">chardet</span><span class="p">,</span> <span class="n">certifi</span><span class="p">,</span> <span class="n">idna</span><span class="p">,</span> <span class="n">urllib3</span><span class="p">,</span> <span class="n">requests</span>
<span class="n">Successfully</span> <span class="n">installed</span> <span class="n">certifi</span><span class="o">-</span><span class="mf">2019.9.11</span> <span class="n">chardet</span><span class="o">-</span><span class="mf">3.0.4</span> <span class="n">idna</span><span class="o">-</span><span class="mf">2.8</span> <span class="n">requests</span><span class="o">-</span><span class="mf">2.22.0</span> <span class="n">urllib3</span><span class="o">-</span><span class="mf">1.25.6</span>
<span class="n">You</span> <span class="n">are</span> <span class="n">using</span> <span class="n">pip</span> <span class="n">version</span> <span class="mf">19.0.3</span><span class="p">,</span> <span class="n">however</span> <span class="n">version</span> <span class="mf">19.2.3</span> <span class="n">is</span> <span class="n">available</span><span class="p">.</span>
<span class="n">You</span> <span class="n">should</span> <span class="n">consider</span> <span class="n">upgrading</span> <span class="n">via</span> <span class="n">the</span> <span class="err">'</span><span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">upgrade</span> <span class="n">pip</span><span class="err">'</span> <span class="n">command</span><span class="p">.</span></code></pre>
<p>Désactivation :</p>
<pre><code class="c"><span class="p">(.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="p">)</span> <span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span><span class="err">$</span> <span class="n">deactivate</span></code></pre>
<p>Espace occupé :</p>
<pre><code class="c"><span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">prompt</span><span class="o">></span><span class="err">$</span> <span class="n">du</span> <span class="o">-</span><span class="n">hsc</span> <span class="p">.</span><span class="n">env</span><span class="o">-</span><span class="n">truc</span><span class="cm">/* | sort -h</span>
<span class="cm">0 .env-truc/include</span>
<span class="cm">0 .env-truc/lib64</span>
<span class="cm">4.0K .env-truc/pyvenv.cfg</span>
<span class="cm">36K .env-truc/bin</span>
<span class="cm">14M .env-truc/lib</span>
<span class="cm">14M total</span></code></pre>
<p>La documentation officielle de la <em>Python Software Foundation</em> est en français : <a href="https://docs.python.org/fr/dev/library/venv.html">https://docs.python.org/fr/dev/library/venv.html</a></p>
<h2 id="toc-virtualenvwrapper-pour-étendre-virtualenv">
<code>virtualenvwrapper</code> pour étendre <code>virtualenv</code>
</h2>
<p>Le projet <a href="https://virtualenvwrapper.readthedocs.io/en/latest/"><code>virtualenvwrapper</code></a> permet de ne plus se soucier du répertoire où se trouvent les environnements virtuels. Ce projet fonctionne et ajoutant des commandes au dessus de la commande <code>virtualenv</code> pour :</p>
<ul>
<li>organiser les environnements virtuels où que l’on se trouve dans l’arborescence des répertoires ;</li>
<li>simplifier la création/copie/suppression des environnements virtuels ;</li>
<li>passer d’un environnement virtuel à un autre (super utile) ;</li>
<li>compléter la ligne de commande, avec touche <code>[Tab]</code>, selon les environnements virtuels disponibles ;</li>
<li>étendre davantage ces commandes avec un système d’extensions <em>(plugins)</em> ;</li>
<li>fournir des crochets <em>(hook)</em> personnalisables.</li>
</ul>
<p>Installation:</p>
<pre><code class="c"><span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">user</span> <span class="o">--</span><span class="n">upgrade</span> <span class="n">virtualenvwrapper</span></code></pre>
<p>Ajouter dans le <code>~/.bashrc</code> (ou autre fichier de configuration équivalent) :</p>
<pre><code class="sh"><span class="nb">export</span> <span class="nv">WORKON_HOME</span><span class="o">=</span>~/.virtualenvs
mkdir -p <span class="nv">$WORKON_HOME</span>
<span class="nb">source</span> ~/.local/bin/virtualenvwrapper.sh</code></pre>
<p>Les commandes suivantes ont le même effet quel que soit le répertoire où l’on se trouve :</p>
<ul>
<li>
<code>mkvirtualenv {nom_env}</code> pour créer un environnement virtuel (avec <code>virtualenv</code>) dans le dossier <code>$WORKON_HOME</code> (<code>~/.virtualenvs</code>) ;</li>
<li>
<code>workon {nom_env}</code> pour activer un environnement virtuel ;</li>
<li>
<code>rmvirtualenv {nom_env}</code> pour supprimer l’environnement virtuel.</li>
</ul>
<p>Les options de <code>mkvirtualenv</code> sont les mêmes que pour la commande <code>virtualenv</code>.</p>
<h2 id="toc-pew-dépasse-virtualenvwrapper">
<code>pew</code> dépasse <code>virtualenvwrapper</code>
</h2>
<p>Installation :</p>
<pre><code class="c"><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">user</span> <span class="o">--</span><span class="n">upgrade</span> <span class="n">pew</span></code></pre>
<p>Par rapport au projet <code>virtualenvwrapper</code>, le nom des commandes <code>pew</code> (tout comme le nom du projet) sont plus courtes et élégantes. Les commandes <code>pew</code> sont aussi plus nombreuses :</p>
<table>
<thead>
<tr>
<th>Équivalence</th>
<th><code>virtualenvwrapper</code></th>
<th><code>pew</code></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>$WORKON_HOME</code> par défaut</td>
<td><code>~/.virtualenvs</code></td>
<td><code>~/local/share/virtualenvs</code></td>
</tr>
<tr>
<td>Lister les env</td>
<td><code>workon</code></td>
<td><code>pew ls</code></td>
</tr>
<tr>
<td>Créer un env</td>
<td><code>mkvirtualenv <nom></code></td>
<td><code>pew new <nom></code></td>
</tr>
<tr>
<td>Supprimer un env</td>
<td><code>rmvirtualenv <nom></code></td>
<td><code>pew rm <nom></code></td>
</tr>
<tr>
<td>Activer un env</td>
<td><code>workon <nom></code></td>
<td><code>pew workon <nom></code></td>
</tr>
<tr>
<td>Aller dans le <code>site-packages</code>
</td>
<td><code>cdsitepackages</code></td>
<td><code>cd $(pew sitepackages_dir)</code></td>
</tr>
<tr>
<td>Exécuter sans activer l'env</td>
<td>(aucune)</td>
<td><code>pew in <nom> <commande></code></td>
</tr>
<tr>
<td>Exécuter dans tous les env</td>
<td>(aucune)</td>
<td><code>pew inall <commande></code></td>
</tr>
<tr>
<td>Dupliquer un env</td>
<td>(aucune)</td>
<td><code>pew cp <nom> <destination></code></td>
</tr>
<tr>
<td>Modifier <code>PYTHONPATH</code>
</td>
<td>(aucune)</td>
<td><code>pew add path</code></td>
</tr>
<tr>
<td>Créer un env temporaire</td>
<td>(aucune)</td>
<td><code>pew mktmpenv</code></td>
</tr>
<tr>
<td>Choisir le dossier par défaut</td>
<td>(aucune)</td>
<td><code>pew setproject path</code></td>
</tr>
</tbody>
</table>
<p> <br>
De plus, <code>pew</code> ne nécessite pas de modifier le <code>.bashrc</code> car <code>pew</code> ouvre un nouveau shell. Donc, la sortie de l'environnement virtuel se fait avec <code>exit</code> ou la combinaison des touches <code>[Ctrl]</code>+<code>[D]</code>.</p>
<p>L'inconvénient de <code>pew</code> est de nécessiter l'exécution de l'interpréteur Python pour chaque commande lancée, ce qui alourdit son utilisation et peut rajouter une latence par rapport à <code>virtualenvwrapper</code>, mais cela reste négligeable pour la plupart des machines modernes. </p>
<h2 id="toc-conda-les-gère-les-aussi">
<code>conda</code> les gère les aussi</h2>
<p>La commande <a href="https://en.wikipedia.org/wiki/Conda_(package_manager)"><code>conda</code></a> d'<em>Anaconda</em> et de <em>Miniconda</em> permet, en plus de la gestion des paquets, de gérer des environnements virtuels Python. On dispose de trois sous-commandes de base :</p>
<ul>
<li>
<code>conda create</code> pour créer un environnement virtuel,</li>
<li>
<code>conda activate</code> pour l'utiliser,</li>
<li>
<code>conda deactivate</code> pour cesser de l'utiliser</li>
</ul>
<p>Exemple :</p>
<pre><code>~$ conda create -n testenv1
Collecting package metadata (current_repodata.json): done
Solving environment: done
## Package Plan ##
environment location: /home/login/.miniconda3/envs/testenv1
Proceed ([y]/n)? y
…
~$ conda activate testenv1
(testenv1) ~$
</code></pre>
<p>Par défaut l'environnement virtuel reprend la version de Python standard du système. Mais il est possible de spécifier une version de Python à utiliser :</p>
<pre><code>~$ conda create -n testenv2 python=3.7
Collecting package metadata (current_repodata.json): done
Solving environment: done
## Package Plan ##
environment location: /home/login/.miniconda3/envs/testenv2
added / updated specs:
- python=3.7
…(plus de choses à installer)…
Proceed ([y]/n)?
…(téléchargement et installation)…
~$ conda activate testenv2
(testenv2) ~$ which python
/home/login/.miniconda3/envs/testenv2/bin/python
(testenv2) ~$ which pip
/home/login/.miniconda3/envs/testenv2/bin/pip
</code></pre>
<p>On remarque qu'une fois l'environnement virtuel activé, la commande <code>pip</code> peut être utilisée directement, de façon saine sans risque de dommages pour votre système.</p>
<h2 id="toc-cest-nul-les-environnements-virtuels">C'est nul les environnements virtuels !</h2>
<p>Des <a href="https://lwn.net/Articles/757354/">experts Pythons</a> trouvent que les environnements virtuels complexifient inutilement l'utilisation de Python et qu'ils dénaturent la philosophie de simplicité. Ces experts réfléchissent avec la PSF pour trouver une solution plus simple en s'inspirant des écosystèmes <a href="https://fr.wikipedia.org/wiki/Ruby">Ruby</a>, <s>Node.js</s> <a href="https://en.wikipedia.org/wiki/Deno_(software)">Deno</a>, <a href="https://fr.wikipedia.org/wiki/Rust_(langage)">Rust</a> (<a href="https://fr.wikipedia.org/wiki/Cargo_(informatique)"><code>cargo</code></a>), <a href="https://fr.wikipedia.org/wiki/Go_(langage)">Go</a>…</p>
<p>En attendant, pour avoir des espaces de développement indépendants entre ses projets, nous avons des alternatives.</p>
<h3 id="toc-pythonuserbase"><code>PYTHONUSERBASE</code></h3>
<p>La façon la plus basique est d'invoquer <code>pip</code> avec la variable d'environnement <code>PYTHONUSERBASE</code>.</p>
<p>Installer les dépendances :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">cd</span> <span class="o">/</span><span class="n">chemin</span><span class="o">/</span><span class="n">mon</span><span class="o">/</span><span class="n">projet</span>
<span class="err">$</span> <span class="n">PYTHONUSERBASE</span><span class="o">=</span><span class="err">$</span><span class="n">PWD</span><span class="o">/</span><span class="p">.</span><span class="n">pip</span><span class="o">-</span><span class="n">env</span>
<span class="err">$</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">user</span> <span class="o">--</span><span class="n">upgrade</span> <span class="n">requests</span></code></pre>
<p>Tester son application :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">cd</span> <span class="o">/</span><span class="n">chemin</span><span class="o">/</span><span class="n">mon</span><span class="o">/</span><span class="n">projet</span>
<span class="err">$</span> <span class="n">PYTHONUSERBASE</span><span class="o">=</span><span class="err">$</span><span class="n">PWD</span><span class="o">/</span><span class="p">.</span><span class="n">pip</span><span class="o">-</span><span class="n">env</span>
<span class="err">$</span> <span class="n">PYTHONPATH</span><span class="o">=</span><span class="err">$</span><span class="n">PWD</span>
<span class="err">$</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">projet</span><span class="o">></span></code></pre>
<p>ou juste un script :</p>
<pre><code class="c"><span class="err">$</span> <span class="n">PYTHONUSERBASE</span><span class="o">=</span><span class="err">$</span><span class="n">PWD</span><span class="o">/</span><span class="p">.</span><span class="n">pip</span><span class="o">-</span><span class="n">env</span>
<span class="err">$</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">python3</span> <span class="o"><</span><span class="n">mon</span><span class="o">-</span><span class="n">script</span><span class="p">.</span><span class="n">py</span><span class="o">></span></code></pre>
<p>Les variables d'environnement peuvent aussi être mises dans un fichier shell et activées avec <code>source /chemin/mon/projet/activate.sh</code>. Mais bon… on réinvente un peu la roue <code>venv</code> !</p>
<pre><code class="c"><span class="n">export</span> <span class="n">PYTHONUSERBASE</span><span class="o">=/</span><span class="n">chemin</span><span class="o">/</span><span class="n">mon</span><span class="o">/</span><span class="n">projet</span><span class="o">/</span><span class="p">.</span><span class="n">pip</span><span class="o">-</span><span class="n">env</span>
<span class="n">export</span> <span class="n">PYTHONPATH</span><span class="o">=/</span><span class="n">chemin</span><span class="o">/</span><span class="n">mon</span><span class="o">/</span><span class="n">projet</span></code></pre>
<h3 id="toc-conteneur">Conteneur</h3>
<p>Les habitué·e·s des conteneurs, comme Docker, ont tendance à se créer une image avec uniquement la version Python de leur choix, puis de travailler dans un shell de ce conteneur pour y installer les dépendances nécessaires.</p>
<p>De toutes façons, dans ces organisations, les applications sont livrées dans des conteneurs. Pourquoi ne pas utiliser le même environnement qu'en production ?</p>
<p>Attention quand même à bien configurer son image pour conserver ses changements. 😁</p>
<h3 id="toc-machine-virtuelle">Machine Virtuelle</h3>
<p>On peut aussi carrément utiliser une machine virtuelle pour isoler un environnement Python. C'est plus lourd qu'un conteneur, mais bon, on faisait comme ça il n'y a pas si longtemps. Sans oublier, que de nombreuses organisations déploient encore leurs applications avec une installation classique. Alors, pourquoi pas utiliser un environnement similaire à celui de la production ?</p>
<p>L'outil <a href="https://fr.wikipedia.org/wiki/Vagrant"><code>vagrant</code></a> peut aider a <em>scripter</em> la création de sa VM. On peut créer plusieurs VMs sur sa machine (en local) et/ou dans les nuages <em>(cloud)</em>…</p>
<h3 id="toc-appvm">AppVM</h3>
<p>L'<a href="https://fr.wikipedia.org/wiki/Qubes_OS#Application_Machines_virtuelles_(AppVM)">Application Machine Virtuelle</a> (AppVM), concept du système d'exploitation Qubes OS, permet d'isoler chaque application.</p>
<h3 id="toc-gestionnaire-de-paquet-de-nouvelle-génération">Gestionnaire de paquet de nouvelle génération</h3>
<p>Le gestionnaire de paquets <a href="https://fr.wikipedia.org/wiki/Nix">Nix</a> est fourni avec la commande <a href="https://nixos.org/nix/manual/#sec-nix-shell"><code>nix-shell</code></a> pour activer un shell en spécifiant les dépendances souhaitées.</p>
<p>Le gestionnaire de paquets <a href="https://fr.wikipedia.org/wiki/GNU_Guix">GNU Guix</a>, inspiré de Nix, offre une solution similaire avec la commande <a href="https://guix.gnu.org/manual/en/html_node/Invoking-guix-environment.html"><code>guix environment</code></a>.</p>
<h2 id="toc-remerciements">Remerciements</h2>
<p>Merci à tous les contributeurs de cette dépêche, et notamment à <a href="//linuxfr.org/users/lolop">lolop</a>, <a href="//linuxfr.org/users/ysabeau">Ysabeau</a> et <a href="http://sametmax.com">Sam & Max</a>. lolop sʼest beaucoup investi dans la rédaction et lʼorganisation des dépêches 🤩, Ysabeau a initié les illustrations de cette série de dépêches 🤩 🎨 🖌 et Sam m'autorise à copier les articles du site <a href="http://sametmax.com%E2%80%AF%F0%9F%A4%A9%E2%80%AF%F0%9F%93%84%E2%80%AF%F0%9F%93%9D">http://sametmax.com 🤩 📄 📝</a>.</p>
<p>Extrait du courrier de Sam :</p>
<blockquote>
<p>Bonjour Oliver,</p>
<p>Je te donne explicitement lʼautorisation de copier, modifier et publier les articles, partiellement, ou en totalité, et de mettre le résultat sous licence CC0. Cette autorisation sʼétend à l'écriture de posts inspirés d'articles du blog par Sam ou Max, avec ou sans reprise de contenu, et te laisse libre de piocher dans le texte, les codes et contenus graphiques que nous avons produit, mais ne peut concerner les articles invités ou les images piochées sur le net sur lesquels nous n'avons pas les droits. Les articles invités peuvent néanmoins être repris sous CC-BY, bien entendu.</p>
<p>[…]</p>
</blockquote>
<h2 id="toc-licence">Licence</h2>
<p>Cette dépêche est publiée sous <a href="https://fr.m.wikipedia.org/wiki/Licence_CC0">licence CC0</a> (sous domaine publique dans les pays où cela est possible) pour permettre de la recopier, modifier, réutiliser et republier sans obligation de citer ses auteurs. Sauf la loi de certains pays, comme la France, qui oblige quand même à citer les auteurs. Au moins, cela montre l'intention des auteurs à autoriser le plagiat.</p>
<h2 id="toc-tes-astuces">Tes astuces ?</h2>
<p>N'hésite pas à partager tes expériences, tes astuces et tes interrogations.</p>
<p>Et n’oublie pas de nous donner un coup de main pour la suite de cette série de dépêches sur Python. Par exemple, <a href="//linuxfr.org/redaction/news/python-pour-noel-202x-partie-9-empaquetage">Python pour Noël 202X— partie 9 — empaquetage</a>.</p>
</div><div><a href="https://linuxfr.org/news/python-partie-7-environnements-virtuels.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118120/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/python-partie-7-environnements-virtuels#comments">ouvrir dans le navigateur</a>
</p>
OliverlolopYsabeau 🧶Axonetisaacbobble bubbletedDi3s3Lgusterhackhttps://linuxfr.org/nodes/118120/comments.atom