Kihagyás

Kódminőség

A Python service-ben számos eszközt alkalmazunk, hogy a kódminőség magas szintű és konzisztens legyen. Az oldal ezen eszközök leírását, illetve a beállításukhoz és konfigurációjukhoz szükséges információkat tartalmazza.

Általánosan elmondható, hogy az adott eszközök konfigurációs beállítása a pyproject.toml fájl megfelelő tábláiban találhatók. Ezek szerkesztésénél ügyeljük arra, hogy a fájl átlátható maradjon, ne szóródjanak szét azonos egységhez tartozó táblák.

Több eszköz fut merge request-eknél is, azaz a rossz kódminőség képes gátolni a merge-elhetőséget. Az eszközöket, melyekre ez fennáll, a 🚀 ikonnal jelezzük, illetve az alábbi összefoglaló táblázat is mutatja.

Eszköz Funkció Ellenőrzött?
Black Kódformázás ✅
Ruff Linter, logikai és minőségbeli javítások ✅
Pyright Típusellenőrzés ❌
Deptry Függőséghasználat ellenőrzése ❌
Beépített hibajelzések JetBrains IDE-kben

A JetBrains IDE-knek (PyCharm, IntelliJ) van saját kódformátum, linting és type checking megoldásuk is. Ez alapvetően nem probléma, ugyanakkor zavaró lehet, hogy ugyanazt a hibát több dolog is jelzi, illetve a beépített linter és type-checker többször jelez olyan dolgokat, amik valójában nem állnak fenn. Emellett a kódformátum nincs bekonfigurálva, hogy egyezzen a megoldásunkkal.

A beépített eszközöket a beállításokban a következő helyeken tudjuk konfigurálni:

EditorCode SytlePython EditorInpsections → Keresés: Python

JetBrains IDE verziók

Korábbi IntelliJ verzióknál számos beállítás nem létezik, melyeket az integrációknál leírunk, ugyanis a plugin-eknek nem lehet a legfrissebb verzióját letölteni. Emiatt érdemes mindig a legfrissebb IntelliJ verziót telepíteni. Ez PyCharm-ra is fennáll.

Formázás – Black 🚀

Egységes kódformátum biztosítására a Black formázót használjuk. A teljes kódbázis formázását a következő parancs segítségével tehetjük meg:

pixi run fix-format

Emellett használhatjuk a pixi run check-format parancsot is, ez kizárólag ellenőrzi a kódformátumot, de nem javítja azokat.

Almappa ellenőrzése

Ha kizárólag egy almappát vagy egy fájlt akarunk ellenőrizni, az megtehetjük a pixi run black <útvonal> parancs kiadásával. A check-format parancs is ezt alias-olja.

Felsorolásvégi vessző

A Black formázó tiszteletben tartja a felsorolásvégi záró vesszőt, amit azt jelenti, hogy a

(
    <elem1>,
    <elem2>,  # <-- fontos ez a vessző
)

alakú kódot nem fogja átalakítani

(<elem1>, <elem2>)

alakúvá. Ez nem áll fenn, ha ezt lehagyjuk. Így tehát ügyeljünk arra, hogy ha a sorok tördelését meg akarjuk tartani például lista, dictionary vagy függvénydefiníciók esetén, akkor ne felejtsük el a felsorolásvégi vesszőt.

Linting – Ruff 🚀

Általános kódminőség fenntartására a Ruff linter-t alkalmazzuk. Ez segít abban, hogy elkerüljünk gyakori formai és logikai hibákat, inkonzisztenciákat, illetve modern kódhasználatot kényszerít.

A teljes kódbázis ellenőrzését a következő parancs segítségével tehetjük meg:

pixi run lint

Amennyiben a parancs után elérési utat specifikálunk, az adott mappa tartalmát tudjuk ellenőrizni. Az automatikusan javítható hibákat a pixi run lint-autofix parancs segítségével javíthatjuk.

A Ruff-nak van a nagyobb IDE-kben integrációja, ami mutatja a hibák helyét, illetve leírást hozzájuk. Most részletezzük a beállítási folyamatot.

Navigáljunk el a Plugin marketplace-re (Settings > Plugins > Marketplace) és telepítsük a Ruff nevű plugint.

Note

Feltétele a Ruff futásának, hogy a JetBrains IDE megtalálja a ruff csomagot a környezetben. Mindenesetre ez a függőségek része.

Telepítsük a Ruff nevű bővítményt. Állítsuk be a következőt:

"ruff.importStrategy": "fromEnvironment"
Tippek type hintek javítására

A type-hintek meghatározásában segíthet, ha alkalmazunk valami olyan plugin-t, ami mutat inlay hint-eket. Erre egy lehetséges segítség JetBrains IDE-ben a Python Inlay Params plugin, ami automatikusan mutatja függvények return típusát, amennyiben sikerül azt kitalálnia.

VS Code-ban van beépítve hasonló funkcionalitás, a következő beállításokat kell bekapcsolni hozzá:

"editor.inlayHints.enabled": "on",
"python.analysis.inlayHints.functionReturnTypes": true,
Tippek függvénykomplexitási hibák javítására

A ruff-ban vannak különböző kód komplexitás mértékek. Ezek főleg azon alapszanak, hogy hány és mennyire mély if-else-ekben gondolkodik a kód (illetve loop, try, stb…). Ha adott függvényben nagyon sok a szétválasztás, arra linter hibát kapunk.

Ezt több módon lehet kezelni. Rengetegszer előfordul, hogy egy kisebb-nagyobb if-else-ben valójában egyetlen változónak a konvertálását végezzük. Ekkor ezt pl. kivezethetjük egy ‘_convert_x_variable_to_y’ jellegű függvénybe.

Másik ilyen jellegű eset lehet egy hiba elkapás és dobás valami váratlan esetben adott állapotban. (Pl. két listának ugyanolyan hosszúnak kéne lennie, de mégse azok, stb…) Ekkor ezt az ellenőrzést kivezethetjük egy _assert_x_is_good(x) -> None függvénybe, ami ha minden jó, nem csinál semmit, ha pedig valami nem okés, akkor dob egy hibát.

Egyéb esetek közé tartozik, hogy ha a függvényünk jól definiált lépésekből áll úgy, hogy a lépéseken belül csak olyan dolgokat számolunk, amik később nem kellenek (a kimeneten kívül), akkor ezeket is függvényekké alakíthatjuk.

Persze van, hogy egy komplex függvényt így sem lehet egyszerűbbé tenni, ekkor akár ignorálni is lehet a hibát.

Hibák ignorálása

Alapvetően minden hiba ellenőrzése be van kapcsolva, néhány kivétellel (lásd pyproject.toml). Ehhez indokolt esetben lehet kivételt hozzávenni. Ugyanakkor globális szinten ne ignoráljunk olyan hibát, ami máshol problémákhoz vagy kódminőség romláshoz vezethet (pl. exception catching figyelmeztetések). Emellett globális ilyenkor írjunk egy indoklást, hogy miért vettük fel a globális ignore-ok közé.

Ignorált hibák
Hibakód Indok
ASD123 BSD123

Esetenkénti alapon lehet, sőt, néha kell is hibákat ignorálni. Ennek klasszikus példája, hogy ha valami failsafe függvényt gyártunk. Ekkor ugyanis bármilyen exception-t el akarunk kapni, viszont erre a linter hibát dob. Nyilván ekkor a hiba ignorálható, mivel a függvény explicit célja a bare except használat.

További példák közé tartozik a már korábban említett „megoldhatatlan komplexitás“ problémája, illetve esetenként a dunder method docstring-ek sem túl indokoltak.

Ignorálni a hibát az adott sor végére írt # noqa: <hibakód> vagy # noqa: <hibakód1>, <hibakód2>[, ...] kommenttel lehet. Fájl szinten is lehet ignorálni adott hiba összes megjelenését. Ehhez a fájl elejére kell beilleszteni a # ruff: noqa: <hibakód> kommentet.

Type checker – Pyright

Formázás és kódminőség ellenőrzés mellett célszerű egy statikus típusellenőrző használata is, hiszen ezzel számos hibát azok bekövetkezése előtt le tudunk kezelni.

Több statikus típusellenőrző van Python-hoz, ezek közül a kettő legnagyobb a MyPy illetve a Pyright. A Pyright jobban követi a PEP-et, illetve beépített opció VS Code-ban, így most ennek a beállítását részletezzük.

Astral ty

A Ruff készítőinél jelenleg folyamatban van egy type checker gyártása, ami a ty névre hallgat. Jelenleg ez még nagyon alpha fázisban van, ugyanakkor érdemes követni a fejlődését, mert lehet, versenyképes alternatíva lesz a Pyright-ra.

Navigáljunk el a Plugin marketplace-re (Settings > Plugins > Marketplace) és telepítsük a Pyright, illetve LSP4IJ nevű plugin-eket. Ezután a ToolsPyrightProject beállítások írjuk felül a következőket:

  • Executable: az útvonalat megadja a pixi run which pyright parancs
  • Configuration file: <python-service mappa>/pyproject.toml
  • Language server executable: az útvonalat megadja a pixi run which python-langserver parancs
  • Running mode: LSP4IJ
  • Kapcsoljuk be a Prefix tooltip messages with "Pyright:" beállítást (opcionális)

IntelliJ Pyright config

A Pyright szerves részét képezi a Pylance bővítménynek, elegendő ezt telepíteni.

Pyright verzió a Pylance-ben

Mivel a Pylance saját Pyright-ot tart fenn, és nem az interpreterből táplálkozik, Python konfigurációtól kívülállóan kell felüldefiniálnunk a Pyright verziót, ha azt akarjuk, hogy ugyanaz a verzió fusson, ami a lock file-ban szerepel. Szerencsére ez lehetséges a kövektező beállítással

"python.analysis.pyrightVersion": <verzió>

Változtatásnál ügyelnünk kell arra, hogy mind a .vscode/settings.default.json fájlban, mind a függőségek között frissítsük a verziót.

Végül ha egy type checker-t bekapcsolunk, az bár általában hasznos, előfordul, hogy saját limitációi, külső programcsomagok mistype-olása, vagy esetleg kódstruktúrálási (pl. hibát dobó validációs függvények) okok miatt nem lehet hiba nélkül type-hintelni. Ekkor ha biztosak vagyunk benne, hogy indokolt, a type hibákat is lehet ignorálni, Pyright esetén a # pyright: ignore[<hibanév>] sorvégi kommenttel.

Univerzális hibaignorálás

A # type: ignore komment a PEP 484-ben került bevezetésre, azaz a Python specifikáció része, és így az összes type checker támogatja. Hátránya, hogy minden hibát figyelmen kívül hagy, azaz ha véletlen egy ismert hiba közvetlen közelében előjönne egy új type hiba, akkor azt a # type: ignore szintén ignorálja.

Helyette célszerű a # pyright: ignore[<hibanév>] flag-et alkalmazni, mivel ez hibaspecifikus, így sokkal kisebb valószínűséggel fed el egyéb hibákat.

Függőséghasználat – Deptry

Fontos, hogy függőségek, amiket a kódban használunk, és azok, melyeket a projekt definiál, azok összhangban legyenek. Ellenkező esetben előfordulhat, hogy egy modult kizárólag azért érünk el, mert egy másik csomaghoz szükséges, és akár egy verziófrissítéssel eltűnhet a telepített dependenciák közül.

Erre a problémára ad megoldást a Deptry, ami összeveti a pyproject.toml-ben definiált függőségeket a kódbázis által használtakkal. Funkciói a következők:

  • Jelenti a hiányzó függőségeket (DEP001)
  • Megtalálja a nem használt függőségeket (DEP002)
  • Felderíti a tranzitív (azaz egyéb csomag függőségei alapján telepített) függőségeket (DEP003)

A teljes projekt ellenőrzését a következő paranccsal tehetjük meg

pixi run check-imports

Hibák ignorálása

Itt nem feltétlen tudunk vagy értelmes minden hibát kezelni, sőt, előfordulhat, hogy valójában szükséges függőséghasználatra kapunk hibát. Erre példa a torch könyvtár definiálása, amit expliciten nem használ a kód, ezért DEP002 hibakódot kap, ugyanakkor a torch esetén verzióról verzióra változik a működés, ezért sokszor el szeretnénk kerülni, hogy egy dependency resolution folyamat határozza meg, mi kerül végül feltelepítésre.

Így tehát – bár törekedve arra, hogy adott függőség pontosan akkor legyen definiálva, ha használt is – eltérhetünk attól, hogy minden vizsgálva legyen.

Hibákat ignorálni a pyproject.toml-ben lehet, itt még nincs lehetőség kommentek segítségével megtenni ezt.