Aller au contenu
English

Qualité et passation

  • Leçon 18
  • 50 min
  • Module 6 : Du carnet Jupyter au projet facile à maintenir
Leçon 18 sur 18 · Python pour l'ingénierie de l'IA en pratiqueQualité et passation

Ce que vous saurez faire

Confiez ticket-cleaner à une autre personne. Elle reçoit un dossier et suit le README. En cinq minutes, le programme fonctionne et les tests réussissent sur son ordinateur, sans qu’elle vous pose de question. C’est le test à appliquer à chaque projet que vous construirez après ce cours.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 50 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

Testé avec Python 3.14.6, ruff 0.16.10 et pytest 9.1.1 sur macOS, et Python 3.14.4 sur Ubuntu 26.04.

On lit le code beaucoup plus souvent qu’on l’écrit. Deux outils facilitent sa lecture et trouvent certains bogues avant son exécution :

  • Un formateur réorganise votre code selon un style uniforme : espaces, longueur des lignes, guillemets, lignes vides. Il ne change pas ce que fait le code. Personne ne perd de temps sur la présentation pendant les révisions, et tous les fichiers ont le même style.
  • Un analyseur statique lit votre code et signale des structures qui sont souvent des bogues. Par exemple : une importation inutilisée, un nom qui n’existe pas ou une liste comme valeur par défaut.

ruff est un outil qui fait les deux, et il est rapide. Ajoutez-le au projet comme dépendance de développement :

  1. Ajoutez ruff==0.16.10 à requirements.txt. Ajoutez des commentaires pour séparer les besoins du programme des outils nécessaires pour travailler sur le code :

    requirements.txt
    # Needed to run the program
    httpx==0.28.1
    # Needed to work on the program: tests, formatting and linting
    pytest==9.1.1
    ruff==0.16.10
  2. Installez-le : python -m pip install -r requirements.txt.

  3. Créez pyproject.toml dans le dossier du projet. Les outils Python lisent leurs paramètres dans ce fichier :

    pyproject.toml
    # Settings for the development tools. The program itself needs only
    # requirements-lock.txt.
    [tool.ruff]
    line-length = 88
    [tool.ruff.lint]
    # The default rules, plus import sorting (I) and common bug patterns (B).
    extend-select = ["I", "B"]
    [tool.pytest.ini_options]
    testpaths = ["tests"]

    testpaths indique à pytest de chercher les tests uniquement dans tests/.

Amira crée un fichier avec quelques problèmes courants, ticket_cleaner/messy.py, pour voir ce que fait ruff :

ticket_cleaner/messy.py
from ticket_cleaner.records import Ticket
import json
def add_problem(problem, problems=[]):
problems.append(problem)
return problems
def count_open(tickets:list[Ticket])->int:
return len( [t for t in tickets if t.status=='open'] )

Elle lance l’analyseur statique sur ce fichier :

Fenêtre de terminal
ruff check ticket_cleaner/messy.py
Sortie (abrégée)
I001 [*] Import block is un-sorted or un-formatted
--> ticket_cleaner/messy.py:1:1
F401 [*] `json` imported but unused
--> ticket_cleaner/messy.py:2:8
B006 Do not use mutable data structures for argument defaults
--> ticket_cleaner/messy.py:5:35
|
5 | def add_problem(problem, problems=[]):
| ^^
help: Replace with `None`; initialize within function
Found 3 errors.
[*] 2 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option).

Chaque problème signalé comprend un code, un message et un emplacement (fichier:ligne:colonne). B006 désigne la liste par défaut qui garde les valeurs, vue dans Le contrat d’une fonction. L’analyseur statique la repère sans exécuter le code. Les problèmes marqués [*] peuvent être corrigés automatiquement sans risque :

Fenêtre de terminal
ruff check --fix ticket_cleaner/messy.py

Cette commande supprime l’importation inutilisée et trie les importations. B006 reste, car sa correction change ce que fait la fonction. Une personne doit donc décider. Ensuite, le formateur corrige la présentation :

Fenêtre de terminal
ruff format ticket_cleaner/messy.py
ticket_cleaner/messy.py (after the fix and format)
from ticket_cleaner.records import Ticket
def add_problem(problem, problems=[]):
problems.append(problem)
return problems
def count_open(tickets: list[Ticket]) -> int:
return len([t for t in tickets if t.status == "open"])

Deux lignes vides entre les fonctions, des espaces autour de :, -> et ==, des guillemets doubles, aucun espace dans les crochets. Le code fait la même chose et ressemble maintenant aux autres fichiers Python. Elle corrige B006 à la main, comme dans le module 3, puis supprime messy.py, car c’était seulement un exemple.

Pendant le cours, vous avez créé des fichiers pour vous exercer : first_run.py, ticket_values.py, urgent_report.py, try_mock.py, try_token.py, show_args.py. Ils ne font pas partie du programme. Une nouvelle personne ne peut pas savoir quels fichiers comptent. Supprimez-les donc, ou déplacez-les dans un dossier hors du projet.

Le projet a alors une structure claire :

ticket-cleaner/
ticket-cleaner/
├── .env.example the settings, without secret values
├── .gitignore
├── README.md how to set up, run and work on it
├── pyproject.toml settings for ruff and pytest
├── requirements.txt what the project uses directly
├── requirements-lock.txt the exact version of every package
├── data/ sample data
├── tests/ one test file for each module
└── ticket_cleaner/ the package

Certains fichiers ne doivent pas se trouver dans la copie que vous remettez. L’autre personne les recrée, ou ils sont privés. Inscrivez-les dans .gitignore, que Git et plusieurs autres outils respectent :

.gitignore
.venv/
__pycache__/
.pytest_cache/
.ruff_cache/
reports/
*.tmp
.env

Mettez à jour .env.example : TICKETS_SOURCE est maintenant l’argument input. Supprimez-le et ajoutez LOG_LEVEL.

Le README est le premier fichier qu’une personne lit. Pour un projet qu’elle doit exécuter, il répond à ces questions, dans cet ordre :

Section Réponses
Première ligne Que fait le programme, en une ou deux phrases?
Prérequis Que dois-je avoir avant de commencer?
Installation Quelles commandes exactes dois-je exécuter une seule fois?
Exécution Quelle commande lance le programme, et que vois-je quand il fonctionne?
Paramètres et options Que puis-je changer?
Codes de sortie Comment un script qui lance le programme connaît-il le résultat?
Travailler sur le code Comment lancer les tests et les vérifications?

Écrivez les commandes exactement comme une personne les tape. Montrez la sortie attendue pour qu’elle sache que chaque étape a fonctionné. Ce sont les mêmes règles que pour une leçon. Voici le début du README d’Amira. Le fichier complet comprend chaque section du tableau :

README.md (start)
# ticket-cleaner
Checks support-ticket records from a CSV file, a JSON file or a web service, and writes a summary report. Every record that is not valid is listed in the report, with the reason.
## Requirements
- Python 3.12 or newer. Check with `python3 --version` (`python --version` on Windows).
- No account. The `remote` input downloads a public file.
## Set up
Do these steps once, in a terminal in this folder.
1. Make a virtual environment: `python3 -m venv .venv`. On Windows, type `python` instead of `python3`.
2. Activate it. On macOS and Linux: `source .venv/bin/activate`. On Windows (PowerShell): `.venv\Scripts\Activate.ps1`. The prompt now starts with `(.venv)`.
3. Install the exact versions of the dependencies: `python -m pip install -r requirements-lock.txt`
## Run
```sh
python -m ticket_cleaner data/tickets.csv
```
Expected output (the last lines):
```text
15 rows: 9 valid, 6 rejected.
Report: reports/summary.json
```
  1. Ajoutez ruff et pyproject.toml comme ci-dessus.

  2. Lancez les vérifications sur tout le projet :

    Fenêtre de terminal
    ruff check .
    ruff format .

    Corrigez ce que ruff check signale. Relancez les deux commandes jusqu’à ce que ruff check . affiche All checks passed! (toutes les vérifications ont réussi). ruff format . doit indiquer qu’aucun fichier n’est à modifier.

  3. Relancez les tests : python -m pytest. Le formateur ne doit pas avoir changé ce que fait le code, et les tests le prouvent.

  4. Supprimez les fichiers d’exercice. Créez .gitignore et mettez à jour .env.example.

  5. Recréez le fichier de verrouillage dans un nouvel environnement. Il contiendra seulement les paquets de requirements.txt (voir l’erreur courante ci-dessous) :

    Fenêtre de terminal
    deactivate
    rm -rf .venv
    python3 -m venv .venv
    source .venv/bin/activate
    python -m pip install -r requirements.txt
    python -m pip freeze > requirements-lock.txt

    Le fichier de verrouillage contient 13 lignes : httpx, pytest, ruff et les paquets dont ils ont besoin.

  6. Écrivez README.md avec chaque section du tableau.

Vous ne pouvez pas tester un README sur l’ordinateur où vous l’avez écrit. Votre environnement, vos variables et votre mémoire comblent toutes les lacunes. Testez-le comme une nouvelle personne le découvre : une nouvelle copie, un nouveau terminal et seulement le README.

  1. Ouvrez un nouveau terminal. N’activez rien.

  2. Faites une copie du projet sans les fichiers indiqués dans .gitignore. Si vous utilisez Git et avez enregistré votre travail dans un commit (enregistrement des modifications), clonez-le :

    Fenêtre de terminal
    cd ~/projects
    git clone ticket-cleaner ticket-cleaner-fresh

    Sans Git, copiez le dossier ticket-cleaner dans votre gestionnaire de fichiers et nommez la copie ticket-cleaner-fresh. Supprimez .venv, reports et tous les dossiers __pycache__ dans la copie.

  3. Exécutez cd ticket-cleaner-fresh, ouvrez README.md et faites exactement ce qu’il indique, rien de plus. N’utilisez aucune commande de mémoire.

  4. Chaque fois que vous devez faire quelque chose que le README n’indique pas, arrêtez. Ajoutez l’étape manquante au README du projet original.

  5. Quand le README est complet, supprimez ticket-cleaner-fresh et recommencez. Continuez jusqu’à ce qu’une nouvelle copie fonctionne avec seulement le README. Le programme affiche 15 rows: 9 valid, 6 rejected. (15 lignes : 9 valides, 6 rejetées). Les commandes python -m pytest et ruff check . réussissent.

Demandez à une personne qui ne connaît pas le projet de faire le test sur une nouvelle copie. Elle doit utiliser son propre ordinateur, si possible avec un système d’exploitation différent. Ne l’aidez pas. Notez chaque endroit où elle s’arrête ou pose une question.

Si personne n’est disponible, faites-le sur un autre ordinateur. Ou attendez un jour et refaites-le vous-même. Une lacune que vous avez manquée aujourd’hui sera plus facile à voir demain.

Afficher les questions les plus fréquentes
  • « Quel Python? » → La section Prérequis doit donner la version et la commande pour la vérifier.
  • « L’invite n’affiche pas (.venv). » → La section Installation doit donner la commande d’activation pour chaque système. Elle doit aussi inclure la correction de la stratégie d’exécution PowerShell vue dans Isoler les dépendances.
  • « Où est le rapport? » → La section Exécution doit donner le chemin du fichier de sortie et la sortie attendue.
  • « Une ligne WARNING indique-t-elle un problème? » → La section Exécution doit préciser ceci : chaque enregistrement rejeté produit un WARNING (avertissement), et c’est normal.
  • « Comment lancer les tests? » → La section Travailler sur le code.

Erreur courante

Un fichier de verrouillage avec des paquets que le projet n'utilise pas

Symptôme : requirements-lock.txt contient beaucoup plus de lignes que prévu, par exemple ipykernel, jupyter_client, traitlets et vingt autres. Ou une nouvelle installation prend beaucoup de temps ou échoue sur un autre système.

Cause : pip freeze liste tous les paquets de l’environnement, pas seulement ceux de requirements.txt. Vous avez peut-être installé quelque chose pour l’essayer. Par exemple, ipykernel pour un carnet (notebook) Jupyter dans De la rigueur dans le carnet. Il se retrouve maintenant dans le fichier de verrouillage, et chaque nouvelle personne l’installe aussi.

Correction :

  1. Créez le fichier de verrouillage dans un nouvel environnement, avec seulement requirements.txt installé (étape 5 de l’exercice guidé).
  2. Comparez l’ancien et le nouveau fichier de verrouillage. Chaque ligne doit correspondre à un paquet de requirements.txt ou à une de ses dépendances.
  3. Installez les outils personnels, comme ipykernel, hors de l’environnement du projet. Ou réinstallez-les après avoir créé le fichier de verrouillage.

Vérifiez votre compréhension

ruff check --fix a supprimé une importation et trié les autres. Comment savoir qu'il n'a rien brisé?

Afficher la réponse

Lancez les tests : python -m pytest. Le formatage et les corrections sans risque ne changent pas ce que fait le code. Mais les tests le prouvent, plutôt que de simplement le croire. C’est pourquoi le README demande de lancer les trois opérations : tests, formatage et vérification.

Vérifiez votre compréhension

Vérification du module : que signifie exécuter le projet dans un nouvel environnement avec seulement le README, et comment le démontrer?

Afficher la réponse

C’est une copie sans .venv, sans reports et sans aucun paramètre provenant de votre terminal. Vous l’installez en suivant le README étape par étape, sans aucune étape faite de mémoire. Vous le démontrez avec les résultats de cette exécution : le programme affiche 15 rows: 9 valid, 6 rejected.. Les commandes python -m pytest et ruff check . réussissent, toutes dans la nouvelle copie. Chaque lacune trouvée est maintenant une étape dans le README.

Récapitulatif

  • Un formateur uniformise la présentation. Un analyseur statique repère des structures qui sont souvent des bogues. ruff fait les deux : ruff format . et ruff check ..
  • Après toute modification automatique, lancez les tests.
  • Une structure claire contient seulement les fichiers nécessaires au projet. .gitignore liste ce qui reste privé ou sera recréé.
  • Le README décrit le programme, les prérequis, l’installation, l’exécution et ce que vous voyez quand tout fonctionne.
  • Créez le fichier de verrouillage dans un nouvel environnement. Testez le README sur une nouvelle copie et ajoutez chaque étape manquante.

Vous pouvez maintenant construire, tester et confier un projet Python à une autre personne. Le travail final réunit tous ces éléments.

Leçon suivante : Travail final