2.41. Hibakeresés#

A kamerán hibára futó szkriptek többsége háromféleképpen hibázik el: kivételt váltanak ki, rossz értéket állítanak elő, vagy lefagynak. Mindegyikhez más-más eszközkészlet tartozik.

2.41.1. A visszakövetés (traceback) olvasása#

Amikor egy szkript kivételt vált ki, és semmi sem kezeli azt, a REPL vagy az IDE kiír egy visszakövetést (traceback) – a hívási lánc feljegyzését a legkülső szkripttől egészen addig a sorig, amely a kivételt kiváltotta.

A visszakövetést alulról felfelé kell olvasni:

  • A legalsó sor megnevezi a kivétel osztályát és annak üzenetét (ValueError: invalid literal for int()...).

  • Minden File "...", line N, in <name> blokk fölötte egy képkocka (frame) – felfelé haladva egy-egy hívással mélyebbre jutunk.

  • A legfelső képkocka az, ahol a szkript elindult; a legalsó képkocka pedig az, ahol a hiba bekövetkezett.

Először a legalsó sort olvasd el, hogy megtudd, mi romlott el, majd haladj felfelé, hogy lásd, hogyan jutott el a szkript odáig. A sorszámok pontos forráshelyekre mutatnak a szkriptben.

2.41.3. Egy objektum felderítése#

Két beépített függvény válaszol arra a kérdésre, hogy „mit tudok kezdeni ezzel a dologgal”:

  • dir() – visszaadja egy objektumon definiált összes név listáját: metódusokat, attribútumokat, dundereket, mindent.

  • help() – kiírja egy függvény, metódus vagy osztály docstringjét (CPython-on a szignatúrát is).

Használd őket együtt: a dir megtalálja a nevet, a help pedig elmagyarázza, mit csinál.

2.41.3.1. Név keresése a dir segítségével#

>>> dir([1, 2, 3])
['__add__', '__class__', '__contains__', '__delitem__',
 '__eq__', '__ge__', ..., 'append', 'clear', 'copy',
 'count', 'extend', 'index', 'insert', 'pop', 'remove',
 'reverse', 'sort']

A lista első része a minden objektumtól örökölt dunder metódusokból áll; az átvizsgálásra érdemes nevek általában ezek után jönnek. A dir bármin működik – osztályon, példányon, modulon, beépített típuson:

>>> import json
>>> dir(json)
['__name__', 'dump', 'dumps', 'load', 'loads']

Ezzel a második alakkal deríthető ki, hogy egy modul valójában mely legfelső szintű neveket teszi elérhetővé, anélkül, hogy el kellene hagyni a REPL-t.

2.41.3.2. Utánanézés a help segítségével#

Miután a dir felszínre hozott egy jelöltet, a help leírja azt:

>>> help(str.split)
split(sep=None, maxsplit=-1)
    Return a list of the words in the string, ...

MicroPython-on a help szűkszavúbb, mint CPython-on – néha csak a szignatúra, néha egy egysoros docstring, néha pedig semmi a beépített C függvények esetén. Mégis gyors emlékeztető, amikor az IDE eszköztippje nincs kéznél.

2.41.4. Amikor valami lefagy#

Egy olyan szkriptet, amely nem tér vissza, nehezebb diagnosztizálni, mint egyet, amely kivételt vált ki. Gyakori okok:

  • Egy while ciklus, amelynek feltétele sosem válik hamissá. Adj hozzá egy nyomtatást a ciklusváltozóról minden iterációban; ha az érték nem változik, akkor a ciklus törzsében van a hiba.

  • Egy blokkoló hívás, amely olyan bemenetre vár, ami sosem érkezik meg – olvasás egy üres sorból, egy vég nélküli sleep. Vedd közre a hívást nyomtatásokkal, hogy lásd, melyik soron akadt el a szkript.

  • Egy végtelen rekurzió. A visszakövetés, amikor végül bekövetkezik (egy RecursionError kivétellel), általában egyenesen rámutat.

Egy lefagyott szkript leghatékonyabb helyreállítása az IDE stop gombja, amely egy KeyboardInterrupt kivételt küld a szkriptnek USB-n keresztül. A megszakítás visszakövetésként jelenik meg az éppen futó sornál – gyakran pontosan annál a sornál, amely nem tér vissza.

Megjegyzés

Ha egy lefagyás minden diagnosztikának ellenáll – a szkript helyesnek tűnik, a megszakítás visszakövetése egy beépített elemre vagy firmware-kódra mutat a saját szkripted helyett, vagy ugyanaz a kód egy korábbi firmware-verzión működött – akkor az ok inkább firmware-hiba lehet, mint szkripthiba. Csökkentsd a szkriptet a legkisebb olyan reprodukáló példára, amely még mindig lefagy, és nyiss egy bejelentést az OpenMV fórumon. Add meg a firmware verzióját, az alaplapot, amelyen futott, valamint a lecsökkentett szkriptet.

2.41.5. Távolítsd el a diagnosztikát szállítás előtt#

A fejlesztés során elhelyezett stratégiai nyomtatások remekek; de száz print hívás egy éles szkriptben hagyva összezavarja a kimenetet, és olyan heap-et használ, amelyet a valódi munka is használhatna helyette. Amikor egy hibát kijavítasz, vedd ki a nyomtatásokat (vagy bújtasd el őket egy hibakeresési jelző mögé, amit ki tudsz kapcsolni).

Az olyan diagnosztikához, amelynek hosszú távon a kódútvonalon kellene maradnia, válts a print() függvényről a logging modulra. Ez egy szintet (level) csatol minden üzenethez (debug, info, warning, error), és lehetővé teszi, hogy egyetlen beállítással elnémítsd a csendeseket éles környezetben:

import logging

log = logging.getLogger("main")
log.info("starting up")
log.debug("loaded config: %s", config)
log.warning("falling back to defaults")

Ha a naplózó szintjét logging.WARNING értékre állítod, akkor az info és debug hívások lényegében semmibe nem kerülnek (az üzenet karakterlánc soha nem épül fel), anélkül, hogy ki kellene kommentezni a sorokat. Ezzel a logging lesz a megfelelő eszköz a tartós diagnosztikához; a nyers print az eldobható diagnosztikához jó.