{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Chyby neboli *bugs* (štěnice) jsou v každém programu (téměř). Naučíme se je chytat za běhu pomocí nástroje zvaného *debugger* (\"odstraňovač štěnic\"). Navíc si ukážeme, jaké jsou výhody dynamického interpretovaného jazyka, jakým je Python.\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pdb -- základní debugger\n", "Pdb je základní debugger pro Python. Základní použití je vložit break pointu do vašeho kódu pomocí `set_trace`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "# definice fact\n", "def fact(nn):\n", " \"\"\"Spočítá faktoriál\n", " \"\"\"\n", " from math import factorial\n", " try:\n", " # zkusíme převést n na číslo\n", " nn = float(nn)\n", " except ValueError as e:\n", " raise ValueError(\"{} není číslo\".format(nn))\n", " # je n celé číslo >= 0\n", " if not nn.is_integer() or nn < 0:\n", " raise ValueError(\"{} není celé číslo >= 0\".format(nn))\n", " return factorial(nn)\n", "\n", "# nová funkce s break pointem\n", "def fact_b(nn):\n", " from pdb import set_trace\n", " # tade se běh programu zastaví a objeví se pdb rozhraní\n", " set_trace()\n", " r = fact(nn)\n", " return r" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tady vidíme, jak vypadá interace s pdb. Všímejte si řádků (Pdb) a příkazů do nich zadaných: `help`, `where`, `step`, `continue`. To jsou příkazy pro samotný debugger. Kromě toho ale můžeme zadávat i příkazy Pythonu, jako např. `dir()`. **Toto je velice důležité! V debuggeru máme k dispozici celý Python, všechny jeho funkce atd.**" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> (21)fact_b()\n", "-> r = fact(nn)\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "(Pdb) help\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Documented commands (type help ):\n", "========================================\n", "EOF c d h list q rv undisplay\n", "a cl debug help ll quit s unt \n", "alias clear disable ignore longlist r source until \n", "args commands display interact n restart step up \n", "b condition down j next return tbreak w \n", "break cont enable jump p retval u whatis \n", "bt continue exit l pp run unalias where \n", "\n", "Miscellaneous help topics:\n", "==========================\n", "exec pdb\n", "\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "(Pdb) continue\n" ] }, { "data": { "text/plain": [ "24" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fact_b(4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Dokonce můžeme za běhu měnit proměnné!** Podívejte se, jak jsme do nn uložili 10 a že výsledek tomu odpovídá." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> (21)fact_b()\n", "-> r = fact(nn)\n", "(Pdb) dir()\n", "['nn', 'set_trace']\n", "(Pdb) nn = 10\n", "(Pdb) c\n" ] }, { "data": { "text/plain": [ "3628800" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fact_b(4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## IPython debugger\n", "Lze asi očekávat, že existují i další možnosti chytání štěnic. Podívejme se, co nabízí náš dobrý známý IPython.\n", "### `%debug`\n", "`%debug` (případně `%%debug` pro celou buňku) spustí příkaz v debug módu, který se spustí, ještě než se začne cokoli provádět. V ipdb např. funguje doplňování pomocí tabelátoru (to ovšem nefunguje v notebooku), jinak je ale téměř totožný s pdb." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "NOTE: Enter 'c' at the ipdb> prompt to continue execution.\n", "> \u001b[1;32m\u001b[0m(1)\u001b[0;36m\u001b[1;34m()\u001b[0m\n", "\n", "ipdb> help\n", "\n", "Documented commands (type help ):\n", "========================================\n", "EOF cl disable interact pdef quit source up \n", "a clear display j pdoc r step w \n", "alias commands down jump pfile restart tbreak whatis\n", "args condition enable ll pinfo return u where \n", "b cont exit longlist pinfo2 retval unalias \n", "break continue h n pp run undisplay\n", "bt d help next psource rv unt \n", "c debug ignore p q s until \n", "\n", "Miscellaneous help topics:\n", "==========================\n", "exec pdb\n", "\n", "Undocumented commands:\n", "======================\n", "l list\n", "\n", "ipdb> q\n" ] } ], "source": [ "%debug fact(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### %pdb\n", "Pomocí `%pdb on` zapneme automatické vyvolání ipdb ve chvíli vyhození výjimky. To je pochopitelně velice užitečné, protože **můžeme začít debugovat právě ve chvíli, kdy nastala chyba**. Navíc máme k dispozici veškerý kontext (obsah paměti, historii volání (stack trace) apod.)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Automatic pdb calling has been turned ON\n" ] }, { "ename": "ValueError", "evalue": "4.1 není celé číslo >= 0", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mget_ipython\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmagic\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'pdb on'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mfact\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m4.1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;32m\u001b[0m in \u001b[0;36mfact\u001b[1;34m(nn)\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;31m# je n celé číslo >= 0\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 12\u001b[0m \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mnn\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mis_integer\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mor\u001b[0m \u001b[0mnn\u001b[0m \u001b[1;33m<\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 13\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"{} není celé číslo >= 0\"\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 14\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 15\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mValueError\u001b[0m: 4.1 není celé číslo >= 0" ] }, { "name": "stdout", "output_type": "stream", "text": [ "> \u001b[1;32m\u001b[0m(13)\u001b[0;36mfact\u001b[1;34m()\u001b[0m\n", "\u001b[1;32m 12 \u001b[1;33m \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mnn\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mis_integer\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mor\u001b[0m \u001b[0mnn\u001b[0m \u001b[1;33m<\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[1;32m---> 13 \u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"{} není celé číslo >= 0\"\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[1;32m 14 \u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\n", "ipdb> dir()\n", "['factorial', 'nn']\n", "ipdb> q\n" ] } ], "source": [ "%pdb on\n", "fact(4.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Break pointy jinak\n", "Někdy (často) není praktické nastavovat break pointy pomocí `set_trace`. (i)pdb nám umožňují nastavit break pointy pomocí čísla řádku (v libovolném souboru) nebo jména funkce. Případně můžeme přidat ještě podmínku.\n", "\n", "Pro ukázku definujeme funkci, která vrací (nn!)^2. Spustíme jí v debug módu a umístíme break point do funkce fact pomocí `break fact`." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "NOTE: Enter 'c' at the ipdb> prompt to continue execution.\n", "> \u001b[1;32m\u001b[0m(1)\u001b[0;36m\u001b[1;34m()\u001b[0m\n", "\n", "ipdb> break fact\n", "Breakpoint 1 at :2\n", "ipdb> c\n", "> \u001b[1;32m\u001b[0m(5)\u001b[0;36mfact\u001b[1;34m()\u001b[0m\n", "\u001b[1;32m 4 \u001b[1;33m \"\"\"\n", "\u001b[0m\u001b[1;32m----> 5 \u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0mmath\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[1;32m 6 \u001b[1;33m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\n", "ipdb> where\n", " \u001b[1;32m/sw/python2/anaconda3/envs/python_course/lib/python3.4/bdb.py\u001b[0m(431)\u001b[0;36mrun\u001b[1;34m()\u001b[0m\n", "\u001b[0;32m 429 \u001b[0m \u001b[0msys\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msettrace\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtrace_dispatch\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0;32m 430 \u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m--> 431 \u001b[1;33m \u001b[0mexec\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcmd\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mglobals\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlocals\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 432 \u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mBdbQuit\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0;32m 433 \u001b[0m \u001b[1;32mpass\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\n", " \u001b[1;32m\u001b[0m(1)\u001b[0;36m\u001b[1;34m()\u001b[0m\n", "\n", " \u001b[1;32m\u001b[0m(2)\u001b[0;36mfact2\u001b[1;34m()\u001b[0m\n", "\u001b[0;32m 1 \u001b[0m\u001b[1;32mdef\u001b[0m \u001b[0mfact2\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m----> 2 \u001b[1;33m \u001b[0mr\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfact\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m**\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 3 \u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mr\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0;32m 4 \u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0;32m 5 \u001b[0m\u001b[0mget_ipython\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmagic\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'debug fact2(4)'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\n", "> \u001b[1;32m\u001b[0m(5)\u001b[0;36mfact\u001b[1;34m()\u001b[0m\n", "\u001b[1;32m 3 \u001b[1;33m \"\"\"Spočítá faktoriál\n", "\u001b[0m\u001b[1;32m 4 \u001b[1;33m \"\"\"\n", "\u001b[0m\u001b[1;32m----> 5 \u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0mmath\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[1;32m 6 \u001b[1;33m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[1;32m 7 \u001b[1;33m \u001b[1;31m# zkusíme převést n na číslo\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[0m\n", "ipdb> q\n" ] } ], "source": [ "def fact2(nn):\n", " r = fact(nn)**2\n", " return r\n", "\n", "%debug fact2(4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### IPython embed\n", "Toto není přísně řečeno debugování, ale má to hodně společného. IPython umožňuje spustit přímou interakci s aktuálním kontextem pomocí funkce `embed`, případně `embed_kernel`. Na rozdíl od pdb se ale změny nepropagují zpět do spuštěcího kontextu, takže jakékoli změny se po ukončení ztratí. Více v [dokumentaci](http://ipython.org/ipython-doc/dev/interactive/reference.html#embedding-ipython)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Další možnosti\n", "\n", "* Vylepšení pdb: [python3-trepan](https://github.com/rocky/python3-trepan/).\n", "* Vynikající je [debugger v PyCharm](https://www.jetbrains.com/help/pycharm/part-1-debugging-python-code.html) nebo ve [VS Code](https://code.visualstudio.com/docs/python/python-tutorial#_configure-and-run-the-debugger).\n", "* [pudb](https://pypi.python.org/pypi/pudb) je \"grafický\" debugger v textovém režimu.\n", "* Většina vývojových prostředí nějakým způsobem umožňuje pustit debugger." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.4" } }, "nbformat": 4, "nbformat_minor": 4 }