Wie CPython die Isolation virtueller Umgebungen implementiert: Architektur, pyvenv.cfg und Low-Level-Logik von sys.path
Virtuelle Umgebungen in Python sind kein abstraktes Konzept – sie sind ein klar definiertes System, das die Interaktionen zwischen dem Dateisystem, Konfigurationsdateien und dem C-Code des Interpreters orchestriert. Ihre Isolation wird nicht durch Containerisierung oder Importabfangung erreicht; stattdessen beruht sie auf deterministischen Änderungen am Verhalten des Modulladers während der Initialisierung. Dies ist entscheidend für reproduzierbare Builds, CI/CD-Pipelines und Multi-Projekt-Entwicklungsumgebungen.
Warum virtualenv veraltet ist, während venv ohne pip funktioniert
Vor Python 3.3 wurde die Isolation von Umgebungen extern gehandhabt: virtualenv kopierte das Interpreter-Binary, duplizierte die Standardbibliothek und patchte sys.path über Wrapper. Dies verlangsamte die Erstellung von Umgebungen, verbrauchte mehr Speicherplatz und erschwerte das Debugging. Mit der Einführung von PEP 405 wurde dieser Mechanismus in den Kern von CPython integriert. Der Hauptunterschied besteht darin, auf das Kopieren von Binärdateien zu verzichten und stattdessen symbolische Links (Linux/macOS) oder Launcher (Windows) zu verwenden, wodurch venv sofort verfügbar ist und nur von der installierten Python-Version abhängt.
Die wichtigste technische Verschiebung besteht darin, die Logik von externen Tools in den Interpreter selbst zu verlagern. Anstatt Python über Umgebungsvariablen oder Shell-Wrappers „auszutricksen“, stützt sich venv auf einen internen Suchalgorithmus für pyvenv.cfg. Diese eine Datei ist der Auslöser, der CPython dazu zwingt, sys.path neu aufzubauen und globale site-packages zu deaktivieren.
Wie CPython pyvenv.cfg liest und sys.path neu berechnet
Die Initialisierung erfolgt auf C-Ebene beim Loader (Py_Initialize). Wenn das ausführbare Programm startet, folgt der Interpreter diesen Schritten:
- Er ermittelt den Speicherort des Executables (z. B.
/project/.venv/bin/python). - Er wandert im Verzeichnisbaum nach oben, bis er
pyvenv.cfgim übergeordneten Verzeichnis findet. - Er analysiert die Datei als INI-ähnliche Textdatei – mit CPythons Standardparser, ganz ohne externe Abhängigkeiten.
- Wenn
include-system-site-packages = falseist, werden globale Pfade vor dem Laden jeglichen Benutzer-Codes aussys.pathentfernt. - Er erstellt einen neuen
sys.path: zuerst das aktuelle Skript, dannlib/pythonX.Y, anschließend die lokalensite-packagesund erst nach expliziter Genehmigung – globale Pakete.
Das bedeutet, dass die Isolation auch gewährleistet ist, wenn man direkt .venv/bin/python -c "import sys; print(sys.path)" ausführt, ohne die Shell zu aktivieren. Die Aktivierung (source .venv/bin/activate) beeinflusst lediglich $PATH und VIRTUAL_ENV, nicht jedoch das Verhalten des Interpreters.
Implementierung eines minimalen venv: Was wirklich notwendig ist
Um eine funktionierende virtuelle Umgebung zu erstellen, braucht es genau drei Komponenten – und nichts weiter. Weder pip, noch setuptools, noch die Shell spielen bei der grundlegenden Isolation eine Rolle.
- Verzeichnisstruktur:
bin/,lib/pythonX.Y/site-packages/ - Datei
pyvenv.cfg: enthälthome,include-system-site-packages,version,executable - Ausführbares in
bin/: ein Symlink (Unix) oder Launcher (Windows), der auf den System-Python zeigt
Alles andere sind Komfortfunktionen: pip, activate, pyproject.toml. Hier ist ein minimales Arbeitsbeispiel für 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"
Wenn man ./my_venv/bin/python -c "import sys; print([p for p in sys.path if 'site-packages' in p])" ausführt, zeigt dies nur den lokalen Pfad – ohne Einbeziehung dritter Tools.
Wichtige Erkenntnisse
- Die Isolation virtueller Umgebungen ist ein Zustand von CPython, nicht von Prozess oder Shell. Sie tritt in Kraft, wenn der Interpreter startet, nicht wenn die Umgebung aktiviert wird.
- Die Datei
pyvenv.cfgist das einzige obligatorische Artefakt. Entfernt man sie, wird die Umgebung zu einem normalen Verzeichnis mit einem Symlink. sys.pathwird vor dem Ausführen jeglichen Python-Codes neu aufgebaut, einschließlichsite.py. Globale Pakete sind physisch unzugänglich.- Poetry, Pipenv und andere Dependency-Manager bauen auf
venvauf. Sie ändern die Isolationslogik nicht; sie automatisieren lediglich das Erstellen vonpyvenv.cfgund das Management vonsite-packages. - Symbolische Links sind sicher: CPython entpackt sie korrekt und verwendet den tatsächlichen
home, der inpyvenv.cfgangegeben ist, statt des Pfads des Symlinks.
Debugging und Diagnose der Isolation
Wenn eine Umgebung sich unerwartet verhält, sollte man nicht pip list prüfen, sondern direkt den Zustand des Interpreters untersuchen:
- Stellen Sie sicher, dass
pyvenv.cfgexistiert undinclude-system-site-packages = falsesetzt. - Führen Sie
.venv/bin/python -c "import sys; print(sys.prefix, sys.base_prefix, sys.path)"aus – vergleichen Sieprefixundbase_prefix. - Überprüfen Sie, ob
sys.pathgenau einen einzigensite-packages-Eintrag enthält – den lokalen. - Führen Sie
.venv/bin/python -c "import site; print(site.getsitepackages())"aus – es sollte eine Liste mit einem einzigen Element zurückgeben.
Wenn globale Pakete weiterhin sichtbar sind, bedeutet das, dass pyvenv.cfg fehlt, beschädigt ist oder include-system-site-packages auf true gesetzt wurde. Keine Umgebungsvariablen (PYTHONPATH, PYTHONHOME) können dieses Verhalten außer Kraft setzen – es ist fest in CPython eingebettet.
— Editorial Team
Noch keine Kommentare.