Integrate Poetry

This commit is contained in:
Fred Pauchet 2023-12-30 21:13:15 +01:00
parent 8cbaf07339
commit 4dcdfda852
12 changed files with 226 additions and 74 deletions

View File

@ -27,6 +27,7 @@ image::<path>[align="center"]
### Liens croisés
Les liens croisés sont construits avec `<< >>`.
La référence doit être, soit le titre exact de la section mentionnée, soit une référence propriétaire structurée par `[[...]]` (cf. https://docs.asciidoctor.org/asciidoc/latest/sections/custom-ids/).
### Index

182
book/annexes/poetry.adoc Normal file
View File

@ -0,0 +1,182 @@
== Poetry
Cela fait quelques temps que j'entends et vois parler de [Poetry](https://python-poetry.org/) comme remplaçant de `pip` et de `venv`.
Après avoir un peu analysé le bidule, il y a plusieurs points très, très intéressants.
En vrac:
* La publication de paquets sur pypi https://aricodes.net/posts/python-package-from-scratch/
* L'intégration des environnements virtuels
* Du pattern matching sur les dépendances, comme ce que fait [npm](https://www.npmjs.com/)
Un des reproches que l'on peut faire au langage concerne sa versatilité: il est possible de réaliser **beaucoup** de choses, mais celles-ci ne sont pas toujours simples ou directes.
Pour quelqu'un qui débarquererait, la quantité d'options différentes peut paraître rebutante
Je pense notamment aux environnements virtuels: ils sont géniaux à utiliser, mais on est passé par [virtualenv](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwjTlsbnme3wAhWQ2aQKHccQDkwQFjAAegQIBhAD&url=https%3A%2F%2Fpypi.org%2Fproject%2Fvirtualenv%2F&usg=AOvVaw3CZEVr8aqeheSGZiDLuPtr) (l'ancêtre), [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html) (sa version améliorée et plus ergonomique), [venv](https://docs.python.org/fr/3/library/venv.html) (la version intégrée depuis la version 3.3 de l'interpréteur, et la manière recommandée de créer un environnement depuis la 3.5).
image::xkcd/1987.png[align="center"]
=== Boilerplate
Poetry se propose de gérer le projet au travers d'un fichier `pyproject.toml`.
[TOML](https://en.wikipedia.org/wiki/TOML) (du nom de son géniteur, Tom Preston-Werner, légèrement CEO de GitHub à ses heures), se place comme alternative aux formats comme JSON, YAML ou INI.
=== Démarrage d'un nouveau projet
La commande `poetry new <project>` créera une structure par défaut relativement compréhensible:
```bash
$ poetry new django-gecko
$ tree django-gecko/
django-gecko/
├── django_gecko
│   └── __init__.py
├── pyproject.toml
├── README.rst
└── tests
├── __init__.py
└── test_django_gecko.py
2 directories, 5 files
```
Ceci signifie que nous avons directement (et de manière standard):
* Un répertoire `django-gecko`, qui porte le nom de l'application que vous venez de créer
* Un répertoires `tests`, libellé selon les standards de [pytest](https://docs.pytest.org/en/)
* Un fichier README.rst (qui ne contient encore rien)
* Un fichier `pyproject.toml`, qui contient ceci:
```toml
[tool.poetry]
name = "django-gecko"
version = "0.1.0"
description = ""
authors = ["... <...@grimbox.be>"]
[tool.poetry.dependencies]
python = "^3.9"
[tool.poetry.dev-dependencies]
pytest = "^5.2"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
```
==== Initialisation pour un projet existant
La commande `poetry init` permet de générer interactivement les fichiers nécessaires à son intégration dans un projet existant.
=== Dependency management made easy
La manière recommandée pour la gestion des dépendances consiste à les épingler dans un fichier `requirements.txt`, placé à la racine du projet.
Ce fichier reprend, ligne par ligne, chaque dépendance et la version nécessaire.
Cet épinglage est cependant relativement basique, dans la mesure où les opérateurs disponibles sont `==`, `<=` et `>=`.
Poetry propose un épinglage basé sur [SemVer](https://semver.org/lang/fr/).
Les contraintes qui peuvent être appliquées aux dépendances sont [plus touffues](https://python-poetry.org/docs/dependency-specification/#version-constraints) que ce que proposent `pip -r`, avec la présence du curseur `^`, qui *ne modifiera pas le nombre différent de zéro le plus à gauche*:
* `^1.2.3` (où le nombre en question est `1`) pourra proposer une mise à jour jusqu'à la version juste avant la version `2.0.0`
* `^0.2.3` pourra être mise à jour jusqu'à la version juste avant `0.3.0`.
* ...
L'avantage est donc que l'on spécifie une version majeure - mineure - patchée, et que l'on pourra spécifier accepter toute mise à jour jusqu'à la prochaine version majeure - mineure patchée (non incluse 😉).
Une bonne pratique consiste également, tout comme pour npm, à intégrer le fichier de lock (`poetry.lock`) dans le dépôt de sources: de cette manière, seules les dépendances testées (et intégrées) seront considérées sur tous les environnements de déploiement.
Il est alors nécessaire de passer par une action manuelle (`poetry update`) pour mettre à jour le fichier de verrou, et assurer une mise à jour en sécurité (seules les dépendances testées sont prises en compte) et de qualité (tous les environnements utilisent la même version d'une dépendance).
=== Ajout d'une dépendance
L'ajout d'une nouvelle dépendance à un projet se réalise grâce à la commande `poetry add <dep>`:
```bash
$ poetry add django
Using version ^3.2.3 for Django
Updating dependencies
Resolving dependencies... (5.1s)
Writing lock file
Package operations: 8 installs, 1 update, 0 removals
• Installing pyparsing (2.4.7)
• Installing attrs (21.2.0)
• Installing more-itertools (8.8.0)
• Installing packaging (20.9)
• Installing pluggy (0.13.1)
• Installing py (1.10.0)
• Installing wcwidth (0.2.5)
• Updating django (3.2 -> 3.2.3)
• Installing pytest (5.4.3)
```
Elle est ensuite ajoutée à notre fichier `pyproject.toml`:
```toml
[...]
[tool.poetry.dependencies]
python = "^3.9"
Django = "^3.2.3"
[...]
```
Et contrairement à `pip`, pas besoin de savoir s'il faut pointer vers un fichier (`-r`) ou un dépôt VCS (`-e`), puisque Poetry va tout essayer, [dans un certain ordre](https://python-poetry.org/docs/cli/#add).
L'avantage également (et cela m'arrive encore souvent, ce qui fait hurler le runner de Gitlab), c'est qu'il n'est plus nécessaire de penser à épingler la dépendance que l'on vient d'installer parmi les fichiers de requirements, puisqu'elles s'y ajoutent automatiquement grâce à la commande `add`.
## Python packaging made easy
Cette partie dépasse mes compétences et connaissances, dans la mesure où je n'ai jamais rien packagé ni publié sur [pypi.org](pypi.org).
Ce n'est pas l'envie qui manque, mais les idées et la nécessité 😉.
Ceci dit, Poetry propose un ensemble de règles et une préconfiguration qui (doivent) énormément facilite(r) la mise à disposition de librairies sur Pypi - et rien que ça, devrait ouvrir une partie de l'écosystème.
Les chapitres 7 et 8 de [Expert Python Programming - Third Edtion](https://www.packtpub.com/product/expert-python-programming-third-edition/9781789808896), écrit par Michal Jaworski et Tarek Ziadé en parlent très bien:
> Python packaging can be a bit overwhelming at first.
> The main reason for that is the confusion about proper tools for creating Python packages.
> Anyway, once you create your first package, you will se that this is as hard as it looks.
> Also, knowing proper, state-of-the-art packaging helps a lot.
En gros, c'est ardu-au-début-mais-plus-trop-après.
Et c'est heureusement suivi et documenté par la PyPA (*[Python Packaging Authority](https://github.com/pypa)*).
Les étapes sont les suivantes:
1. Utiliser setuptools pour définir les projets et créer les distributions sources,
2. Utiliser **wheels** pour créer les paquets,
3. Passer par **twine** pour envoyer ces paquets vers PyPI
4. Définir un ensemble d'actions (voire, de plugins nécessaires - lien avec le VCS, etc.) dans le fichier `setup.py`, et définir les propriétés du projet ou de la librairie dans le fichier `setup.cfg`.
Avec Poetry, deux commandes suffisent (théoriquement - puisque je n'ai pas essayé 🤪): `poetry build` et `poetry publish`:
```bash
$ poetry build
Building geco (0.1.0)
- Building sdist
- Built geco-0.1.0.tar.gz
- Building wheel
- Built geco-0.1.0-py3-none-any.whl
$ tree dist/
dist/
├── geco-0.1.0-py3-none-any.whl
└── geco-0.1.0.tar.gz
0 directories, 2 files
```
Ce qui est quand même 'achement plus simple que d'appréhender tout un écosystème.
=== En conclusion
Il y a plein de bonnes idées, qui demandent en même temps quelques changements d'habitudes (comme tout nouveau produit, vous me direz).
En parallèle, la lisibilité globale de la sortie console est franchement améliorée et beaucoup plus claire/agréable à lire et parcourir.
Le fait aussi que la gestion des dépendances soit simplifiée est agréable aussi.
Ce qui est peut-être un chouia dommage, c'est que cela cela redéfinisse un nouveau standard et ne se base pas ou n'améliore pas les fichiers de dépendances actuels.
Mais le plus important concerne l'orientation que prend le langage et l'écosystème vers l'utilisation de standards, et peut-être qu'abandonner des pratiques non-suivies par l'industrie constituera un gain pour la suite.

View File

@ -90,20 +90,13 @@ sécurisée et plombée de capteurs en tout genre qui faciliteront
=== Terminal
_A priori_, les IDE proposés ci-dessus fournissent par défaut ou _via_
des greffons un terminal intégré.
Ceci dit, disposer dun terminal
séparé facilite parfois certaines tâches.
_A priori_, les IDE proposés ci-dessus fournissent par défaut ou _via_ des greffons un terminal intégré.
Ceci dit, disposer dun terminal séparé facilite parfois certaines tâches.
A nouveau, si vous manquez didées :
. Soit vous utilisez celui qui intégré à VSCodium et qui fera
suffisament bien son travail
. Si vous êtes sous Windows, téléchargez une copie de
https://cmder.net/[Cmder]. Il nest pas le plus rapide, mais propose une
intégration des outils Unix communs (`ls`, `pwd`, `grep`, `ssh`, `git`,
...) sans trop se fouler.
. Pour tout autre système, vous devriez disposer en natif de ce quil
faut.
. Soit vous utilisez celui qui intégré à VSCodium et qui fera suffisament bien son travail
. Si vous êtes sous Windows, téléchargez une copie de https://cmder.net/[Cmder] ou de https://aka.ms/terminal[Windows Terminal]. Les deux proposent une intégration des outils Unix communs (`ls`, `pwd`, `grep`, `ssh`, `git`, ...) sans aucun effort.
. Pour tout autre système, vous devriez disposer en natif de ce quil faut.
image::environment/terminal.png[align="center"]

View File

@ -61,6 +61,7 @@ En plus de cela, chaque développeur a une copie de lapplication qui fonction
Comme lexplique Eran Messeri, ingénieur dans le groupe Google Developer Infrastructure: "_Un des avantages dutiliser un dépôt unique de sources, est quil permet un accès facile et rapide à la forme la plus à jour du code, sans aucun besoin de coordination_ cite:[devops_handbook(288-298)].
Ce dépôt nest pas uniquement destiné à hébergé le code source, mais également à dautres artefacts et autres formes de connaissance, comme les standards de configuration (Chef recipes, Puppet manifests, …), outils de déploiement, standards de tests (y compris ce qui touche à la sécurité), outils danalyse et de monitoring ou tutoriaux.
[[dependencies]]
=== Déclaration explicite et isolation des dépendances
Chaque installation ou configuration doit être toujours réalisée de la même manière, et doit pouvoir être répétée quel que soit lenvironnement cible.

View File

@ -19,65 +19,50 @@ Si vous avez l'habitude de `pip`, restez dessus.
Si vous débutez ou souhaitez découvrir quelque chose de plus récent, donnez une chance à `poetry`.
====
Django fonctionne sur un
https://docs.djangoproject.com/en/dev/internals/release-process/[roulement
de trois versions mineures pour une version majeure], clôturé par une
version LTS (_Long Term Support_).
Django fonctionne sur un https://docs.djangoproject.com/en/dev/internals/release-process/[roulement de trois versions mineures pour une version majeure], clôturé par une version LTS (_Long Term Support_).
image::django-support-lts.png[align="center"]
Une bonne pratique consiste à ne viser que ces versions LTS, et à se prévoir une fenêtre de maintenance à intervalle regulier.
Une bonne pratique consiste à ne viser que ces versions LTS, supportées durant trois ans, et à se prévoir une fenêtre de maintenance à intervalle regulier.
Les sauts de versions sont bien documentés, et ne nécessitent que quelques heures de travail pour passer à la suivante.
Pour installer une nouvelle librairie, vous pouvez simplement passer par la commande `pip install <my_awesome_library>+`. Dans le cas de Django, et après
avoir activé lenvironnement, nous pouvons à présent y installer Django.
Comme expliqué ci-dessus, la librairie restera indépendante du reste du
système, et ne polluera aucun autre projet. nous exécuterons donc la
commande suivante:
Pour installer une nouvelle librairie, vous pouvez simplement passer par la commande `pip install <my_awesome_library>`.
Dans le cas de Django, et après avoir activé lenvironnement, nous pouvons à présent y installer Django.
Comme expliqué ci-dessus, la librairie restera indépendante du reste du système, et ne polluera aucun autre projet. nous exécuterons donc la commande suivante:
....
$ source ~/.venvs/gwift-env/bin/activate # ou ~/.venvs/gwift-
env/Scrips/activate.bat pour Windows.
$ pip install django
Collecting django
Downloading Django-3.1.4
100% |################################|
Installing collected packages: django
Successfully installed django-3.1.4
$ pip install django
Collecting django
Downloading Django-3.1.4
100% |################################|
Installing collected packages: django
Successfully installed django-3.1.4
....
Ici, la commande `+pip install django+` récupère la *dernière version
connue disponible dans les dépôts https://pypi.org/* (sauf si vous en
avez définis dautres. Mais cest hors sujet). Nous en avons déjà
discuté : il est important de bien spécifier la version que vous
souhaitez utiliser, sans quoi vous risquez de rencontrer des effets de
bord.
Ici, la commande `+pip install django+` récupère la *dernière version connue disponible dans les dépôts https://pypi.org/*.
Nous en avons déjà discuté : il est important de bien spécifier la version que vous souhaitez utiliser, sans quoi vous risquez de rencontrer des effets de bord (cf. <<dependencies>>).
Linstallation de Django a ajouté un nouvel exécutable:
`+django-admin+`, que lon peut utiliser pour créer notre nouvel espace
de travail. Par la suite, nous utiliserons `+manage.py+`, qui constitue
un *wrapper* autour de `+django-admin+`.
Linstallation de Django a ajouté un nouvel exécutable: `+django-admin+`, que lon peut utiliser pour créer notre nouvel espace de travail.
Par la suite, nous utiliserons `+manage.py+`, qui constitue un *wrapper* autour de `+django-admin+`.
Pour démarrer notre projet, nous lançons
`+django-admin startproject gwift+`:
Pour démarrer notre projet, nous lançons `+django-admin startproject gwift+`:
....
$ django-admin startproject gwift
$ django-admin startproject gwift
....
Cette action a pour effet de créer un nouveau dossier `+gwift+`, dans
lequel nous trouvons la structure suivante :
Cette action a pour effet de créer un nouveau dossier `+gwift+`, dans lequel nous trouvons la structure suivante :
....
$ tree gwift
gwift
-- gwift
----- asgi.py
----- __init__.py
----- settings.py
----- urls.py
----- wsgi.py
-- manage.py
$ tree gwift
gwift
-- gwift
----- asgi.py
----- __init__.py
----- settings.py
----- urls.py
----- wsgi.py
-- manage.py
....
Cest dans ce répertoire que vont vivre tous les fichiers liés au
@ -157,8 +142,6 @@ mur assuré. Avec les mécanismes dintégration continue et de tests
unitaires, nous verrons plus loin comment se prémunir dun changement
inattendu.
image:images/django-support-lts.png[image]
La version utilisée sera une bonne indication à prendre en considération
pour nos dépendances, puisquen visant une version particulière, nous ne
devrons pratiquement pas nous soucier (bon, un peu quand même, mais nous
@ -187,7 +170,7 @@ sera pas toujours possible de viser une généricité parfaite.
Pour `+gwift+`, nous aurons :
.Projet Django vs Applications
image::images/django/django-project-vs-apps-gwift.png[images/django/django-project-vs-apps-gwift]
image::django/django-project-vs-apps-gwift.png[align="center"]
. Une première application pour la gestion des listes de souhaits et des
éléments,
@ -203,7 +186,7 @@ quelle-même.
Pour `+khana+`, nous pourrions avoir quelque chose comme ceci:
.Django Project vs Applications
image::images/django/django-project-vs-apps-khana.png[images/django/django-project-vs-apps-khana]
image::django/django-project-vs-apps-khana.png[align="center"]
En rouge, vous pouvez voir quelque chose que nous avons déjà vu: la
gestion des utilisateurs et la possibilité quils auront de communiquer
@ -283,29 +266,21 @@ http://localhost:8000) comme le propose si gentiment notre (nouveau)
meilleur ami, nous verrons ceci:
.python manage.py runserver (Non, ce nest pas Challenger)
image::images/django/manage-runserver.png[images/django/manage-runserver]
image::django/manage-runserver.png[align="center"]
Nous avons mis un morceau de la sortie console entre crochet `+[+``+…+`
ci-dessus, car elle concerne les migrations. Si vous avez suivi les
étapes jusquici, vous avez également dû voir un message type
`+You have 18 unapplied migration(s). +``+[+``+… Run python manage.py migrate to apply them.+`
Cela concerne les migrations, et cest un point que nous verrons un peu
plus tard.
Nous avons mis un morceau de la sortie console entre crochet `+[+``+…+` ci-dessus, car elle concerne les migrations.
Si vous avez suivi les étapes jusquici, vous avez également dû voir un message type` You have 18 unapplied migration(s). +``+[+``+… Run python manage.py migrate to apply them.+`
Cela concerne les migrations, et cest un point que nous verrons un peu plus tard.
=== Minimal Viable Application
Maintenant que nous avons a vu à quoi servait `+manage.py+`, nous
pouvons créer notre nouvelle application grâce à la commande
`+manage.py startapp <label>+`.
Maintenant que nous avons a vu à quoi servait `+manage.py+`, nous pouvons créer notre nouvelle application grâce à la commande `+manage.py startapp <label>+`.
Notre première application servira à structurer des listes de souhaits,
les éléments qui les composent et les pourcentages de participation que
chaque utilisateur aura souhaité offrir. De manière générale, essayez de
trouver un nom éloquent, court et qui résume bien ce que fait
lapplication. Pour nous, ce sera donc `+wish+`.
Notre première application servira à structurer des listes de souhaits, les éléments qui les composent et les pourcentages de participation que chaque utilisateur aura souhaité offrir.
De manière générale, essayez de trouver un nom éloquent, court et qui résume bien ce que fait lapplication. Pour nous, ce sera donc `+wish+`.
....
$ python manage.py startapp wish
$ python manage.py startapp wish
....
Résultat? Django nous a créé un répertoire `+wish+`, dans lequel nous

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB