cassionAnalyse de données

Leçon 3 sur 8

Unité · La même réponse deux fois

Ça marche sur ma machine, et c'est le rapport de bogue

Le même script, les mêmes données et une autre version de pandas produisent d'autres nombres. Un fichier de verrouillage est ce qui transforme « ça marche sur ma machine » d'une défense en une affirmation vérifiable, et cela coûte une commande.

PythonR135 minCritères d'évaluation du CAD de l'OCDENorme humanitaire fondamentale (CHS)

Ce que l’ordinateur d’un collègue fait différemment

Vous envoyez un script et un CSV. Il l’exécute. Le nombre diffère.

Ce qui diffère Ce que cela change
Version de paquet Un argument par défaut, une règle d’arrondi, un ordre de tri
Version du langage Ordre des dictionnaires, division entière, traitement des chaînes
Système d’exploitation Fins de ligne, ordre des fichiers, séparateurs de chemin, locale
Locale 1,234 interprété comme 1234 ou comme 1,234
Ce qui est déjà installé Une bibliothèque que votre script importe sans jamais la déclarer

Rien de tout cela n’est dans votre dépôt, raison pour laquelle un code identique ne suffit pas. Un fichier de verrouillage est la moitié manquante de l’analyse.

Python : uv

uv init                      # creates pyproject.toml
uv add pandas matplotlib     # records the dependency and resolves it
uv run python run.py         # runs inside the pinned environment

Deux fichiers, et les deux sont commités. pyproject.toml dit ce que vous avez demandé — pandas>=2.0 — et uv.lock dit exactement ce que vous avez obtenu, jusqu’à l’empreinte de chaque paquet de l’arbre.

Un collègue lance uv sync et dispose de votre environnement, sur son système, sans que vous sachiez ce qu’il avait installé auparavant.

Figez aussi la version du langage.

# pyproject.toml
requires-python = ">=3.12,<3.13"

Une plage de versions, non une version unique. Figer à 3.12.4 exactement signifie qu’un collègue en 3.12.7 ne peut pas l’exécuter du tout, ce qui est un échec pire que celui qu’on prévenait.

R : renv

renv::init()                 # snapshots the project library
renv::snapshot()             # after adding a package
renv::restore()              # on the colleague's machine

renv.lock est l’artefact équivalent et il est commité. Il enregistre chaque paquet, sa version et le dépôt d’où il vient, et renv::restore() le reconstruit.

Enregistrez la version de R, ce que renv fait automatiquement, et vérifiez-la dans le script là où une différence compterait.

if (getRversion() < "4.3.0") stop("This analysis requires R >= 4.3.0")
# The Python equivalent, at the top of the entry point.
import sys
assert sys.version_info >= (3, 12), "This analysis requires Python 3.12+"

Pourquoi un fichier de verrouillage et non requirements.txt

pandas>=2.0
matplotlib

Ce fichier ne dit presque rien. Il se résout en versions différentes selon les jours et ne mentionne pas les cinquante paquets que ces deux-là entraînent. Une installation en mars et une en juillet depuis le même requirements.txt sont deux environnements différents.

Déclare Reproduit
requirements.txt L’intention Non
requirements.txt avec des == Une couche En partie — les dépendances transitives flottent
uv.lock / renv.lock L’arbre entier Oui

Commitez l’intention et le verrou. Le fichier d’intention est ce qu’un humain édite ; le verrou est ce depuis quoi une machine reproduit, et aucun ne remplace l’autre.

Ce que cette plateforme fige, et pourquoi un verrou est inhabituel

{
  "packageManager": "pnpm@11.9.0",
  "engines": { "node": ">=22" },
  "devDependencies": { "typescript": "^6.0.3" }
}

Le gestionnaire de paquets lui-même est figé, parce qu’un pnpm différent résout le fichier de verrouillage différemment, et c’est la couche en dessous de celle que la plupart des projets figent.

TypeScript est maintenu en 6.x délibérément. TypeScript 7 — le compilateur natif — n’expose pas encore l’API programmatique dont dépend astro check, si bien que pnpm typecheck échoue purement et simplement en 7. Le verrou a une raison, la raison est écrite, et elle nomme la condition de sa levée.

C’est la forme que devrait avoir un verrou. Une contrainte de version sans commentaire est une contrainte que personne n’osera retirer et que personne ne peut justifier de garder.

# pandas is pinned below 3.0 because the copy-on-write default changes the
# behaviour of the recode in src/clean.py:88. Revisit when that is rewritten.
pandas = ">=2.1,<3.0"

Les conteneurs, et quand ils en valent la peine

Un conteneur fige aussi le système d’exploitation, la seule couche qu’un fichier de verrouillage ne peut pas atteindre.

Ils en valent la peine quand l’analyse a des dépendances hors Python ou hors R — GDAL, une distribution TeX, un client de base de données —, ou quand elle doit tourner sans surveillance sur un serveur, ou quand le constat sera réexaminé des années plus tard.

Ils n’en valent pas la peine quand un fichier de verrouillage reproduit déjà le résultat et que le public est deux collègues avec des ordinateurs portables. Un conteneur ajoute une étape de construction, un registre et une compétence qu’une petite équipe de suivi-évaluation peut ne pas avoir, et le coût est réel.

FROM python:3.12-slim
COPY pyproject.toml uv.lock ./
RUN pip install uv && uv sync --frozen
COPY . .
CMD ["uv", "run", "python", "run.py"]

Commencez par le fichier de verrouillage. Passez au conteneur quand vous pouvez nommer la dépendance qu’il fige et que le verrou ne peut pas.

Le test qui le prouve

Reproduisez votre propre résultat sur une machine qui n’a jamais vu le projet. Un clone neuf dans un répertoire temporaire en fait l’essentiel.

git clone <repo> /tmp/check && cd /tmp/check
uv sync
uv run python run.py
diff -r outputs/ ~/project/outputs/
# renv::restore() then source("run.R"), and compare.

Si vous ne pouvez pas le faire, « c’est reproductible » n’est pas testé. L’ordinateur d’un collègue vaut mieux et un exécuteur d’intégration continue vaut mieux encore, parce que c’est une machine propre à chaque fois et qu’il s’exécute que quiconque y pense ou non.

Rapportez-le en entier

Computational environment

  Python 3.12, dependencies pinned in uv.lock (committed). Reproduce with:

      uv sync && uv run python run.py

  pandas is held below 3.0 because the copy-on-write default changes the
  recode in src/clean.py; this is revisited when that function is rewritten.

  The pipeline runs on every push in CI on a clean machine, so the claim
  that a fresh checkout reproduces these outputs is tested rather than
  asserted.

  Analysis run on 2026-03-14 with commit a3f9c21.

La dernière ligne est celle qui rend le reste utilisable un an plus tard. Un résultat sans le commit qui l’a produit se reproduit approximativement ; avec lui, exactement.

La suite

Un environnement figé produit encore une réponse différente à la seconde exécution si le code lit l’horloge, tire un nombre aléatoire, ou se fie à l’ordre dans lequel les fichiers reviennent. La leçon suivante trouve les quatre.

Animer cette leçon

La leçon en diaporama, la prose étant reléguée dans les notes du présentateur plutôt que projetée. Produit à partir de cette page, dont il ne peut donc pas s'écarter.

Lancer le diaporamaLire les diapositives

Le PDF ne requiert aucun logiciel et se projette depuis n'importe quel poste. Le fichier PowerPoint est fait pour être modifié : appliquez la charte de votre organisation, retirez une section pour une séance plus courte, ou fusionnez deux leçons en atelier.