Egy elemzés akkor reprodukálható, ha nemcsak a kódot, hanem a kódot futtató környezetet is ismerjük. A gyakori hibajelenség: a notebookban lefutó script egy másik gépen nem találja a NumPy-t, vagy éppen más verziót tölt be, mint amit a szerző látott. A diagnosztika első lépése nem a csomagok újratelepítése, hanem a futó folyamat önazonosítása.
A konkrét feladat bemenete: terminálparancsok egy meghatározott
munkakönyvtárban. A megfigyelési egység: egy elindított Python-folyamat,
pontosabban annak interpreter- és modulkeresési állapota. A kimenet:
környezetjelentés, amely tartalmazza a sys.executable,
sys.prefix, sys.path, Python-verzió, illetve a
NumPy verzióját és betöltési fájlját. Az ellenőrzési feltétel: ha a
kódot a .venv-a/bin/python indította, akkor a jelentésben a
sys.prefix a .venv-a mappára mutat, a
numpy.__file__ pedig ugyanezen prefix alatti
site-packages-ben kezdődik. Előzetes becslés: a
.venv-a és .venv-b két jelentése eltérő
NumPy-verziót ír ki, miközben a Python-verzió azonos marad. A futtatott
példák ebben a dokumentumban nem ténylegesen mért kimenetek; ahol fájl-
vagy verzióadat szerepel, ott „szemléltető” jelölést használunk.
Az interpreter (értelmező) az a program, amely a
Python-forrást gépi végrehajtásra alakítja. A tanulási alap itt a
CPython 3.12, tehát a hivatalos C-ben írt interpreter. A
python3.12 parancs nem magát a nyelvet jelöli, hanem egy
konkrét futtatható fájlt. A folyamaton belül a
sys.executable mondja meg, hogy az adott futó Python melyik
fájlból indult:
import sys
print(sys.executable)Szemléltető kimenet:
/home/user/ds-work/s009/.venv-a/bin/python. A
sys.version ezzel szemben a futtató interpreter verzióját
adja; a kettő együtt használható gépnaplóba, mert egyazon
python név mögött is több verzió állhat a PATH sorrendjétől
függően.
A virtuális környezet (virtual environment, venv)
nem a Python másolata, hanem egy mappa, amely saját
interpreter-hivatkozást és saját csomagtelepítési területet tart fenn. A
python -m venv .venv-a parancs létrehozza például a
következő szerkezetet Linuxon:
.venv-a/bin/python – az indítószkript,.venv-a/lib/python3.12/site-packages/ – az ide települő
csomagok,.venv-a/pyvenv.cfg – a környezet beállításai.A pyvenv.cfg egyik kulcsa a
include-system-site-packages = false. Ez az alapértelmezés
biztosítja, hogy a rendszer Python csomagjai nem látszanak a venv-ből,
tehát a NumPy telepítése a venv-be ténylegesen izolált marad. A venv
tehát a Python saját környezetizolációs eszköze; a conda/mamba
alternatíva ezen túl natív binárisokat és akár más Python-verziót is
telepíthet, ezért a választást a későbbi s011 alfejezet
tárgyalja.
A sys.path a modulkeresés útvonalainak
listája. Amikor a kód import numpy-t ír, a Python e listán
sorban keresi a modult. Script futtatásakor az első elem a script
könyvtára, -c kapcsolóval vagy interaktív módban pedig az
aktuális munkakönyvtárat jelölő üres string. A venv
site-packages könyvtára a lista végén szerepel, de a
konkrét betöltést nem a lista puszta léte, hanem a modul
__file__ attribútuma bizonyítja:
import numpy as np
print(np.__file__)Szemléltető kimenet:
/home/user/ds-work/s009/.venv-a/lib/python3.12/site-packages/numpy/__init__.py.
Ha ez az útvonal nem a sys.prefix alatt kezdődik, akkor a
NumPy máshonnan jött, például a PYTHONPATH környezeti
változóból vagy a rendszer Python telepítéséből.
A PATH ezzel szemben operációsrendszer-szintű
keresőlista: amikor a terminálban a python vagy a
pip parancsot begépeljük, az operációs rendszer e lista
elemeit járja végig. Linuxon a which python csak az első
találatot mutatja, a type -a python viszont az összes
jelöltet; Windowson a Get-Command python -All a
PowerShell-megfelelő. A source .venv-a/bin/activate parancs
épp ezt a listát írja át az adott terminálablakban: a venv
bin könyvtára kerül előre. A direkt interpreter-hívás,
például .venv-a/bin/python script.py, megkerüli ezt a
PATH-függést, mert a teljes útvonalat adjuk meg.
A pip a Python csomagtelepítője. A pip
parancs valójában egy Python-szkript, amelynek saját interpreter-kötése
van; a python -m pip viszont az adott
python-hoz tartozó pip modult futtatja. Ez a különbség
akkor válik fontossá, ha a PATH-on a pip más Pythonhoz
tartozik, mint a python. A pip --version
kimenete elárulja a hozzá tartozó Pythont:
Szemléltető kimenet:
pip 24.2 from /home/user/ds-work/s009/.venv-a/lib/python3.12/site-packages/pip (python 3.12).
A python -m pip show numpy parancs Location
mezője pedig a telepített NumPy mappáját adja; ennek egyeznie kell a
futó kód numpy.__file__ útvonalának elejével.
A következő munkamenet Linuxon vagy macOS-en bash-terminálban fut,
CPython 3.12 és PyPI-hálózat elérhetősége mellett. A munkakönyvtár
legyen ~/ds-work/s009. A példa a venv/pip alternatív utat
mutatja; a könyv fő útja az uv, amelyet a következő, s010
alfejezet vezet be.
mkdir -p ~/ds-work/s009
cd ~/ds-work/s009
python3.12 -m venv .venv-a
python3.12 -m venv .venv-b
.venv-a/bin/python -m pip install numpy==1.26.4
.venv-b/bin/python -m pip install numpy==2.0.2A verziószámok itt példák; a ténylegesen elérhető verziókat a PyPI
aktuális listája adja meg, ezért a helyi futtatáskor a telepítési log
rögzítendő. Ezután hozzunk létre egyetlen env_report.py
fájlt:
import json
import sys
def main():
try:
import numpy as np
except ImportError:
np = None
report = {
"executable": sys.executable,
"prefix": sys.prefix,
"python_version": (
f"{sys.version_info.major}."
f"{sys.version_info.minor}."
f"{sys.version_info.micro}"
),
"sys_path": list(sys.path),
"numpy_version": None if np is None else np.__version__,
"numpy_file": None if np is None else str(np.__file__),
}
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()A scriptet mindkét környezet interpreterével futtatjuk, aktiválás nélkül:
.venv-a/bin/python env_report.py > report-a.json
.venv-b/bin/python env_report.py > report-b.jsonA report-a.json szemléltető, elméletileg várt
tartalma:
{
"executable": "/home/user/ds-work/s009/.venv-a/bin/python",
"prefix": "/home/user/ds-work/s009/.venv-a",
"python_version": "3.12.7",
"sys_path": [
"/home/user/ds-work/s009",
"/home/user/ds-work/s009/.venv-a/lib/python312.zip",
"/home/user/ds-work/s009/.venv-a/lib/python3.12",
"/home/user/ds-work/s009/.venv-a/lib/python3.12/lib-dynload",
"/home/user/ds-work/s009/.venv-a/lib/python3.12/site-packages"
],
"numpy_version": "1.26.4",
"numpy_file": "/home/user/ds-work/s009/.venv-a/lib/python3.12/site-packages/numpy/__init__.py"
}A report-b.json ugyanezen a gépen csak a
.venv-b útvonalakban és a NumPy-verzióban tér el,
2.0.2 értékkel. Windowson a munkamenet szerkezete azonos,
de a binárisok a Scripts mappában vannak, és a
py launchert használjuk:
mkdir $HOME\ds-work\3.1
cd $HOME\ds-work\3.1
py -3.12 -m venv .venv-a
py -3.12 -m venv .venv-b
.venv-a\Scripts\python.exe -m pip install numpy==1.26.4
.venv-b\Scripts\python.exe -m pip install numpy==2.0.2
.venv-a\Scripts\python.exe env_report.py | Out-File -Encoding utf8 report-a.jsonA sys.executable itt például
C:\Users\user\ds-work\3.1\.venv-a\Scripts\python.exe
alakú.
A két jelentés együtt bizonyítja az izolációt: azonos
env_report.py kód, azonos Python-verzió, de eltérő
prefix, eltérő site-packages és eltérő
NumPy-verzió. A kézi kontroll invariáns: a numpy_file
stringnek a prefix stringgel kell kezdődnie. Ha ez nem
teljesül, a telepítés nem a várt környezetbe került, vagy egy
PYTHONPATH elem elé vág. A sys_path első eleme
itt /home/user/ds-work/s009, tehát a script könyvtára; ez
nem a NumPy forrása, ezért nem szabad a modulkeresés bizonyítékaként a
lista puszta jelenlétére hagyatkozni.
Az aktiválás elhagyása szándékos. A .venv-a/bin/python
teljes útvonalas hívás akkor is ugyanoda mutat, ha a terminál PATH-ja
közben megváltozott. A source .venv-a/bin/activate csak
kényelmi réteg: rövid parancsneveket ad, de nem változtatja meg a már
futó folyamatok környezetét. A környezetjelentés tehát akkor értékes, ha
a tényleges futtató interpreterrel készül, nem a terminál aktuális
PATH-állapotából következtetünk.
Interpreterazonosság. A python, python3 és
py parancs ugyanazon gépen sem feltétlenül ugyanazt az
interpretert választja. A telepítési és ellenőrzési példákban a
kiválasztott interpretert a sys.executable értékével
ellenőrizzük, és a csomagkezelést ehhez kötjük:
python -m pip .... A virtuális környezet izolációja
elsősorban a Python-csomagokra vonatkozik, nem teljes
operációsrendszer-szintű konténerizáció.
Ellenpróba: a környezet aktiválása után futtasd a
python -c 'import sys; print(sys.executable)' ellenőrzést,
majd a notebook kernelében is nézd meg ugyanazt az értéket. Ha
különböznek, a notebook nem bizonyítottan a kívánt környezetből fut.
Értelmezés. Egy terminálban a
which python az /usr/bin/python útvonalat írja
ki, de a python -c "import sys; print(sys.executable)"
parancs a /home/user/ds-work/s009/.venv-a/bin/python
értéket adja. Nevezzen meg legalább két lehetséges magyarázatot, és adja
meg azt a parancsot, amellyel az összes python jelöltet
kilistázná.
Implementálás. Egészítse ki az
env_report.py scriptet egy --check
kapcsolóval. A kapcsoló jelenléte esetén a script ne csak kiírja a
jelentést, hanem ellenőrizze is, hogy a betöltött NumPy
__file__ útvonala a sys.prefix alatt kezdődik,
és a futtató Python legalább 3.12-es. Ha bármelyik feltétel sérül, 1
kilépési kóddal térjen vissza; sikeres ellenőrzésnél 0-val. Ügyeljen az
útvonal-normalizálásra Windows és Linux esetén.
Hibadiagnózis. Egy felhasználó a
rendszerterminálban kiadta a pip install numpy==2.0.2
parancsot, de a már megnyitott Jupyter notebookban az
import numpy as np; np.__version__ továbbra is
1.26.4. Magyarázza el a két legvalószínűbb okot, és írja le
azokat a parancsokat, amelyeket a notebook cellájában, illetve a
terminálban futtatva eldönthető, melyik környezetbe települt a 2.0.2-es
NumPy.
Megváltozott feltétel. Egy következő gépen nincs
python3.12 nevű parancs: a python3 a CPython
3.12-t, a python viszont csak 3.9-et indít. Írjon egy
ellenőrző parancsot a venv létrehozása előtt, amely megtagadja a futást,
ha a kiválasztott interpreter 3.12-nél régebbi, majd a
python3 használatával hozza létre a .venv-a
környezetet. Választhat bash vagy PowerShell megoldást.
1. megoldás. Az egyik magyarázat, hogy a
source .venv-a/bin/activate után a shell PATH-ja
tartalmazza a .venv-a/bin mappát, de a which
egy másik terminálablakból futott. A másik, hogy a shell a korábbi
python útvonalat gyorsítótárban tartja (hash),
és az aktiválás után hash -r nélkül a régi találatot adja
vissza. A teljes lista Linuxon: type -a python; Windowson
PowerShellben: Get-Command python -All. Az ellenőrzés
lényege: a which egyetlen találat, a type -a
az összes PATH-jelölt.
2. megoldás. A kiegészített script kulcsrésze:
import argparse
from pathlib import Path
def check(report):
prefix = Path(sys.prefix).resolve()
ok = True
if report["numpy_file"] is not None:
np_file = Path(report["numpy_file"]).resolve()
try:
np_file.relative_to(prefix)
except ValueError:
ok = False
if sys.version_info < (3, 12):
ok = False
return okA Path.resolve() szimbolikus hivatkozásokat és relatív
elemeket is abszolút útvonallá alakít, így a relative_to
nem szembesül a Windows C:\ és Linux / alakok
keveredésével. A --check argumentumot az
argparse elemzi; hiba esetén sys.exit(1),
siker esetén sys.exit(0). Az invariant tehát nem
string-összehasonlítás, hanem „a NumPy fájl a prefix mappa alatt
van”.
3. megoldás. A legvalószínűbb ok, hogy a terminál
pip parancsa nem a notebook kerneléhez tartozó Pythonhoz
kötődik, hanem egy másik környezethez. A notebook kernel ugyanis már
futó folyamat; a terminál telepítése nem módosítja annak
sys.path állapotát. A diagnosztika a notebook
cellájában:
import sys
print(sys.executable)
print(sys.prefix)
import numpy as np
print(np.__version__)
print(np.__file__)A terminálban ugyanezt a környezetet a
python -m pip show numpy Location mezőjével
vetjük össze. Ha a notebook sys.executable értéke
.venv-b/bin/python, a telepítés pedig a rendszer Pythonba
ment, a kettő természetesen eltér. A javítás nem a PATH átírása, hanem a
notebook kernelének újraindítása a megfelelő interpreterrel, vagy a
telepítés a kernel interpreterével:
import sys; !{sys.executable} -m pip install numpy==2.0.2.
4. megoldás. Bash változat:
python3 -c "import sys; sys.exit(0 if sys.version_info >= (3, 12) else 1)" \
&& python3 -m venv .venv-aPowerShell változat, ha a py launcher elérhető:
py -3.12 -c "import sys; assert sys.version_info >= (3, 12), sys.version"
py -3.12 -m venv .venv-aA feltétel célja, hogy a 3.9-es python soha ne kerüljön
a létrehozó parancsba. A python3 név itt verzióellenőrzés
után kap szerepet; ha a gépen a python3 is 3.11 lenne, a
parancs a venv létrehozása előtt leáll, ami a kívánt viselkedés.
A leggyakoribb tévedés: a terminálban aktivált környezet
automatikusan átváltja a már futó notebook kernelét. Tegyük
fel, hogy a notebook a .venv-b kernellel fut; a felhasználó
ezután egy másik terminálban kiadja a
source .venv-a/bin/activate parancsot, majd a notebook
cellájába írja: import numpy as np; print(np.__version__).
A kimenet továbbra is a .venv-b NumPy verzióját adja, mert
a kernel folyamata már elindult, és az aktiválás csak az adott terminál
jövőbeli gyermekfolyamatainak PATH-ját módosította. A hibás
következtetés így hangzik: „most már jó, hiszen a terminálban aktiválva
van a környezet”. A helyes ellenőrzés a notebookban a
sys.executable kiíratása; ha az még
.venv-b/bin/python, akkor a környezet nem váltott. Ez a
probléma rokona annak, amit az s005 alfejezet tárgyalt: a
notebook kernel CWD-je sem a notebook mappája automatikusan, ezért
minden futó folyamatot a saját állapotából kell bizonyítani, nem a
terminál látszólagos állapotából.
sys.executable és a
which python információja között?sys.path lista puszta jelenléte,
hogy a NumPy a venv-ből töltődött be?report-a.json és report-b.json mely
mezőinek kell eltérniük, és melyiknek kell azonosnak lenniük az izoláció
igazolásához?numpy.__file__ nem a
sys.prefix alatt kezdődik?A futó környezet kiléte nem hitkérdés: a sys.executable,
a sys.prefix, a sys.path és a modul
__file__ mezője együtt megmutatja, melyik interpreter és
melyik site-packages szolgálja ki a kódot. A venv akkor izolál, ha a
megfelelő interpretert indítjuk; a PATH és a csupasz pip
parancs eltérése gyakori forrása a csendes keveredésnek. Két környezetre
lefuttatott környezetjelentés a verziók és útvonalak összevetésével
bizonyítja a szétválasztást. A következő alfejezet ezt a szerződést
szigorítja: az uv zárolt függőségfeloldást készít, és a
--locked ellenőrzést teszi a reprodukálhatóság
kapujává.
Platformellenőrzés. Windows PowerShellben aktiválás
nélkül is ellenőrizhető a környezet saját interpretere:
./.venv/Scripts/python.exe --version. Unix-szerű rendszeren
a megfelelő útvonal ./.venv/bin/python --version. A
rendszer python parancsának elérhetősége önmagában nem
bizonyítja, hogy az izolált környezet aktív.