Integrate Poetry
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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 d’un 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 d’un terminal séparé facilite parfois certaines tâches.
|
||||
A nouveau, si vous manquez d’idé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 n’est 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 qu’il
|
||||
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 qu’il faut.
|
||||
|
||||
image::environment/terminal.png[align="center"]
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ En plus de cela, chaque développeur a une copie de l’application qui fonction
|
|||
Comme l’explique Eran Messeri, ingénieur dans le groupe Google Developer Infrastructure: "_Un des avantages d’utiliser un dépôt unique de sources, est qu’il 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 n’est pas uniquement destiné à hébergé le code source, mais également à d’autres 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 d’analyse 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 l’environnement cible.
|
||||
|
|
|
@ -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é l’environnement, 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é l’environnement, 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 d’autres. Mais c’est 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>>).
|
||||
|
||||
L’installation de Django a ajouté un nouvel exécutable:
|
||||
`+django-admin+`, que l’on 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+`.
|
||||
L’installation de Django a ajouté un nouvel exécutable: `+django-admin+`, que l’on 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
|
||||
....
|
||||
|
||||
C’est dans ce répertoire que vont vivre tous les fichiers liés au
|
||||
|
@ -157,8 +142,6 @@ mur assuré. Avec les mécanismes d’intégration continue et de tests
|
|||
unitaires, nous verrons plus loin comment se prémunir d’un 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, puisqu’en 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 @@ qu’elle-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é qu’ils 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 n’est 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 jusqu’ici, 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 c’est 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 jusqu’ici, 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 c’est 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
|
||||
l’application. 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 l’application. 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
|
||||
|
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 142 KiB |
After Width: | Height: | Size: 143 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 42 KiB |