2.41. Ladění¶
Většina skriptů, které na kameře selžou, selže jedním ze tří způsobů: vyvolají výjimku, vyprodukují nesprávnou hodnotu, nebo se zaseknou. Každý z nich vyžaduje jinou sadu nástrojů.
2.41.1. Čtení tracebacku¶
Když skript vyvolá výjimku a nic ji neobslouží, REPL nebo IDE vypíše traceback – záznam řetězce volání od nejvzdálenějšího skriptu až po řádek, který výjimku vyvolal.
Traceback se čte zdola nahoru:
Spodní řádek pojmenovává třídu výjimky a její zprávu (
ValueError: invalid literal for int()...).Každý blok
File "...", line N, in <name>nad ním je snímek (frame) – o jedno volání hlouběji, jak postupujete nahoru.Úplně nejvyšší snímek je místo, kde skript začal; úplně nejnižší snímek je místo, kde chyba nastala.
Nejprve si přečtěte spodní část, abyste zjistili, co se pokazilo, a poté postupujte nahoru, abyste viděli, jak se skript do daného místa dostal. Čísla řádků ukazují na přesná místa ve zdrojovém kódu skriptu.
2.41.2. Ladění pomocí výpisů¶
Nejrychlejší způsob, jak zjistit, co skript dělá, je vypsat podezřelé hodnoty. Tři vestavěné funkce činí výpisy užitečnějšími:
repr()– vrací řetězec v podobě určené pro vývojáře.print(repr(value))rozlišuje"5"od5aNoneod"None", což prostýprint()nedokáže.type()– vrací třídu hodnoty.print(type(value))je způsob, jak zjistit, zda proměnná, která „by měla být int“, není potají řetězec.len()– délka sekvence nebo kolekce. Překvapivě velká část chyb jsou problémy typu o jedničku vedle (off-by-one) nebo nesoulad velikostí.
print("got:", repr(value), "type:", type(value), "len:", len(value))
Vložte výpis do každé větve, která vás zajímá – do obou větví příkazu if, do každého bloku except, do těla cyklu, u kterého máte podezření, že proběhne nulakrát. Cena je jeden řádek výstupu; přínosem je zjištění, zda cesta kódu, o které si myslíte, že se vykonává, je opravdu tou, která se vykonává.
2.41.3. Zkoumání objektu¶
Dvě vestavěné funkce odpovídají na otázku „co s touto věcí mohu dělat“:
dir()– vrací seznam každého jména definovaného na objektu: metody, atributy, dunder metody, vše.help()– vypíše dokumentační řetězec (a na CPythonu i signaturu) funkce, metody nebo třídy.
Používejte je společně: dir najde jméno, help vysvětlí, co dělá.
2.41.3.1. Nalezení jména pomocí dir¶
>>> dir([1, 2, 3])
['__add__', '__class__', '__contains__', '__delitem__',
'__eq__', '__ge__', ..., 'append', 'clear', 'copy',
'count', 'extend', 'index', 'insert', 'pop', 'remove',
'reverse', 'sort']
První část seznamu tvoří dunder metody zděděné z každého objektu; jména, která stojí za prohlédnutí, jsou obvykle až za nimi. dir funguje na čemkoli – na třídě, instanci, modulu, vestavěném typu:
>>> import json
>>> dir(json)
['__name__', 'dump', 'dumps', 'load', 'loads']
Tato druhá forma je způsob, jak zjistit, která jména nejvyšší úrovně modul skutečně zpřístupňuje, aniž byste opustili REPL.
2.41.3.2. Vyhledání pomocí help¶
Jakmile dir odhalí kandidáta, help jej popíše:
>>> help(str.split)
split(sep=None, maxsplit=-1)
Return a list of the words in the string, ...
Na MicroPythonu je help úspornější než na CPythonu – někdy jen signatura, někdy jednořádkový dokumentační řetězec, někdy pro vestavěné funkce v C nic. Přesto je to rychlá připomínka, když není po ruce nápověda v IDE.
2.41.4. Když se něco zasekne¶
Skript, který se nevrací, se diagnostikuje obtížněji než ten, který vyvolá výjimku. Časté příčiny:
Cyklus
while, jehož podmínka se nikdy nestane nepravdivou. Přidejte výpis řídicí proměnné cyklu při každé iteraci; pokud se hodnota nemění, tělo cyklu obsahuje chybu.Blokující volání čekající na vstup, který nikdy nedorazí – čtení z prázdné fronty, spánek bez konce. Obklopte volání výpisy, abyste viděli, na kterém řádku skript uvázl.
Nekonečná rekurze. Traceback, který se nakonec objeví (s
RecursionError), na ni obvykle ukazuje přímo.
Nejúčinnějším způsobem obnovy zaseknutého skriptu je tlačítko stop v IDE, které skriptu pošle přes USB KeyboardInterrupt. Přerušení se objeví jako traceback na právě běžícím řádku – často právě na tom řádku, který se nevrací.
Poznámka
Pokud se zaseknutí brání každé diagnostice – skript se zdá správný, traceback přerušení ukazuje do vestavěné funkce nebo do kódu firmwaru, a ne do vašeho skriptu, nebo tentýž kód fungoval na předchozím sestavení firmwaru – příčinou může být chyba firmwaru spíše než chyba skriptu. Zredukujte skript na nejmenší reprodukovatelný příklad, který se stále zasekává, a založte hlášení na fóru OpenMV. Uveďte verzi firmwaru, desku, na které běžel, a zredukovaný skript.
2.41.5. Před nasazením diagnostiku odstraňte¶
Strategické výpisy během vývoje jsou skvělé; sto volání print ponechaných v produkčním skriptu zahlcuje výstup a spotřebovává haldu (heap), kterou by mohla využívat skutečná práce. Když je chyba opravena, výpisy odstraňte (nebo je ohraničte ladicím příznakem, který lze vypnout).
Pro diagnostiku, která by měla zůstat v cestě kódu dlouhodobě, přejděte z print() na modul logging. Ten ke každé zprávě připojí úroveň (debug, info, warning, error) a umožňuje jediným nastavením v produkci umlčet ty méně důležité:
import logging
log = logging.getLogger("main")
log.info("starting up")
log.debug("loaded config: %s", config)
log.warning("falling back to defaults")
Nastavení úrovně loggeru na logging.WARNING způsobí, že volání info a debug nestojí prakticky nic (řetězec zprávy se nikdy nesestaví), aniž byste museli zakomentovávat řádky. To dělá z logging správný nástroj pro trvalou diagnostiku; surový print je v pořádku pro tu jednorázovou.