Gestion des paquets en Python 2/4

Partie 6 — Comprendre les wheels

20. Qu’est-ce qu’une wheel ?

Une wheel est un format de distribution construit, portant l’extension :

.whl

Elle peut contenir :

  • du code Python ;
  • des métadonnées ;
  • des ressources ;
  • des bibliothèques compilées ;
  • des extensions natives.

L’intérêt principal est d’éviter de compiler le logiciel pendant l’installation.

On peut comparer :

sdist  = code source à construire
wheel  = paquet déjà construit

21. Anatomie d’un nom de wheel

Exemple :

numpy-2.1.0-cp312-cp312-win_amd64.whl

Décomposition :

numpy       nom du paquet
2.1.0       version
cp312       interpréteur Python
cp312       ABI
win_amd64   plateforme

Autre exemple :

requests-2.32.3-py3-none-any.whl

Décomposition :

py3    compatible avec Python 3
none   aucune ABI native spécifique
any    n’importe quelle plateforme

Cette seconde wheel est dite « pure Python ».

22. Les trois tags d’une wheel

Une wheel utilise généralement cette structure :

{python tag}-{ABI tag}-{platform tag}

Python tag

Exemples :

py3
cp311
cp312
cp313

cp312 signifie CPython 3.12.

ABI tag

L’ABI est l’interface binaire entre le code compilé et Python.

Exemples :

none
cp312
abi3

Une extension utilisant l’ABI stable peut porter :

abi3

Elle peut alors fonctionner sur plusieurs versions de CPython.

Platform tag

Exemples :

win_amd64
win32
macosx_11_0_arm64
manylinux2014_x86_64
manylinux_2_17_aarch64
musllinux_1_2_x86_64
any

23. Pourquoi une wheel est refusée

Exemple :

ERROR: package.whl is not a supported wheel on this platform

Causes possibles :

  • wheel Python 3.11 utilisée avec Python 3.12 ;
  • wheel Windows utilisée sous Linux ;
  • wheel x86-64 utilisée sur ARM ;
  • wheel ARM utilisée sur x86-64 ;
  • wheel CPython utilisée avec PyPy ;
  • ABI incompatible ;
  • version de pip trop ancienne pour reconnaître les tags ;
  • système Linux incompatible avec le niveau manylinux.

Exemple incompatible :

torch-2.4.0-cp312-cp312-manylinux2014_aarch64.whl

Cette wheel cible CPython 3.12, Linux et l’architecture ARM64. Elle ne conviendra pas à un Windows x86-64. Les tags de wheels peuvent encoder la version Python, l’implémentation, le système d’exploitation et l’architecture.

24. Voir les tags acceptés par son environnement

python -m pip debug --verbose

La sortie contient une liste de tags compatibles, par exemple :

cp312-cp312-win_amd64
cp312-abi3-win_amd64
cp311-abi3-win_amd64
py3-none-any

Cela permet de comparer directement l’environnement avec le nom de la wheel.

Partie 7 — Archives source et compilation

25. Qu’est-ce qu’une sdist ?

Une source distribution utilise souvent :

.tar.gz

Exemple :

package-1.2.0.tar.gz

Elle contient le code source et les informations permettant de construire le paquet.

Lorsque aucune wheel compatible n’est disponible, pip peut télécharger la sdist puis tenter de construire une wheel localement.

On voit alors des messages comme :

Building wheel for package...

ou :

Failed building wheel for package

26. Pourquoi une compilation échoue

Un paquet peut contenir du :

  • C ;
  • C++ ;
  • Rust ;
  • Fortran ;
  • Cython.

Il peut donc nécessiter :

  • un compilateur ;
  • un linker ;
  • des fichiers d’en-tête ;
  • le SDK du système ;
  • des bibliothèques natives ;
  • Rust et Cargo ;
  • CMake ;
  • Ninja ;
  • pkg-config.

Sous Windows

Selon le paquet :

  • Microsoft C++ Build Tools ;
  • Windows SDK ;
  • CMake ;
  • Rust.

Sous Debian ou Ubuntu

Exemple générique :

sudo apt update
sudo apt install build-essential python3-dev

Selon le paquet :

sudo apt install libpq-dev
sudo apt install libssl-dev
sudo apt install libffi-dev
sudo apt install libxml2-dev libxslt1-dev

Exemple PostgreSQL

Le paquet psycopg ou certaines variantes de psycopg2 peuvent nécessiter les bibliothèques PostgreSQL de développement.

Erreur typique :

pg_config executable not found

Solution Debian/Ubuntu :

sudo apt install libpq-dev

27. Forcer l’utilisation de wheels

Pour refuser toute compilation :

python -m pip install --only-binary=:all: package

Exemple :

python -m pip install --only-binary=:all: numpy

Si aucune wheel compatible n’existe, l’installation échoue immédiatement.

C’est utile pour savoir si le problème vient de la compilation.

Préférer les wheels mais autoriser les sources :

python -m pip install --prefer-binary package

Refuser les wheels :

python -m pip install --no-binary=:all: package

Cette dernière commande force une compilation depuis les sources.

28. Construire ses propres wheels

python -m pip wheel -r requirements.txt -w wheels/

Puis installation locale :

python -m pip install --no-index --find-links=wheels -r requirements.txt

Cas d’usage :

  • déploiement hors ligne ;
  • CI/CD ;
  • serveurs sans compilateur ;
  • accélération des déploiements ;
  • contrôle des artefacts installés.

Partie 8 — Résolution des dépendances

29. Dépendances directes et transitives

Supposons :

mon-projet
└── fastapi
    ├── starlette
    ├── pydantic
    └── typing-extensions

fastapi est une dépendance directe.

starlette, pydantic et typing-extensions sont des dépendances transitives.

Lorsqu’on demande :

python -m pip install fastapi

pip doit chercher un ensemble de versions compatibles pour toutes ces bibliothèques.

Le processus qui détermine les versions à installer est appelé résolution des dépendances.

30. Exemple de conflit

Un projet demande :

package-a dépend de common-lib >=2,<3
package-b dépend de common-lib >=3,<4

Aucune version de common-lib ne peut satisfaire les deux contraintes.

pip peut afficher :

ResolutionImpossible

Ce message ne signifie pas nécessairement que pip est cassé.

Il signifie souvent que les contraintes sont mathématiquement incompatibles.

Les versions récentes du résolveur sont volontairement plus strictes face aux dépendances contradictoires.

31. Syntaxe des contraintes de versions

requests==2.32.3
requests>=2.30
requests>=2.30,<3
requests~=2.32.0
requests!=2.32.1

Version exacte

requests==2.32.3

Version minimale

requests>=2.30

Intervalle

requests>=2.30,<3

Version compatible

requests~=2.32.0

Cela signifie approximativement :

>=2.32.0,<2.33.0

Alors que :

requests~=2.32

signifie approximativement :

>=2.32,<3.0

32. Version de Python dans pyproject.toml

[project]
requires-python = ">=3.11,<3.13"

Cela indique que le projet accepte :

Python 3.11
Python 3.12

mais pas :

Python 3.10
Python 3.13

Ce champ est essentiel pour empêcher l’installation du projet avec une version incompatible.

33. Environment markers

Une dépendance peut être conditionnelle :

dependencies = [
    "importlib-metadata; python_version < '3.10'",
    "colorama; sys_platform == 'win32'",
]

Ainsi :

  • colorama sera installé sous Windows ;
  • importlib-metadata ne sera installé que pour certaines versions de Python.

Les résolveurs prennent en compte :

  • python_version ;
  • python_full_version ;
  • sys_platform ;
  • platform_machine ;
  • platform_python_implementation.

Partie 9 — requirements.txt, contraintes et verrouillage

34. Le fichier requirements.txt

Exemple simple :

fastapi
uvicorn
sqlalchemy
psycopg

Installation :

python -m pip install -r requirements.txt

Mais ce fichier ne garantit pas nécessairement une reproduction exacte.

Aujourd’hui, il peut installer :

fastapi 0.x
pydantic 2.x

Dans six mois, il peut choisir d’autres versions.

35. Versions figées

fastapi==0.116.1
uvicorn==0.35.0
sqlalchemy==2.0.41

Cela améliore la reproductibilité.

Pour exporter l’environnement actuel :

python -m pip freeze > requirements.txt

Mais pip freeze exporte généralement tout ce qui est installé, y compris les dépendances transitives.

Exemple :

fastapi==...
pydantic==...
pydantic-core==...
starlette==...
typing-extensions==...

Ce n’est pas toujours un bon fichier à maintenir manuellement.

36. Différence entre dépendances directes et environnement figé

Il est utile de distinguer :

Dépendances voulues

fastapi
sqlalchemy
psycopg

Résultat exact de la résolution

fastapi==...
starlette==...
pydantic==...
sqlalchemy==...
psycopg==...
typing-extensions==...

Le premier fichier décrit l’intention.

Le second décrit l’environnement résolu.

Les outils modernes comme uv, Poetry ou PDM maintiennent cette distinction avec :

  • pyproject.toml ;
  • un fichier de verrouillage.

37. Fichier de contraintes

Exemple constraints.txt :

urllib3<2.3
pydantic==2.11.7

Installation :

python -m pip install -r requirements.txt -c constraints.txt

Une contrainte ne demande pas nécessairement l’installation du paquet.

Elle limite la version si ce paquet doit être installé.

Retour en haut