Retour à l'accueil

Architecture venv en Python : Comment pyvenv.cfg gère sys.path

L'article explique comment les environnements virtuels en Python fournissent l'isolation des dépendances au niveau CPython via le fichier pyvenv.cfg et le recalcul de sys.path. Il couvre l'architecture de bas niveau, les exigences minimales de l'environnement et les méthodes de diagnostic.

Comment CPython lit pyvenv.cfg et isole les dépendances
Advertisement 728x90

L’isolation des environnements virtuels dans CPython : architecture, pyvenv.cfg et logique de sys.path au niveau bas

Les environnements virtuels en Python ne sont pas un concept abstrait — il s’agit d’un système bien défini qui orchestre les interactions entre le système de fichiers, les fichiers de configuration et le code C de l’interpréteur. Leur isolation n’est pas obtenue par conteneurisation ou interception des imports ; elle repose plutôt sur des modifications déterministes du comportement du chargeur de modules lors de l’initialisation. Cela est crucial pour des builds reproductibles, des pipelines CI/CD et des environnements de développement multi-projets.

Pourquoi virtualenv est obsolète tandis que venv fonctionne sans pip

Avant Python 3.3, l’isolation des environnements était gérée de manière externe : virtualenv copiait le binaire de l’interpréteur, dupliquait la bibliothèque standard et patchait sys.path via des wrappers. Cela ralentissait la création des environnements, consommait plus d’espace disque et rendait le débogage plus difficile. Avec l’introduction de PEP 405, ce mécanisme a été intégré au cœur de CPython. La principale différence réside dans l’abandon de la copie des binaires au profit de liens symboliques (Linux/macOS) ou de lanceurs (Windows), ce qui rend venv instantané et dépend uniquement de la version de Python installée.

Le principal changement technique consiste à déplacer la logique des outils externes vers l’interpréteur lui-même. Plutôt que de « tromper » Python via des variables d’environnement ou des wrappers shell, venv s’appuie sur un algorithme de recherche interne pour pyvenv.cfg. Ce fichier unique est le déclencheur qui force CPython à reconstruire sys.path et à désactiver les site-packages globaux.

Google AdInline article slot

Comment CPython lit pyvenv.cfg et recalcule sys.path

L’initialisation se produit au niveau du chargeur C (Py_Initialize). Lorsque l’exécutable est lancé, l’interpréteur suit ces étapes :

  • Il détermine l’emplacement de l’exécutable (par exemple, /project/.venv/bin/python).
  • Il remonte dans l’arborescence jusqu’à trouver pyvenv.cfg dans le répertoire parent.
  • Il analyse le fichier comme un fichier texte de type INI — en utilisant uniquement le parseur standard de CPython, sans dépendances externes.
  • Si include-system-site-packages = false, les chemins globaux sont exclus de sys.path avant le chargement de tout code utilisateur.
  • Il construit un nouveau sys.path : d’abord le script actuel, puis lib/pythonX.Y, ensuite les site-packages locaux, et seulement après autorisation explicite — les packages globaux.

Cela signifie que l’isolation est garantie même lorsque vous exécutez directement .venv/bin/python -c "import sys; print(sys.path)" sans activer le shell. L’activation (source .venv/bin/activate) n’affecte que $PATH et VIRTUAL_ENV, mais pas le comportement de l’interpréteur.

Implémenter un venv minimal : ce qui est vraiment nécessaire

Pour créer un environnement virtuel fonctionnel, il suffit de trois composants — et rien de plus. Ni pip, ni setuptools, ni le shell ne jouent un rôle dans l’isolation de base.

Google AdInline article slot
  • Structure du répertoire : bin/, lib/pythonX.Y/site-packages/
  • Fichier pyvenv.cfg : contient home, include-system-site-packages, version, executable
  • Exécutable dans bin/ : un lien symbolique (Unix) ou un lanceur (Windows) pointant vers le Python système

Tout le reste n’est qu’une commodité : pip, activate, pyproject.toml. Voici un exemple minimal fonctionnel pour Linux :

#!/bin/bash
ENV_DIR="my_venv"
mkdir -p "$ENV_DIR/bin" "$ENV_DIR/lib/python$(python -c 'import sys; print(f\"{sys.version_info.major}.{sys.version_info.minor}\")')/site-packages"

cat > "$ENV_DIR/pyvenv.cfg" <<EOF
home = $(dirname $(which python))
include-system-site-packages = false
version = $(python -c 'import sys; print(".".join(map(str, sys.version_info[:3])) )')
executable = $(which python)
EOF

ln -sf "$(which python)" "$ENV_DIR/bin/python"

En exécutant ./my_venv/bin/python -c "import sys; print([p for p in sys.path if 'site-packages' in p])", vous verrez uniquement le chemin local — aucun outil tiers n’est impliqué.

Points clés

  • L’isolation des environnements virtuels est un état de CPython, pas du processus ou du shell. Elle s’active lorsque l’interpréteur démarre, pas lorsque l’environnement est activé.
  • Le fichier pyvenv.cfg est le seul artefact obligatoire. Supprimer ce fichier transforme l’environnement en un répertoire ordinaire avec un lien symbolique.
  • sys.path est reconstruit avant l’exécution de tout code Python, y compris site.py. Les packages globaux sont physiquement inaccessibles.
  • Poetry, Pipenv et autres gestionnaires de dépendances sont construits sur la base de venv. Ils ne modifient pas la logique d’isolation ; ils se contentent d’automatiser la création de pyvenv.cfg et la gestion des site-packages.
  • Les liens symboliques sont sûrs : CPython les déréférence correctement et utilise le home réel spécifié dans pyvenv.cfg, plutôt que le chemin du lien symbolique.

Débogage et diagnostic de l’isolation

Lorsqu’un environnement se comporte de manière inattendue, vérifiez non pas pip list, mais l’état direct de l’interpréteur :

Google AdInline article slot
  • Assurez-vous que pyvenv.cfg existe et que include-system-site-packages = false.
  • Exécutez .venv/bin/python -c "import sys; print(sys.prefix, sys.base_prefix, sys.path)" — comparez prefix et base_prefix.
  • Vérifiez que sys.path contient exactement une entrée site-packages — celle locale.
  • Exécutez .venv/bin/python -c "import site; print(site.getsitepackages())" — cela devrait renvoyer une liste avec un seul élément.

Si les packages globaux sont encore visibles, cela signifie que pyvenv.cfg est manquant, corrompu ou que include-system-site-packages est réglé sur true. Aucune variable d’environnement (PYTHONPATH, PYTHONHOME) ne peut contourner ce comportement — il est codé en dur dans CPython.

— Editorial Team

Advertisement 728x90

Lire ensuite