Tracebacks helfen in Python beim Aufspüren von Fehlern nach einer nicht abgefangenen Exception. Nachteil ist allerdings, dass die standardmäßige Ausgabeform dieses Hilfsmittels nicht so aussagekräftig ist wie z. B. eine GDB-Session: so werden nur der Aufrufstack sowie die konkrete Exception angezeigt.
Oftmals ist man allerdings an bestimmten Variablen interessiert, die einen bestimmten edge case erzeugen, der zum Absturz führt. Mit klassischen Tracebacks kommt man da nur schwerlich weiter und behilft sich, indem man eigene Ausnahmebehandlungen (except
) einfügt, welche dann z. B. die Variablen im Fehlerfall anzeigen.
Es gibt allerdings auch weitaus angenehmere Möglichkeiten, um dies umzusetzen. Zwei beispielhafte Tools möchte ich euch kurz in diesem Artikel vorstellen. Hierzu verwende ich folgendes Programm, welches bei bestimmten Werten zu einer ZeroDivisionError
-Exception führt.
# hier werden dann die traceback helper importiert
def foo(x, y):
z = y - 3
return bar(x, z)
def bar(x, y):
return baz(x, y + 1)
def baz(x, y):
return x / y
foo(1, 2)
traceback_with_variables
traceback_with_variables ist ein Python-Package, welches die oben angesprochene Exception-Meldung gegen eine andere austauscht. Der Fokus liegt hierbei auf der Anzeige der Variablen. So werden direkt im Traceback alle relevanten Variablen im Kontext für jedes Element im call stack angezeigt, sodass edge cases klar identifziert werden können.
Der Einsatz dieses Packages gestaltet sich relativ einfach, da nur ein Import genügt, um die Funktionalität zu aktivieren:
from traceback_with_variables import activate_by_import
Diese Tracebacks sehen dann folgendermaßen aus.
Traceback with variables (most recent call last):
File "/tmp/demo.py", line 15, in <module>
foo(1, 2)
__name__ = '__main__'
__doc__ = None
__package__ = None
__loader__ = <...> # gekuerzt fuer den Blog
__spec__ = None
__annotations__ = {}
__builtins__ = <module 'builtins' (built-in)>
__file__ = '/tmp/demo.py'
__cached__ = None
activate_by_import = <> <...> # gekuerzt fuer den Blog
foo = <function foo at 0x7f65076ca040>
bar = <function bar at 0x7f65073a2670>
baz = <function baz at 0x7f65073a2820>
File "/tmp/demo.py", line 7, in foo
return bar(x, z)
x = 1
y = 2
z = -1
File "/tmp/demo.py", line 10, in bar
return baz(x, y + 1)
x = 1
y = -1
File "/tmp/demo.py", line 13, in baz
return x / y
x = 1
y = 0
builtins.ZeroDivisionError: division by zero
Stackprinter
Ein weiteres Package, welches in ähnlicher Art und Weise hilft, ist stackprinter. Stackprinter muss zur Aktivierung importiert und manuell aktiviert werden:
import stackprinter
stackprinter.set_excepthook(style='darkbg2')
Dieses Python-Package zeigt ebenfalls für jede Aufrufebene die Variablen im Kontext an, ergänzt dies allerdings bereits in der Standardkonfiguration um Codesnippets, sodass das Debugging ebenfalls deutlich vereinfacht werden kann. Somit wird ein Komfort ermöglicht, der sonst nur von z. B. IDEs bekannt ist.
Tracebacks sehen dann so aus:
File "/tmp/demo.py", line 15, in <module>
11
12 def baz(x, y):
13 return x / y
14
--> 15 foo(1, 2)
File "/tmp/demo.py", line 7, in foo
5 def foo(x, y):
6 z = y - 3
--> 7 return bar(x, z)
..................................................
x = 1
y = 2
z = -1
..................................................
File "/tmp/demo.py", line 10, in bar
9 def bar(x, y):
--> 10 return baz(x, y + 1)
..................................................
x = 1
y = -1
..................................................
File "/tmp/demo.py", line 13, in baz
12 def baz(x, y):
--> 13 return x / y
..................................................
x = 1
y = 0
..................................................
ZeroDivisionError: division by zero
Sentry
Während die obigen Python-Packages besonders bei der Entwicklung helfen, ist Sentry konzeptionell eher für die Fehlersuche in Production-Umgebungen gedacht. Hier werden die Fehler an eine Webanwendung gemeldet, von wo aus der Traceback samt Codeauszug analysiert werden kann. Auf Programmseite kommt das Sentry Python SDK dann zum Einsatz.
Sentry ist weitaus aufwändiger zu installieren, soll allerdings an dieser Stelle nicht unerwähnt bleiben.
Fazit
Für das klassische Debugging auf dem Terminal gibt es einige Packages, die die Standard-Exception-Meldung gegen eine eigene austauschen können und somit die Entwicklung weiter vereinfachen. Dabei gibt es verschiedene Lösungen, die auch verschieden komplex implementiert werden können. Am Ende kommt es jedoch auf die persönlichen Präferenzen sowie auf die Umgebung an, in der die Tracebacks angezeigt bzw. gesammelt werden sollen.