Debugování

Při vývoji software se často dostaneme do situace, kdy si chceme projít kód krok za krokem a zjistit například to, jaké jsou aktuální hodnoty proměnných, jestli se správně vyhodnocují podmínky atd. Tomuto procesu se česky říká ladění, často se ale setkáte s anglickým výrazem debugging. Obvykle ho provádíme ve chvíli, kdy se program nechová podle očekávání, tedy jsme narazili na chybu (bug).

Možná jste byly zvyklé si na různá místa v programu pomocí funkce print vypisovat aktuální stav programu. Zjistíte, že u většího projektu je tento přístup většinou nedostatečný a že si chcete program projít krok za krokem.

Abychom mohli debugovat, potřebujeme k tomu nástroj zvaný debugger. VSCode spolu s rozšířením pro Python ho má zabudovaný. Ve zbytku této lekce si ukážeme, jak ho používat.

Ovládání debuggeru

Do debuggeru se přepneme kliknutím na tlačítko s přeškrtnutým broukem, které vidíte na obrázku níže.

(Menu debuggeru)

Zajímají nás především:

  • tlačítko pro spuštění debugování (zelený trojúhelník)
  • panel s výpisem aktuálních hodnot proměnných - Variables
  • panel pro ovládání debuggeru

Panel pro ovládání debuggeru se zobrazí až po spuštění debuggeru. Po spuštění je možné debugger restartovat (spustit od začátku) kliknutím na zelenou šipku ve tvaru kruhu nebo ho zastavit kliknutím na červený čtvereček.

Program se chová stejně, jako by debugger nebyl zaplý. Např., pokud se podmínka vyhodnotí jako False, tak do jejího těla debugger nevstoupí. Stejně tak debugger bude procházet cyklem tolikrát, jako za normálního běhu programu.

Breakpoint

Klíčovou roli při debugování hraje zarážka - angl. breakpoint. Když debuger narazí na zarážku, tak zastaví vykonávání programu a předá kontrolu uživateli. Ten poté může zjistit hodnoty proměnných, pokračovat na další krok nebo třeba vejít do funkce, která se na řádku volá. Breakpoint umístíme kliknutím vlevo od řádku, kde chceme, aby se debugger zastavil:

(Umístění breakpointu)

Pokud klikneme na tlačítko Continue (modrý trojúhelník nebo F5), tak bude debugger pokračovat až do dalšího breakpointu, případně na konec programu.

Step over, into, out

Často budeme chtít pokračovat na další řádek kódu. Docílíme toho pomocí klávesy F10, případně kliknutím na Step over.

Pokud bychom chtěli vstoupit do funkce, která je volaná na aktualním řádku, tak toho docílíme pomocí F11 - Step into.

Pro vystoupení z aktuální funkce se používá Shift + F11. Program pokračuje až do chvíle, než se vrátí do funkce, která volala funkci, ze které jsme chtěli vystoupit.

Debug testů

Často zjistíme, že nám neprocházejí testy. Může se jednat o chybu v testu, nebo o chybu v programu. Ideální je si test prodebugovat, a to například tak, že dáme breakpoint na začátek testu a pak se kombinací Step over / into / out dostaneme na problémové místo programu. Budeme sledovat, jak se vyhodnocují jednotlivé podmínky, jaké argumenty se předávají do funkcí atd. Často se nám tímto způsobem podaří chybu najít.

Pokud máme správně nastavený VSCode, tak by se nad testovacími funkcemi měly objevit možnosti Run Test a Debug Test (jako na obrázku níže).

(Debug testu)

Jestli ve svém editoru tyto možnosti nemáš, tak zkus:

  • zkontrolovat, že máš nastavený interpreter
    • v levém dolním rohu VSCode by jsi kromě verze Pythonu měla vidět jméno virtuálního prostředí (např. Python 3.6 (venv))
      • pokud jméno virtuálního prostředí nevidíš, tak stiskni Ctrl + Shift + P, vyber Select Python Interpreter a zvol své virtuální prostředí (pravděpodobně venv)
  • Stisknout Ctrl + Shift + P a vybrat Discover Unit Tests

Po kliknutí na Debug test se spustí debugger.

Debug console

Po spuštění debugeru je možné používat debugovací konzoli. Najdete ji ve spodním panelu v záložce vedle integrovaného terminálu.

(Debug konzole)

Představ si, že jsi spustila Python z příkazové řádky a poté zadala stejný kód, kterým debugger prošel, než se dostal na breakpoint. V tu chvíli tedy máte k dispozici všechny inicializované globální proměnné, můžete volat funkce nebo pracovat s proměnným úplně stejně, jak jsme to dělali v lekcích na začátku kurzu.

Pokud například víte, že na dalším řádku vzniká IndexError, ale nevíte proč, tak můžete zjistit, na jaký index se přistupuje, jaké hodnoty jsou v seznamu nebo slovníku obsažené a co by se stalo, kdybyste použily jiný index.

Do debug konzole se také vypisují printy, pokud je v programu používáte.

Jen pozor - pokud upravíte hodnoty proměnných, tak se změny promítnout do zbytku programu, takže je někdy nutné debugger restartovat.

{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2019/brno-jaro-2019-ut:tests:4",
      "title": "Debugování",
      "html": "\n          \n    \n\n    <h1>Debugov&#xE1;n&#xED;</h1>\n<p>P&#x159;i v&#xFD;voji software se &#x10D;asto dostaneme do situace,\nkdy si chceme proj&#xED;t k&#xF3;d krok za krokem a zjistit\nnap&#x159;&#xED;klad to, jak&#xE9; jsou aktu&#xE1;ln&#xED; hodnoty prom&#x11B;nn&#xFD;ch,\njestli se spr&#xE1;vn&#x11B; vyhodnocuj&#xED; podm&#xED;nky atd.\nTomuto procesu se &#x10D;esky &#x159;&#xED;k&#xE1; <em>lad&#x11B;n&#xED;</em>, &#x10D;asto se ale\nsetk&#xE1;te s anglick&#xFD;m v&#xFD;razem <em>debugging</em>. Obvykle\nho prov&#xE1;d&#xED;me ve chv&#xED;li, kdy se program nechov&#xE1;\npodle o&#x10D;ek&#xE1;v&#xE1;n&#xED;, tedy jsme narazili na chybu (bug).</p>\n<p>Mo&#x17E;n&#xE1; jste byly zvykl&#xE9; si na r&#x16F;zn&#xE1; m&#xED;sta v programu\npomoc&#xED; funkce <code>print</code> vypisovat aktu&#xE1;ln&#xED; stav programu.\nZjist&#xED;te, &#x17E;e u v&#x11B;t&#x161;&#xED;ho projektu je tento p&#x159;&#xED;stup v&#x11B;t&#x161;inou\nnedostate&#x10D;n&#xFD; a &#x17E;e si chcete program proj&#xED;t krok za krokem.</p>\n<p>Abychom mohli debugovat, pot&#x159;ebujeme k tomu n&#xE1;stroj zvan&#xFD;\n<em>debugger</em>. VSCode spolu s <a href=\"https://marketplace.visualstudio.com/items?itemName=ms-python.python\">roz&#x161;&#xED;&#x159;en&#xED;m pro Python</a> ho m&#xE1; zabudovan&#xFD;.\nVe zbytku t&#xE9;to lekce si uk&#xE1;&#x17E;eme, jak ho pou&#x17E;&#xED;vat.</p>\n<h2>Ovl&#xE1;d&#xE1;n&#xED; debuggeru</h2>\n<p>Do debuggeru se p&#x159;epneme kliknut&#xED;m na tla&#x10D;&#xED;tko s p&#x159;e&#x161;krtnut&#xFD;m broukem,\nkter&#xE9; vid&#xED;te na obr&#xE1;zku n&#xED;&#x17E;e.</p>\n<p><span class=\"figure\"><a href=\"/2019/brno-jaro-2019-ut/beginners/debug/static/debug.png\"><img src=\"/2019/brno-jaro-2019-ut/beginners/debug/static/debug.png\" alt=\"(Menu debuggeru)\"></a></span></p>\n<p>Zaj&#xED;maj&#xED; n&#xE1;s p&#x159;edev&#x161;&#xED;m:</p>\n<ul>\n<li>tla&#x10D;&#xED;tko pro spu&#x161;t&#x11B;n&#xED; debugov&#xE1;n&#xED; (zelen&#xFD; troj&#xFA;heln&#xED;k)</li>\n<li>panel s v&#xFD;pisem aktu&#xE1;ln&#xED;ch hodnot prom&#x11B;nn&#xFD;ch - <em>Variables</em></li>\n<li>panel pro ovl&#xE1;d&#xE1;n&#xED; debuggeru</li>\n</ul>\n<p><strong>Panel pro ovl&#xE1;d&#xE1;n&#xED; debuggeru</strong> se zobraz&#xED; a&#x17E; po spu&#x161;t&#x11B;n&#xED; debuggeru.\nPo spu&#x161;t&#x11B;n&#xED; je mo&#x17E;n&#xE9; debugger restartovat (spustit od za&#x10D;&#xE1;tku)\nkliknut&#xED;m na zelenou &#x161;ipku ve tvaru kruhu\nnebo ho zastavit kliknut&#xED;m na &#x10D;erven&#xFD; &#x10D;tvere&#x10D;ek.</p>\n<p>Program se chov&#xE1; stejn&#x11B;, jako by debugger nebyl zapl&#xFD;.\nNap&#x159;., pokud se podm&#xED;nka vyhodnot&#xED; jako <code>False</code>, tak do jej&#xED;ho t&#x11B;la debugger nevstoup&#xED;.\nStejn&#x11B; tak debugger bude proch&#xE1;zet cyklem tolikr&#xE1;t, jako za norm&#xE1;ln&#xED;ho\nb&#x11B;hu programu.</p>\n<h3>Breakpoint</h3>\n<p>Kl&#xED;&#x10D;ovou roli p&#x159;i debugov&#xE1;n&#xED; hraje zar&#xE1;&#x17E;ka - angl. <em>breakpoint</em>.\nKdy&#x17E; debuger naraz&#xED; na zar&#xE1;&#x17E;ku, tak zastav&#xED; vykon&#xE1;v&#xE1;n&#xED; programu\na p&#x159;ed&#xE1; kontrolu u&#x17E;ivateli. Ten pot&#xE9; m&#x16F;&#x17E;e zjistit hodnoty prom&#x11B;nn&#xFD;ch,\npokra&#x10D;ovat na dal&#x161;&#xED; krok nebo t&#x159;eba vej&#xED;t do funkce, kter&#xE1; se na &#x159;&#xE1;dku vol&#xE1;.\nBreakpoint um&#xED;st&#xED;me kliknut&#xED;m vlevo od &#x159;&#xE1;dku, kde chceme, aby se debugger zastavil:</p>\n<p><span class=\"figure\"><a href=\"/2019/brno-jaro-2019-ut/beginners/debug/static/breakpoint.png\"><img src=\"/2019/brno-jaro-2019-ut/beginners/debug/static/breakpoint.png\" alt=\"(Um&#xED;st&#x11B;n&#xED; breakpointu)\"></a></span></p>\n<p>Pokud klikneme na tla&#x10D;&#xED;tko <em>Continue</em> (modr&#xFD; troj&#xFA;heln&#xED;k nebo <kbd>F5</kbd>),\ntak bude debugger pokra&#x10D;ovat a&#x17E; do dal&#x161;&#xED;ho breakpointu,\np&#x159;&#xED;padn&#x11B; na konec programu.</p>\n<h3>Step over, into, out</h3>\n<p>&#x10C;asto budeme cht&#xED;t pokra&#x10D;ovat na dal&#x161;&#xED; &#x159;&#xE1;dek k&#xF3;du. Doc&#xED;l&#xED;me toho pomoc&#xED;\nkl&#xE1;vesy <kbd>F10</kbd>, p&#x159;&#xED;padn&#x11B; kliknut&#xED;m na <em>Step over</em>.</p>\n<p>Pokud bychom cht&#x11B;li vstoupit do funkce, kter&#xE1; je volan&#xE1; na aktualn&#xED;m &#x159;&#xE1;dku,\ntak toho doc&#xED;l&#xED;me pomoc&#xED; <kbd>F11</kbd> - <em>Step into</em>.</p>\n<p>Pro vystoupen&#xED; z aktu&#xE1;ln&#xED; funkce se pou&#x17E;&#xED;v&#xE1;\n<kbd>Shift</kbd> + <kbd>F11</kbd>.\nProgram pokra&#x10D;uje a&#x17E; do chv&#xED;le, ne&#x17E; se vr&#xE1;t&#xED; do funkce, kter&#xE1; volala\nfunkci, ze kter&#xE9; jsme cht&#x11B;li vystoupit.</p>\n<h2>Debug test&#x16F;</h2>\n<p>&#x10C;asto zjist&#xED;me, &#x17E;e n&#xE1;m neproch&#xE1;zej&#xED; testy.\nM&#x16F;&#x17E;e se jednat o chybu v testu, nebo o chybu v programu.\nIde&#xE1;ln&#xED; je si test prodebugovat, a to nap&#x159;&#xED;klad tak,\n&#x17E;e d&#xE1;me breakpoint na za&#x10D;&#xE1;tek testu a pak se kombinac&#xED;\n<em>Step over / into / out</em> dostaneme na probl&#xE9;mov&#xE9; m&#xED;sto programu.\nBudeme sledovat, jak se vyhodnocuj&#xED; jednotliv&#xE9; podm&#xED;nky,\njak&#xE9; argumenty se p&#x159;ed&#xE1;vaj&#xED; do funkc&#xED; atd.\n&#x10C;asto se n&#xE1;m t&#xED;mto zp&#x16F;sobem poda&#x159;&#xED; chybu naj&#xED;t.</p>\n<p>Pokud m&#xE1;me spr&#xE1;vn&#x11B; nastaven&#xFD; VSCode, tak by se nad testovac&#xED;mi\nfunkcemi m&#x11B;ly objevit mo&#x17E;nosti <em>Run Test</em> a <em>Debug Test</em>\n(jako na obr&#xE1;zku n&#xED;&#x17E;e).</p>\n<p><span class=\"figure\"><a href=\"/2019/brno-jaro-2019-ut/beginners/debug/static/test_debug.png\"><img src=\"/2019/brno-jaro-2019-ut/beginners/debug/static/test_debug.png\" alt=\"(Debug testu)\"></a></span></p>\n<p>Jestli ve sv&#xE9;m editoru tyto mo&#x17E;nosti nem&#xE1;&#x161;, tak zkus:</p>\n<ul>\n<li>zkontrolovat, &#x17E;e m&#xE1;&#x161; nastaven&#xFD; interpreter<ul>\n<li>v lev&#xE9;m doln&#xED;m rohu VSCode by jsi krom&#x11B; verze Pythonu\nm&#x11B;la vid&#x11B;t jm&#xE9;no virtu&#xE1;ln&#xED;ho prost&#x159;ed&#xED; (nap&#x159;. Python 3.6 (venv))<ul>\n<li>pokud jm&#xE9;no virtu&#xE1;ln&#xED;ho prost&#x159;ed&#xED; nevid&#xED;&#x161;, tak stiskni\n<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>,\nvyber <em>Select Python Interpreter</em> a zvol sv&#xE9; virtu&#xE1;ln&#xED; prost&#x159;ed&#xED;\n(pravd&#x11B;podobn&#x11B; <em>venv</em>)</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>Stisknout <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> a vybrat <em>Discover Unit Tests</em></li>\n</ul>\n<p>Po kliknut&#xED; na <em>Debug test</em> se spust&#xED; debugger.</p>\n<h2>Debug console</h2>\n<p>Po spu&#x161;t&#x11B;n&#xED; debugeru je mo&#x17E;n&#xE9; pou&#x17E;&#xED;vat debugovac&#xED; konzoli.\nNajdete ji ve spodn&#xED;m panelu v z&#xE1;lo&#x17E;ce vedle integrovan&#xE9;ho termin&#xE1;lu.</p>\n<p><span class=\"figure\"><a href=\"/2019/brno-jaro-2019-ut/beginners/debug/static/debug_console.png\"><img src=\"/2019/brno-jaro-2019-ut/beginners/debug/static/debug_console.png\" alt=\"(Debug konzole)\"></a></span></p>\n<p>P&#x159;edstav si, &#x17E;e jsi spustila Python z p&#x159;&#xED;kazov&#xE9; &#x159;&#xE1;dky\na pot&#xE9; zadala stejn&#xFD; k&#xF3;d, kter&#xFD;m debugger pro&#x161;el, ne&#x17E; se dostal na breakpoint.\nV tu chv&#xED;li tedy m&#xE1;te k dispozici v&#x161;echny inicializovan&#xE9; glob&#xE1;ln&#xED; prom&#x11B;nn&#xE9;,\nm&#x16F;&#x17E;ete volat funkce nebo pracovat s prom&#x11B;nn&#xFD;m &#xFA;pln&#x11B; stejn&#x11B;,\njak jsme to d&#x11B;lali v lekc&#xED;ch na za&#x10D;&#xE1;tku kurzu.</p>\n<p>Pokud nap&#x159;&#xED;klad v&#xED;te, &#x17E;e na dal&#x161;&#xED;m &#x159;&#xE1;dku vznik&#xE1; <code>IndexError</code>, ale nev&#xED;te pro&#x10D;,\ntak m&#x16F;&#x17E;ete zjistit, na jak&#xFD; index se p&#x159;istupuje, jak&#xE9; hodnoty jsou v\nseznamu nebo slovn&#xED;ku obsa&#x17E;en&#xE9; a co by se stalo, kdybyste pou&#x17E;ily jin&#xFD; index.</p>\n<p>Do debug konzole se tak&#xE9; vypisuj&#xED; <code>printy</code>, pokud je v programu pou&#x17E;&#xED;v&#xE1;te.</p>\n<p>Jen pozor - pokud uprav&#xED;te hodnoty prom&#x11B;nn&#xFD;ch,\ntak se zm&#x11B;ny prom&#xED;tnout do zbytku programu, tak&#x17E;e je n&#x11B;kdy nutn&#xE9;\ndebugger restartovat.</p>\n\n\n        "
    }
  }
}