None
Programmierung

Traceback-Helfer für Python

by Viktor Garske on Nov. 30, 2020, 6:48 p.m.

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.

Author image
Viktor Garske

Viktor Garske ist der Hauptautor des Blogs und schreibt gerne über Technologie, Panorama sowie Tipps & Tricks.

Comments (0)

Comments are not enabled for this entry.