Powrót do strony głównej

Architektura venv w Pythonie: jak pyvenv.cfg zarządza sys.path

Artykuł wyjaśnia, jak wirtualne środowiska w Pythonie zapewniają izolację zależności na poziomie CPython za pomocą pliku pyvenv.cfg i przeliczenia sys.path. Omówiona jest niskopoziomowa architektura, minimalne wymagania środowiskowe i metody diagnostyki.

Jak CPython czyta pyvenv.cfg i izoluje zależności
Advertisement 728x90

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.

Google AdInline article slot

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.cfg w 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 z sys.path przed załadowaniem jakiegokolwiek użytkowego kodu.
  • Formuje nowy sys.path: najpierw bieżący skrypt, następnie lib/pythonX.Y, potem lokalne site-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.

Google AdInline article slot
  • Struktura katalogów: bin/, lib/pythonX.Y/site-packages/
  • Plik pyvenv.cfg: zawiera home, include-system-site-packages, version, executable
  • Wykonywalny w bin/: symboliczny link (Unix) lub launcher (Windows), wskazujący na systemowy python

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.cfg jest jedynym obowiązkowym artefaktem. Jego usunięcie zamienia środowisko w zwykły katalog z symbolicznym linkiem.
  • sys.path jest przebudowywany przed wykonaniem jakiegokolwiek kodu Pythona, w tym site.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ą tworzenie pyvenv.cfg i zarządzanie site-packages.
  • Symboliczne linki są bezpieczne: CPython poprawnie rozszerza je i korzysta z rzeczywistego home z pyvenv.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:

Google AdInline article slot
  • Upewnij się, że pyvenv.cfg istnieje i zawiera include-system-site-packages = false.
  • Uruchom .venv/bin/python -c "import sys; print(sys.prefix, sys.base_prefix, sys.path)" — porównaj prefix i base_prefix.
  • Sprawdź, czy sys.path zawiera dokładnie jeden site-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

Advertisement 728x90

Czytaj dalej