Architektura izolacji wirtualnych środowisk w CPython: pyvenv.cfg i niskopoziomowa logika sys.path
Wirtualne środowiska w Pythonie to nie abstrakcja, ale ściśle określona systemowa interakcja między systemem plików, konfiguracją i kodem C interpretatora. Ich izolacja osiągana jest nie poprzez konteneryzację ani przejmowanie importów, lecz przez deterministyczne zmiany zachowania ładowarki modułów podczas inicjalizacji. To ma kluczowe znaczenie dla reprodukowalnych buildów, CI/CD oraz środowisk wieloprojektowych.
Dlaczego virtualenv jest przestarzały, a venv działa bez pipa
Do Pythona 3.3 izolacja była zadaniem zewnętrznym: virtualenv kopiował binarne wykonywalne interpretatora, duplikował bibliotekę standardową i patchował sys.path za pomocą wrapperów. To spowalniało tworzenie środowisk, zwiększało zużycie miejsca na dysku i komplikowało debugowanie. Z pojawieniem się PEP 405 mechanizm ten został zintegrowany z rdzeniem CPythona. Kluczowa różnica polega na rezygnacji z kopiowania binarnego na rzecz symbolicznych linków (Linux/macOS) lub launcherów (Windows), co czyni venv natychmiastowym i zależnym tylko od zainstalowanego Pythona.
Głównym technicznym przesunięciem jest przeniesienie logiki z zewnątrz do samego interpretatora. Zamiast „oszukiwać” Pythona za pomocą zmiennych środowiskowych czy wrapperów shellowych, venv opiera się na wewnętrznym algorytmie wyszukiwania pyvenv.cfg. Ten plik jest jedynym triggerem, który zmusza CPython do przebudowy sys.path i wyłączenia globalnych site-packages.
Jak CPython czyta pyvenv.cfg i przelicza sys.path
Inicjalizacja odbywa się na poziomie kodu C ładowarki (Py_Initialize). Podczas uruchamiania pliku wykonywalnego interpretator wykonuje następującą sekwencję:
- Określa lokalizację pliku wykonywalnego (np.
/project/.venv/bin/python). - Wspinając się w górę drzewem katalogów, szuka pliku
pyvenv.cfgw katalogu nadrzędnym. - Parsuje go jako plik tekstowy podobny do INI — bez zewnętrznych zależności, wyłącznie standardowym parserem CPythona.
- Jeśli
include-system-site-packages = false, globalne ścieżki są wykluczone zsys.pathprzed załadowaniem jakiegokolwiek użytkowego kodu. - Formuje nowy
sys.path: najpierw bieżący skrypt, następnielib/pythonX.Y, potem lokalnesite-packages, a dopiero później — przy wyraźnym zezwoleniu — globalne pakiety.
Oznacza to, że izolacja jest gwarantowana nawet przy bezpośrednim wywołaniu .venv/bin/python -c "import sys; print(sys.path)" bez aktywacji shellu. Aktywacja (source .venv/bin/activate) wpływa jedynie na $PATH i VIRTUAL_ENV, ale nie na zachowanie interpretatora.
Implementacja minimalnego venv: co naprawdę jest niezbędne
Stworzenie funkcjonalnego wirtualnego środowiska wymaga trzech komponentów — i nic więcej. Ani pip, ani setuptools, ani wrappera nie uczestniczą w bazowej izolacji.
- Struktura katalogów:
bin/,lib/pythonX.Y/site-packages/ - Plik
pyvenv.cfg: zawierahome,include-system-site-packages,version,executable - Wykonywalny w
bin/: symboliczny link (Unix) lub launcher (Windows), wskazujący na systemowypython
Wszystko inne to udogodnienia: pip, activate, pyproject.toml. Oto minimalny działający przykład dla Linuxa:
#!/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"
Uruchomienie ./my_venv/bin/python -c "import sys; print([p for p in sys.path if 'site-packages' in p])" pokaże tylko lokalną ścieżkę — bez żadnych zewnętrznych narzędzi.
Co jest ważne
- Izolacja wirtualnego środowiska to stan CPythona, a nie procesu czy wrappera. Aktywowana jest podczas startu interpretatora, a nie podczas aktywacji środowiska.
- Plik
pyvenv.cfgjest jedynym obowiązkowym artefaktem. Jego usunięcie zamienia środowisko w zwykły katalog z symbolicznym linkiem. sys.pathjest przebudowywany przed wykonaniem jakiegokolwiek kodu Pythona, w tymsite.py. Globalne pakiety są fizycznie niedostępne.- Poetry, Pipenv i inne menedżery zależności to nadbudowy nad
venv. Nie zmieniają logiki izolacji, tylko automatyzują tworzeniepyvenv.cfgi zarządzaniesite-packages. - Symboliczne linki są bezpieczne: CPython poprawnie rozszerza je i korzysta z rzeczywistego
homezpyvenv.cfg, a nie z drogi do symbolicznego linka.
Debugowanie i diagnostyka izolacji
Kiedy środowisko zachowuje się nieoczekiwane, sprawdź nie pip list, lecz bezpośrednio stan interpretatora:
- Upewnij się, że
pyvenv.cfgistnieje i zawierainclude-system-site-packages = false. - Uruchom
.venv/bin/python -c "import sys; print(sys.prefix, sys.base_prefix, sys.path)"— porównajprefixibase_prefix. - Sprawdź, czy
sys.pathzawiera dokładnie jedensite-packages— lokalny. - Wykonaj
.venv/bin/python -c "import site; print(site.getsitepackages())"— powinien zwrócić listę z jednym elementem.
Jeśli globalne pakiety są nadal widoczne — oznacza to, że pyvenv.cfg brakuje, jest uszkodzony lub include-system-site-packages ustawiono na true. Żadne zmienne środowiskowe (PYTHONPATH, PYTHONHOME) nie przekraczają tego zachowania — jest ono twardo zakodowane w CPythonie.
— Editorial Team
Brak komentarzy.