Licencellenőrzés: az alkalmazáslicenc, a függőségek licence és a modell-/adatlicenc külön ellenőrzendő. A megengedett felhasználás nem következik abból, hogy a csomag telepíthető vagy a súlyok nyilvánosan elérhetők.
A Northstar lab csapata egy változáskérelmet kap: a szervezet fenntartási szabályzata szerint a projekt pandas-dependenciáját 3.x főverzióra kell emelni, mert az N-1 politika a 2.x sorozatot a következő negyedévben elavultnak fogja minősíteni. A döntést nem „majd kiderül” alapon hozzák meg, hanem verziókezelési szabállyal, licencellenőrzéssel, frissítési teszttel, platformkorláttal és költség-/karbantartási korláttal alátámasztott döntési lapot (decision sheet) készítenek.
Bemenetek: a lab/pyproject.toml verziótartománya, a
saját uv.lock a feloldás után, a
lab/starter_experiment.py, a projekt tesztjei és a meglévő
kontrolljegyzék. Megfigyelési egység: egy ellenőrzési tétel a döntési
lapon; minden tételhez érték, dátumozott forrás és állapot
(verified, pending, n/a)
tartozik. Kimenet: kitöltött döntési lap és
go/no-go döntés. Előzetes becslés: a
főverzió-emelés elsősorban az implicit láncolt értékadást (chained
assignment) törheti; mivel a kezdőprojekt explicit .loc-ot
használ, a kockázat várhatóan alacsony – de ez csak hipotézis, amíg a
frissítési teszt le nem fut.
Version policy (verziókezelési szabály). A
Python-ökoszisztéma jellemzően szemantikus verziózást használ:
MAJOR.MINOR.PATCH. A PATCH csak hibajavítást,
a MINOR visszafelé kompatibilis bővítést, a
MAJOR törő változást jelez. A pyproject.toml
tartománya szándék, a uv.lock a feloldás pillanatának
rögzített ténye: a tartományból később más verzió is feloldható, a
lockból nem. A policy két kérdést ír elő minden emelés előtt: hány
támogatott főverziót tartunk életben, és milyen teszt jár az egyes
emelési osztályokhoz. A példában az N-1 szabály azt jelenti, hogy a
legutóbbi stabil főverziót és az előtte lévőt tartják karbantartva; ha a
3.x stabil, a 2.x karbantartási ablak zárul. Az ellenőrizhető forrás a
csomag METADATA Requires-Python mezője és a
release notes.
Kis számpélda: 2.2.3 → 2.2.4 patch,
2.2.3 → 2.3.0 minor, 2.2.3 → 3.0.0 major. A
konzervatív policy patch-emelésnél füsttesztet, minor-emelésnél teljes
tesztet és numerikus kontrollt, major-emelésnél ezeken felül
kompatibilitási auditot és rövid canary-ablakot követel.
License check (licencellenőrzés). A csomag licencét nem elég egyszer ismerni: a célverziónál újra ellenőrizni kell, mert a licenc is változhat. A gyakori SPDX-azonosítók közül a permissive családba tartozik a MIT, BSD, Apache-2.0; a copyleft családba a GPL, a hálózati szolgáltatásnál különösen érzékeny AGPL; létezik kettős licencelés is. A döntést a felhasználás módja befolyásolja: belső script, tovább terjesztett csomag vagy nyilvános szolgáltatás különböző kockázati osztály. A döntési lap ezért nem „a pandas szabad-e” kérdést tesz fel, hanem azt, hogy melyik SPDX-kifejezés szerepel a célverzió metaadatában, és mikor nyitották meg az upstream LICENSE fájlt. Jogi állásfoglalást a lap nem helyettesít.
Upgrade test (frissítési teszt). A frissítés nem verziók cseréje, hanem viselkedésváltozás kockázata. Három réteg: füstteszt (import, alapművelet, ismert invariant), teljes projektteszt, majd reprodukálható numerikus kontroll. A kontroll nem „ránézésre ugyanaz”, hanem a Jelentés és nyelvek közötti adatcsere alfejezetben bevezetett abszolút-relatív tolerancia:
ahol és ugyanannak a mennyiségnek a jelölt és a baseline értéke ugyanabban a mértékegységben, az abszolút küszöb, a relatív küszöb. Egész értékeknél (sorazonosítók, darabszámok, minor unitok) bitpontos egyezést várunk; lebegőpontos értékeknél és észszerű szigorú választás.
Platform constraint (platformkorlát). Egy verzió
létezése nem jelenti, hogy az adott gépen fut. A lock feloldás a
Python-verzió, az operációs rendszer és a CPU-architektúra
kombinációjától függ: egy macOS arm64 wheel nem érvényes Linux
x86-64-en, és egy bináris csomag feltételezi a megfelelő
libc/BLAS-környezetet. A uv.lock platformcímkéken keresztül
rögzíti, mely wheel mely környezethez tartozik; a --locked
futtatás ezt ellenőrzi. A döntési lap ezért külön tételként rögzíti a
támogatott platformot, és nem engedi, hogy a fejlesztői gép sikeréből a
másik környezetre következtessünk.
Cost cap (költségplafon). Helyi bevezetésnél a „költség” karbantartási idő, managed notebook vagy felhőszolgáltatás esetén ellenőrzött díjszabásból számolt keret. A plafon négy elemet jelent: elszámolási metrika (pl. vCPU-óra, compute unit-óra, tárolt GB), riasztási küszöb (pl. a keret 80%-ánál), hard limit (a fölött a számítás leáll), és visszaállítási út (a régi helyi környezet visszaáll). Az aktuális díjat ez a könyv nem találja ki; a döntési lap a szolgáltató hivatalos árazó oldalát és az ellenőrzés dátumát kéri.
A munkamenet a lab projektgyökérből indul, bash alatt. A
szállított labban nincs uv.lock; a baseline lockot egyszer
kell létrehozni, és azt verziózni kell.
cd lab
uv lock
uv sync --locked
uv run pytest -qAz uv lock a pyproject.toml tartományaiból
felold és uv.lock-ot ír. Az uv sync --locked
megtagadja a futtatást, ha a lock és a projekt nem egyezik; a
--frozen a további aktualitásvizsgálatot is kihagyja. A
baseline teszt eredményét rögzítjük a döntési lapon.
Ezután a jelölt ágat külön worktree-ben nyitjuk, hogy a baseline érintetlen maradjon:
git worktree add ../lab-candidate pandas3-candidate
cd ../lab-candidate
# itt a pyproject.toml pandas-tartományát a 3.x sorozatra állítjuk
uv lock
uv sync --locked
uv run pytest -qA füstteszt a projekt adatán egy, a pandas-verziótól független invariánst ellenőriz:
import pandas as pd
df = pd.read_csv("data/orders.csv", parse_dates=["order_ts"])
assert df["target_late"].isin({0, 1}).all()
by_segment = df.groupby("segment")["basket_minor"].sum()
assert by_segment.sum() == df["basket_minor"].sum()
print(df["basket_minor"].sum())A csoportösszegek összege a teljes összeggel egyezik: ez a Spark DataFrame és végrehajtási terv alfejezetből
visszahívott relációs invariant. A kimenet szám; az ellenőrzés az
assert miatt már a kódon belül megtörtént.
A numerikus kontroll a starter_experiment.py kimenetét
hasonlítja össze a jelölt és a baseline környezetben:
cd ../lab-candidate
uv run python starter_experiment.py
cp artifacts/split.json ../lab/artifacts/split_candidate.json
cd ../lab
uv run python starter_experiment.py
cp artifacts/split.json artifacts/split_baseline.json
uv run python compare_splits.py artifacts/split_baseline.json artifacts/split_candidate.jsonA compare_splits.py gén specifikus, rekurzív
JSON-összehasonlító: egészekre és sztringekre bitpontos egyezést,
lebegőpontos értékekre a fenti toleranciaképletet alkalmazza.
import json
import math
import sys
ATOL = 1e-9
RTOL = 1e-7
def compare(a, b, path="$"):
if type(a) is not type(b):
return [f"{path}: típuseltérés {type(a).__name__} != {type(b).__name__}"]
if isinstance(a, dict):
out = []
for key in sorted(set(a) | set(b)):
if key not in a:
out.append(f"{path}.{key}: csak a jobb oldalon")
elif key not in b:
out.append(f"{path}.{key}: csak a bal oldalon")
else:
out.extend(compare(a[key], b[key], f"{path}.{key}"))
return out
if isinstance(a, list):
if len(a) != len(b):
return [f"{path}: hosszeltérés {len(a)} != {len(b)}"]
out = []
for i, (x, y) in enumerate(zip(a, b)):
out.extend(compare(x, y, f"{path}[{i}]"))
return out
if isinstance(a, float) and isinstance(b, float):
if math.isclose(a, b, rel_tol=RTOL, abs_tol=ATOL):
return []
return [f"{path}: {a} != {b} tűrésen kívül"]
return [] if a == b else [f"{path}: {a!r} != {b!r}"]
def main():
_, left, right = sys.argv
a = json.load(open(left, encoding="utf-8"))
b = json.load(open(right, encoding="utf-8"))
issues = compare(a, b)
if issues:
print("\n".join(issues))
sys.exit(1)
print("OK: a két split-kontroll a tűrésen belül egyezik.")
if __name__ == "__main__":
main()A listaág sorrendérzékeny: ha a két fájl listái eltérő sorrendben tartalmaznák ugyanazokat az azonosítókat, az hibának számít. Ez szándékos, mert a split fájlban a sorrend maga is része a szerződésnek.
A döntési lap sablonja:
| # | Tétel | Ellenőrzött érték | Forrás és dátum | Állapot |
|---|---|---|---|---|
| 1 | Baseline verzió | a jelenlegi lab pandas-verzió | saját uv.lock és futtatási jegyzék |
verified |
| 2 | Célverzió a policy szerint | 3.x jelölt; N-1 szabály | szervezeti policy verziószámmal | pending |
| 3 | Licenc | SPDX-kifejezés a célverzión | PyPI metadata + upstream LICENSE | pending |
| 4 | Platform wheel | Python-verzió, OS, architektúra | uv lock és PyPI wheel-lista |
pending |
| 5 | Füstteszt | import, csoportinvariáns zöld | lokális munkamenet | pending |
| 6 | Teljes tesztsor | uv run pytest -q zöld |
lokális munkamenet | pending |
| 7 | Numerikus kontroll | split.json egyezés a tűrésen belül |
compare_splits.py kimenet |
pending |
| 8 | Költség/karbantartás | n/a – helyi CPU-n | – | n/a |
| 9 | Visszaállítási terv | régi lock + uv sync --locked --frozen |
dokumentálva a repóban | verified |
A sablon nem kész döntés, hanem gyűjtőhely. A go
döntéshez minden következő feltételnek teljesülnie kell: (1) az 1–4.
tételeknél a forrás a tényleges ellenőrzés dátumával szerepel, és az
ellenőrzés nem régebbi 90 napnál; (2) az 5–7. tétel lokálisan lefutott,
a tolarencián belül; (3) a 9. tétel visszaállítási sora konkrét
parancsot tartalmaz, nem „megoldjuk”; (4) a 8. tétel n/a
mellett explicit módon kimondja, hogy nincs felhő- vagy
szolgáltatásköltség. Az n/a állapot itt maga is állítás: a
következő hónapban, managed notebookra átálláskor ezt a sort újra ki
kell tölteni, nem örökölhető.
A frissítési teszt numerikus értelmezése: ha a split-azonosítók
bitpontosan egyeznek, és a lebegőpontos mezők az abszolút-relatív
tűrésen belül maradnak, akkor a főverzió-emelés a kezdőprojekt döntési
felületét nem változtatta meg. Ez statisztikai értelemben nem
„bizonyíték arra, hogy a 3.x mindenhol azonos”, hanem egy konkrét
inputra, konkrét kontrollra vonatkozó megfigyelés. A
compare_splits.py kimenete a tanuló gépén lehet
OK: ...; automatikusan még nem ellenőrzött, ezért itt csak
a lehetséges kimenetet írtuk le.
Értelmezés. A következő verzióváltások közül
melyik patch, minor, major emelés a MAJOR.MINOR.PATCH
szabály szerint: (a) 2.2.3 → 2.2.4; (b)
2.2.3 → 2.3.0; (c) 2.2.3 → 3.0.0; (d)
3.0.0 → 3.1.0. A konzervatív policyben melyikhez elég csak
füstteszt, melyikhez kérünk teljes numerikus kontrollt is?
Megvalósítás. A csapat managed notebook
szolgáltatást vezetne be három elemző számára: Linux konténer, 4 vCPU,
havi kb. 40 használati óra, a bemenő adatok Parquet-fájlok helyi
hálózaton. Készíts a fenti mintához illeszkedő, legalább nyolcsoros
döntési lapot: minden sorhoz írj ellenőrizhető forrást, és ahol most nem
lehetséges az ellenőrzés, pending állapotot a szükséges
forrás megnevezésével. Aktuális díjat ne írj.
Hibadiagnózis. A kolléga a
pyproject.toml numpy>=1.26,<3
tartományával dolgozik, de nem készített uv.lock-ot, és
mindig sima uv sync-et futtat. Egy hónappal később a teszt
zöld, a numerikus kontroll azonban 1e-4 relatív eltérést
mutat a baseline kontrolljegyzékhez képest. Mi a policy hiba, mi
okozhatta az eltérést, és milyen paranccsal állítod helyre a
baseline-környezetet?
Megváltozott feltétel. Az előző managed notebook bevezetés költségplafonja: havi 100 elszámolási egység, riasztás 80 egységnél, hard limit 100 egységnél. Add meg: mit kell naponta mérni, mi történjen a hard limit elérésekor, és melyik helyi elem maradjon készenlétben visszaállítási útnak? Az egység árát ne tippeld; a metrika definícióját a szolgáltató fogalomtárából kell idézni.
Továbbvitel. A managed notebook kliensparancsa MIT licencű, ezért a kolléga szerint a teljes szolgáltatás ingyenes. Nevezd meg a lap azon sorát, amely ezt a következtetést megcáfolja, és add meg, milyen dokumentumot kell dátummal ellenőrizni.
1. (a) patch: PATCH nő, API- és
funkcióváltozás nincs; füstteszt elegendő. (b) minor: új funkció,
visszafelé kompatibilis; füstteszt mellett teljes tesztet és numerikus
kontrollt kérünk, mert a belső számítási sorrend akkor is változhat, ha
a publikus API nem tör. (c) major: törő változás lehetséges; a
konzervatív policy teljes kompatibilitási auditot, az összes függő
csomag ellenőrzését és rövid canary-időszakot ír elő. (d) minor, ugyanaz
az osztály, mint (b). A numerikus kontroll mindig a saját
kontrolljegyzékre vonatkozik, nem a csomag upstream tesztjeire.
2. Egy lehetséges lap (a 4. oszlop szerint minden
sor pending, ahol helyi futtatás vagy friss forrás
hiányzik):
| # | Tétel | Ellenőrzött érték | Forrás és dátum | Állapot |
|---|---|---|---|---|
| 1 | Szolgáltatástípus | managed notebook, Linux konténer | szolgáltató dokumentációja | pending |
| 2 | Verziópolicy | a szolgáltató által kínált környezetverzió | szolgáltató release notes | pending |
| 3 | Klienslicenc | MIT kliens, de a szolgáltatás külön vizsgálandó | kliens repo LICENSE | pending |
| 4 | Platform | 4 vCPU, Linux, Parquet-hozzáférés | szolgáltató instance-leírás | pending |
| 5 | Füstteszt | egy notebook betölt, olvas Parquet-ot | lokális próbagép | pending |
| 6 | Hozzáférés | 3 felhasználó, legkisebb jogosultság | szervezeti hozzáférési szabály | pending |
| 7 | Adatkijárat | Parquet be- és kimenet útvonala | hálózati terv | pending |
| 8 | Költségplafon | metrika, riasztás, hard limit | szolgáltató árazó és számlázó oldala | pending |
| 9 | Visszaállítás | helyi uv/Jupyter készenlétben | dokumentált | verified |
A táblázat nem tartalmaz árat, mert a feladat ezt tiltja; a 8. sor forrása pontosan megnevezi, mit kell később dátumozva ellenőrizni.
3. A tartomány fagyasztás nélkül lebegő ablak: egy
hónap alatt a numpy új patch/minor verziója kerülhetett a
környezetbe, és a 1e-4-es relatív eltérés tipikusan
lebegőpontos műveleti sorrend-változás vagy könyvtári algoritmusváltás
következménye, nem feltétlenül hiba. A policy hiba az, hogy lock nélkül
a „teszt zöld” nem ugyanaz a környezet, mint a kontrolljegyzék
készítésekor. A javítás: uv lock, a lock verziózása,
ezentúl minden futtatás uv sync --locked-dal. A
baseline-hoz tartozó régi lock visszaállítása után
uv sync --locked --frozen hozza létre a kontrolljegyzékkel
konzisztens környezetet; az eltérő verziókat a döntési lap 2. sorában
feljegyzik.
4. Metrika: a szolgáltató fogalomtárában definiált elszámolási egység fogyasztása naponta exportálva, a számlázási nézetből. A riasztási szabály: a kumulált havi fogyasztás 80 egység fölött figyelmeztetést küld a projekt tulajdonosának. Hard limit: 100 egységnél a futtató szkript leállítja az új számításokat; a meglévő adatokat a leállítási eljárás kimenti a szervezeti tárhelyre, mielőtt a szolgáltatás számítási hozzáférése felfüggesztődne. Visszaállítási út: a helyi uv/Jupyter környezet a Parquet-adatokkal a bevezetés előtti állapot szerint készenlétben marad, így a kísérleti munka nem áll le a szolgáltatás kikapcsolásával.
5. A lap 3. sora (klienslicenc) és 8. sora (költségplafon) együtt cáfolja a következtetést: az MIT licenc a klienskód felhasználására vonatkozik, nem a menedzselt infrastruktúra díjára. Ellenőrizendő a szolgáltató hivatalos árazó oldala, a szolgáltatási feltételek (Terms of Service) és az adatkijárat/tárhely díjszabása, mind dátumozva. A nyílt forrású kliens egy ellenőrzési tétel, nem az egész döntés.
A leggyakoribb tévedés, hogy a nyílt forrású kliensből a teljes
szolgáltatás ingyenességére következtetnek. Ellenpélda: a helyi DuckDB
Python-csomag MIT licencű, és önmagában tényleg ingyenesen használható;
ha azonban a csapat a DuckDB-t menedzselt szolgáltatásként vagy
felhőbeli tárhely felett futtatja, a számítási erőforrás, a tárhely és a
hálózati kijárat költsége külön szerződés tárgya. Ugyanez igaz a managed
notebook kategóriákra: a nyílt forrású kliens vagy a Jupyter-projekt
licence semmilyen állítást nem tesz a mögötte álló infrastruktúra
áráról. A döntési lap 8. sora ezért mindig külön tétel, és a
n/a állapot is dátumozott döntés, nem örökölt
alapbeállítás. Ez a tétel a CPU, GPU és elosztott
rendszer közötti döntés alfejezetből ismert költség- és
erőforrás-korlát gyakorlati megvalósítása; a numerikus kontroll a Jelentés és nyelvek közötti adatcsere
toleranciaszerződését, a hibatűrés pedig a Hibaszeletek és felelős értékelés alfejezetben
tanult, számokkal alátámasztott értékelési szemléletet viszi tovább.
Önellenőrzés: minden döntési lapsorban van-e dátumozott forrás? A
lock és a projekt együtt van-e verziózva? A frissítési teszt a saját
kontrolljegyzékre futott-e, nem az upstream dokumentációjára? A
visszaállítási terv konkrét parancsot tartalmaz-e? Ha bármelyik válasz
nem, a döntési lap még pending, nem go.
Az eszközbevezetés nem egyetlen parancs, hanem öt összetevős döntési folyamat. A verziókezelési szabály rögzíti, milyen emeléshez milyen teszt jár; a licencellenőrzés mindig az adott célverzión fut, dátumozott forrással; a frissítési teszt füsttesztből, teljes tesztsorból és numerikus kontrollból áll, előre rögzített abszolút-relatív toleranciával; a platformkorlát a wheel és a natív környezet együttesét vizsgálja; a költségplafon metrikát, riasztást, hard limitet és visszaállítási utat követel. A döntési lap minden sora ellenőrzött érték–forrás–dátum–állapot négyest hordoz, és a nyílt forrású kliens ingyenessége semmikor nem bizonyítja a mögöttes szolgáltatás költségmentességét. Következő alkalmazásként a Saját data science munkaasztal specifikációja saját data science munkaasztal specifikációját állítja össze ezekből a kritériumokból.