2.41. Otklanjanje pogrešaka¶
Većina skripti koje zakažu na kameri zakaže na jedan od tri načina: podignu iznimku, daju pogrešnu vrijednost ili se zablokiraju. Svaki od njih ima drukčiji skup alata.
2.41.1. Čitanje povratnog traga (traceback)¶
Kada skripta podigne iznimku, a nitko je ne obradi, REPL ili IDE ispisuje traceback – zapis lanca poziva od najvanjskije skripte sve do retka koji je podigao iznimku.
Traceback se čita odozdo prema gore:
Donji redak imenuje klasu iznimke i njezinu poruku (
ValueError: invalid literal for int()...).Svaki blok
File "...", line N, in <name>iznad njega jest okvir – jedan poziv dublje kako se penjete prema gore.Najgornji okvir mjesto je gdje je skripta započela; najdonji okvir mjesto je gdje je pogreška nastala.
Najprije pročitajte donji redak da biste saznali što je pošlo po zlu, a zatim se penjite prema gore da biste vidjeli kako je skripta tamo dospjela. Brojevi redaka pokazuju na točne lokacije u izvornom kodu skripte.
2.41.2. Otklanjanje pogrešaka ispisom (print)¶
Najbrži način da saznate što skripta radi jest ispisati sumnjive vrijednosti. Tri ugrađene funkcije čine ispise korisnijima:
repr()– vraća programerski oblik niza za neku vrijednost.print(repr(value))razlikuje"5"od5iNoneod"None", što običniprint()ne može.type()– vraća klasu vrijednosti.print(type(value))način je da saznate je li varijabla koja „bi trebala biti int” potajno niz znakova.len()– duljina niza ili kolekcije. Iznenađujuće velik dio pogrešaka su pogreške pomaka za jedan ili neusklađenosti veličina.
print("got:", repr(value), "type:", type(value), "len:", len(value))
Ubacite ispis u svaku granu koja vam je važna – u obje grane if-a, u svaki except blok, u tijelo petlje za koju sumnjate da se izvršava nula puta. Cijena je jedan redak ispisa; vrijednost je spoznaja izvršava li se uistinu onaj put kroz kod za koji mislite da se izvršava.
2.41.3. Istraživanje objekta¶
Dvije ugrađene funkcije odgovaraju na pitanje „što mogu raditi s ovom stvari”:
dir()– vraća popis svakog imena definiranog na objektu: metode, atribute, dunder metode, sve.help()– ispisuje docstring (a na CPythonu i potpis) funkcije, metode ili klase.
Koristite ih zajedno: dir pronalazi ime, help objašnjava što ono radi.
2.41.3.1. Pronalaženje imena pomoću dir¶
>>> dir([1, 2, 3])
['__add__', '__class__', '__contains__', '__delitem__',
'__eq__', '__ge__', ..., 'append', 'clear', 'copy',
'count', 'extend', 'index', 'insert', 'pop', 'remove',
'reverse', 'sort']
Prvi dio popisa čine dunder metode naslijeđene od svakog objekta; imena vrijedna pregledavanja obično su nakon njih. dir radi na bilo čemu – na klasi, instanci, modulu, ugrađenom tipu:
>>> import json
>>> dir(json)
['__name__', 'dump', 'dumps', 'load', 'loads']
Taj drugi oblik način je da saznate koja imena na najvišoj razini neki modul stvarno izlaže, a da pritom ne napustite REPL.
2.41.3.2. Provjera pomoću help¶
Kada dir izbaci na površinu kandidata, help ga opisuje:
>>> help(str.split)
split(sep=None, maxsplit=-1)
Return a list of the words in the string, ...
Na MicroPythonu je help skromniji nego na CPythonu – ponekad samo potpis, ponekad jednoredni docstring, ponekad ništa za ugrađene C funkcije. Ipak je brz podsjetnik kada IDE-ov opis pri prelasku mišem nije pri ruci.
2.41.4. Kada se nešto zablokira¶
Skriptu koja se ne vraća teže je dijagnosticirati od one koja podiže iznimku. Uobičajeni krivci:
Petlja
whilečiji uvjet nikada ne postane neistinit. Dodajte ispis varijable petlje u svakoj iteraciji; ako se vrijednost ne mijenja, tijelo petlje ima pogrešku.Blokirajući poziv koji čeka ulaz koji nikada ne stigne – čitanje iz prazne reda čekanja, spavanje bez kraja. Okružite poziv ispisima kako biste vidjeli na kojem je retku skripta zapela.
Beskonačna rekurzija. Traceback kada se na kraju aktivira (uz
RecursionError) obično pokazuje točno na nju.
Najučinkovitiji oporavak za zablokiranu skriptu jest IDE-ov gumb stop, koji skripti šalje KeyboardInterrupt preko USB-a. Prekid izranja kao traceback na retku koji se trenutačno izvršava – često upravo onom retku koji se ne vraća.
Napomena
Ako se blokada opire svakoj dijagnostici – skripta se čini ispravnom, traceback prekida pokazuje na ugrađenu funkciju ili na kod firmwarea umjesto na vašu skriptu, ili je isti kod radio na prethodnoj verziji firmwarea – uzrok može biti pogreška u firmwareu, a ne u skripti. Smanjite skriptu na najmanji primjer koji još uvijek izaziva blokadu i otvorite prijavu na OpenMV forumu. Uključite verziju firmwarea, ploču na kojoj se izvršavao i smanjenu skriptu.
2.41.5. Uklonite dijagnostiku prije isporuke¶
Strateški ispisi tijekom razvoja su sjajni; stotinu poziva print ostavljenih u produkcijskoj skripti zatrpava ispis i koristi heap koji bi stvarni posao mogao koristiti. Kada se pogreška ispravi, uklonite ispise (ili ih zaštitite iza zastavice za otklanjanje pogrešaka koju možete isključiti).
Za dijagnostiku koja bi trebala dugoročno ostati u kodu, prijeđite s print() na modul logging. On svakoj poruci pridružuje razinu (debug, info, warning, error) i omogućuje da jedna postavka utiša one tihe u produkciji:
import logging
log = logging.getLogger("main")
log.info("starting up")
log.debug("loaded config: %s", config)
log.warning("falling back to defaults")
Postavljanje razine zapisivača na logging.WARNING čini da pozivi info i debug praktički ništa ne koštaju (niz s porukom nikada se ne gradi), bez potrebe za zakomentiravanjem redaka. To logging čini pravim alatom za trajnu dijagnostiku; obični print u redu je za jednokratnu.