{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Fehleranalyse\n", "\n", "IPython enthält verschiedene Werkzeuge, um fehlerhaften Code zu analysieren, im Wesentlichen das *Exception Reporting* und den Debugger." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exceptions kontrollieren mit `%xmode`\n", "\n", "Wenn die Ausführung eines Python-Skripts fehlschlägt, wird meist eine sog. *Exception* ausgelöst und relevante Informationen zur Fehlerursache in einen *Traceback* geschrieben. Mit der `%xmode`-Magic-Funktion könnt ihr in IPython die Menge der Informationen steuern, die euch angezeigt werden. Betrachten wir hierfür den folgenden Code:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "def func1(a, b):\n", " return a / b\n", "\n", "\n", "def func2(x):\n", " a = x\n", " b = x - 1\n", " return func1(a, b)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[2], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfunc2\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", "Cell \u001b[0;32mIn[1], line 8\u001b[0m, in \u001b[0;36mfunc2\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 6\u001b[0m a \u001b[38;5;241m=\u001b[39m x\n\u001b[1;32m 7\u001b[0m b \u001b[38;5;241m=\u001b[39m x \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m----> 8\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc1\u001b[49m\u001b[43m(\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mb\u001b[49m\u001b[43m)\u001b[49m\n", "Cell \u001b[0;32mIn[1], line 2\u001b[0m, in \u001b[0;36mfunc1\u001b[0;34m(a, b)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mfunc1\u001b[39m(a, b):\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43ma\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mb\u001b[49m\n", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } ], "source": [ "func2(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Der Aufruf von `func2`führt zu einem Fehler, und der `Traceback` zeigt genau, was passiert ist: in jeder Zeile wird der Kontext jedes Schritts angezeigt, der schließlich zum Fehler geführt hat. Mit der `%xmode`-Magic-Funktion (kurz für *Exception-Modus*) können wir steuern, welche Informationen uns angezeigt werden sollen.\n", "\n", "`%xmode` nimmt ein einziges Argument, den Modus, und es gibt drei Möglichkeiten:\n", "* `Plain`\n", "* `Context`\n", "* `Verbose`\n", "\n", "Die Standardeinstellung ist `Context` und gibt eine Ausgabe wie die obige aus. `Plain` ist kompakter und liefert weniger Informationen:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Exception reporting mode: Plain\n" ] }, { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "error", "traceback": [ "Traceback \u001b[0;36m(most recent call last)\u001b[0m:\n", "\u001b[0m Cell \u001b[1;32mIn[3], line 2\u001b[0m\n func2(1)\u001b[0m\n", "\u001b[0m Cell \u001b[1;32mIn[1], line 8\u001b[0m in \u001b[1;35mfunc2\u001b[0m\n return func1(a, b)\u001b[0m\n", "\u001b[0;36m Cell \u001b[0;32mIn[1], line 2\u001b[0;36m in \u001b[0;35mfunc1\u001b[0;36m\n\u001b[0;31m return a / b\u001b[0;36m\n", "\u001b[0;31mZeroDivisionError\u001b[0m\u001b[0;31m:\u001b[0m division by zero\n" ] } ], "source": [ "%xmode Plain\n", "func2(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Der `Verbose`-Modus zeigt einige zusätzliche Informationen an, einschließlich der Argumente für alle aufgerufenen Funktionen:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Exception reporting mode: Verbose\n" ] }, { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[4], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m get_ipython()\u001b[38;5;241m.\u001b[39mrun_line_magic(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mxmode\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mVerbose\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m----> 2\u001b[0m \u001b[43mfunc2\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", "Cell \u001b[0;32mIn[1], line 8\u001b[0m, in \u001b[0;36mfunc2\u001b[0;34m(x=1)\u001b[0m\n\u001b[1;32m 6\u001b[0m a \u001b[38;5;241m=\u001b[39m x\n\u001b[1;32m 7\u001b[0m b \u001b[38;5;241m=\u001b[39m x \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m----> 8\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc1\u001b[49m\u001b[43m(\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mb\u001b[49m\u001b[43m)\u001b[49m\n a \u001b[0;34m= 1\u001b[0m\u001b[0;34m\n \u001b[0mb \u001b[0;34m= 0\u001b[0m\n", "Cell \u001b[0;32mIn[1], line 2\u001b[0m, in \u001b[0;36mfunc1\u001b[0;34m(a=1, b=0)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mfunc1\u001b[39m(a, b):\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43ma\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mb\u001b[49m\n a \u001b[0;34m= 1\u001b[0m\u001b[0;34m\n \u001b[0mb \u001b[0;34m= 0\u001b[0m\n", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } ], "source": [ "%xmode Verbose\n", "func2(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Diese zusätzlichen Informationen können helfen, den Grund für die *Exception* einzugrenzen. Umgekehrt kann der `Verbose`-Modus jedoch bei komplexem Code zu extrem langen Tracebacks führen, bei denen kaum noch die wesentlichen Stellen erkannt werden können." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Debugging\n", "\n", "Wenn durch das Lesen eines `Traceback` ein Fehler nicht gefunden werden kann, hilft Debugging weiter. Der Python-Standard für interaktives Debugging ist der Python-Debugger `pdb`. Mit ihm könnt ihr euch zeilenweise durch den Code navigieren, um festzustellen, was möglicherweise einen Fehler verursacht. Die erweiterte Version für IPython ist `ipdb`.\n", "\n", "In IPython ist der `%debug`-Magic-Befehl möglicherweise die bequemste Art zum Debugging. Wenn ihr ihn aufruft, nachdem eine Exception ausgegeben wurde, wird automatisch ein interaktiver Debug-Prompt während der Exception geöffnet. Mit der `ipdb`-Eingabeaufforderung könnt ih den aktuellen Status des Stacks untersuchen, die verfügbaren Variablen untersuchen und sogar Python-Befehle ausführen.\n", "\n", "Schauen wir uns die letzte Exception an und führen dann einige grundlegende Aufgaben aus:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> \u001b[0;32m/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_31526/3792871231.py\u001b[0m(2)\u001b[0;36mfunc1\u001b[0;34m()\u001b[0m\n", "\u001b[0;32m 1 \u001b[0;31m\u001b[0;32mdef\u001b[0m \u001b[0mfunc1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m----> 2 \u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 3 \u001b[0;31m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 4 \u001b[0;31m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 5 \u001b[0;31m\u001b[0;32mdef\u001b[0m \u001b[0mfunc2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "ipdb> s\n" ] } ], "source": [ "%debug" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Der interaktive Debugger bietet jedoch viel mehr – wir können im Stack auch auf und ab gehen und die Werte von Variablen untersuchen:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> \u001b[0;32m/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_31526/3792871231.py\u001b[0m(2)\u001b[0;36mfunc1\u001b[0;34m()\u001b[0m\n", "\u001b[0;32m 1 \u001b[0;31m\u001b[0;32mdef\u001b[0m \u001b[0mfunc1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m----> 2 \u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 3 \u001b[0;31m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 4 \u001b[0;31m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 5 \u001b[0;31m\u001b[0;32mdef\u001b[0m \u001b[0mfunc2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "ipdb> up\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "> \u001b[0;32m/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_31526/3792871231.py\u001b[0m(8)\u001b[0;36mfunc2\u001b[0;34m()\u001b[0m\n", "\u001b[0;32m 4 \u001b[0;31m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 5 \u001b[0;31m\u001b[0;32mdef\u001b[0m \u001b[0mfunc2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 6 \u001b[0;31m \u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 7 \u001b[0;31m \u001b[0mb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m----> 8 \u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "ipdb> print(x)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "1\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "ipdb> up\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "> \u001b[0;32m/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_31526/1541833627.py\u001b[0m(2)\u001b[0;36m\u001b[0;34m()\u001b[0m\n", "\u001b[0;32m 1 \u001b[0;31m\u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_line_magic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'xmode'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'Verbose'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m----> 2 \u001b[0;31m\u001b[0mfunc2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "ipdb> down\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "> \u001b[0;32m/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_31526/3792871231.py\u001b[0m(8)\u001b[0;36mfunc2\u001b[0;34m()\u001b[0m\n", "\u001b[0;32m 4 \u001b[0;31m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 5 \u001b[0;31m\u001b[0;32mdef\u001b[0m \u001b[0mfunc2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 6 \u001b[0;31m \u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 7 \u001b[0;31m \u001b[0mb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m----> 8 \u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "ipdb> quit\n" ] } ], "source": [ "%debug" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dies vereinfacht die Suche nach den Funktionsaufrufen, die zum Fehler geführt haben, enorm.\n", "\n", "Wenn der Debugger automatisch gestartet werden soll, wenn eine Ausnahme ausgelöst wird, könnt ihr die `%pdb`-Magic-Funktion verwenden, um dieses Verhalten zu aktivieren:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Exception reporting mode: Plain\n", "Automatic pdb calling has been turned ON\n" ] }, { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "error", "traceback": [ "Traceback \u001b[0;36m(most recent call last)\u001b[0m:\n", "\u001b[0m Cell \u001b[1;32mIn[7], line 3\u001b[0m\n func2(1)\u001b[0m\n", "\u001b[0m Cell \u001b[1;32mIn[1], line 8\u001b[0m in \u001b[1;35mfunc2\u001b[0m\n return func1(a, b)\u001b[0m\n", "\u001b[0;36m Cell \u001b[0;32mIn[1], line 2\u001b[0;36m in \u001b[0;35mfunc1\u001b[0;36m\n\u001b[0;31m return a / b\u001b[0;36m\n", "\u001b[0;31mZeroDivisionError\u001b[0m\u001b[0;31m:\u001b[0m division by zero\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "> \u001b[0;32m/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_31526/3792871231.py\u001b[0m(2)\u001b[0;36mfunc1\u001b[0;34m()\u001b[0m\n", "\u001b[0;32m 1 \u001b[0;31m\u001b[0;32mdef\u001b[0m \u001b[0mfunc1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m----> 2 \u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 3 \u001b[0;31m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 4 \u001b[0;31m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0;32m 5 \u001b[0;31m\u001b[0;32mdef\u001b[0m \u001b[0mfunc2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0m\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "ipdb> print(b)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "0\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "ipdb> quit\n" ] } ], "source": [ "%xmode Plain\n", "%pdb on\n", "func2(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wenn ihr ein Skript habt, das ihr von Anfang an im interaktiven Modus ausführen möchtet, so könnt ihr dies mit dem Befehl `%run -d`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Wesentliche Befehle des `ipdb`\n", "\n", "| Befehl | Beschreibung |\n", "| -------------- | ------------------------------------------------------------------------- |\n", "| `list` | Zeige den aktuellen Ort in der Datei |\n", "| `h`(`elp`) | Liste der Befehle anzeigen oder Hilfe zu einem bestimmten Befehl suchen |\n", "| `q`(`uit`) | Beendet den Debugger und das Programm |\n", "| `c`(`ontinue`) | Den Debugger beenden, im Programm fortfahren |\n", "| `n`(`ext`) | Gehe zum nächsten Schritt des Programms |\n", "| `` | Wiederhole den vorherigen Befehl |\n", "| `p`(`rint`) | Druckvariablen |\n", "| `s`(`tep`) | Schritt in eine Unterroutine |\n", "| `r`(`eturn`) | Rückkehr aus einem Unterprogramm |\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Weitere Informationen zum IPython-Debugger erhaltet ihr unter [ipdb](https://github.com/gotcha/ipdb)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.13 Kernel", "language": "python", "name": "python313" }, "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.13.0" }, "latex_envs": { "LaTeX_envs_menu_present": true, "autoclose": false, "autocomplete": true, "bibliofile": "biblio.bib", "cite_by": "apalike", "current_citInitial": 1, "eqLabelWithNumbers": true, "eqNumInitial": 1, "hotkeys": { "equation": "Ctrl-E", "itemize": "Ctrl-I" }, "labels_anchors": false, "latex_user_defs": false, "report_style_numbering": false, "user_envs_cfg": false }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }