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ó.