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.
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.cfgdans 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 desys.pathavant le chargement de tout code utilisateur. - Il construit un nouveau
sys.path: d’abord le script actuel, puislib/pythonX.Y, ensuite lessite-packageslocaux, 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.
- Structure du répertoire :
bin/,lib/pythonX.Y/site-packages/ - Fichier
pyvenv.cfg: contienthome,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.cfgest le seul artefact obligatoire. Supprimer ce fichier transforme l’environnement en un répertoire ordinaire avec un lien symbolique. sys.pathest reconstruit avant l’exécution de tout code Python, y comprissite.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 depyvenv.cfget la gestion dessite-packages. - Les liens symboliques sont sûrs : CPython les déréférence correctement et utilise le
homeréel spécifié danspyvenv.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 :
- Assurez-vous que
pyvenv.cfgexiste et queinclude-system-site-packages = false. - Exécutez
.venv/bin/python -c "import sys; print(sys.prefix, sys.base_prefix, sys.path)"— comparezprefixetbase_prefix. - Vérifiez que
sys.pathcontient exactement une entréesite-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
Aucun commentaire pour le moment.