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.2. Hibakeresés nyomtatással#
A leggyorsabb módja annak, hogy kiderítsd, mit csinál egy szkript, ha kiírod a gyanús értékeket. Három beépített függvény teszi hasznosabbá a nyomtatást:
repr()– visszaadja egy érték fejlesztői stílusú szöveges alakját. Aprint(repr(value))megkülönbözteti a"5"és az5értéket, valamint aNoneés a"None"értéket, amire egy egyszerűprint()nem képes.type()– visszaadja egy érték osztályát. Aprint(type(value))segítségével deríthető ki, hogy az a változó, amelynek „int-nek kellene lennie”, titokban nem éppen egy karakterlánc-e.len()– egy sorozat vagy gyűjtemény hossza. A hibák meglepően nagy hányada eggyel-elcsúszott vagy méret-eltérési probléma.
print("got:", repr(value), "type:", type(value), "len:", len(value))
Helyezz el egy nyomtatást minden olyan ágban, ami érdekel – egy if mindkét ágában, minden except blokkban, egy olyan ciklus törzsében, amelyről azt gyanítod, hogy nulla alkalommal fut le. A költsége egy sornyi kimenet; a haszna pedig az, hogy kideríted, vajon az a kódútvonal fut-e, amelyikről gondolod, hogy fut.
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
whileciklus, 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
RecursionErrorkivé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ó.