Pysnooper – Alternative zum print-Debugging

PySnooper ist der Debugger des armen Mannes. Wenn man bereits eine Bash verwendet hat, dann ist er wie ein „set -x“ für Python.

Der Hintergrund: Man versucht herauszufinden, warum der Python-Code nicht das tut, was er der eigenen Meinung nach tun sollte. Man würde gerne einen vollwertigen Debugger mit Haltepunkten und Überwachungsfunktionen verwenden, aber man hat vielleicht nicht die Software und Zugriffsrechte verfügbar, um ihn einzurichten.

Man will einfach nur wissen, welche Zeilen laufen und welche nicht, und welche Werte die lokalen Variablen haben.
 Die meisten Leute würden den print-Befehl an strategischen Stellen verwenden, um sich die Werte von Variablen anzeigen zu lassen.

Mit PySnooper kann man dasselbe tun, nur dass man nicht sorgfältig die richtigen Zeilen für den print-Befehl suchen muss, sondern einfach eine Decorator-Zeile zu der Funktion hinzufügt, an der  man interessiert ist. Man erhält ein ausführliches Protokoll der Funktion, einschließlich der Zeilen, die ausgeführt wurden, sowie alle lokale Variablen, die verwendet wurden.

Was hebt PySnooper von allen anderen Hilfsmitteln ab? Man kann ihn in der ausufernden Unternehmenscodebasis verwenden, ohne irgendetwas aufwendig einrichten zu müssen. Man setzt einfach temporär den Decorator ein. Die Ausgabe erfolgt standardmäßig am Bildschirm, man kann aber die Ausgabe, da sie sehr umfangreich sein kann, in eine Protokolldatei umleiten, indem man deren Pfad als erstes Argument angibt.

Installation mit pip: pip3 install pysnooper

Ausgabe umleiten in eine Datei, z.B. @pysnooper.snoop(‚/my/log/debug.log‘)
Man kann die „snoop“-Tiefe festlegen, dadurch werden auch Zeilen der Funktionen angezeigt, die von der Funktion aufgerufen werden, z.B. @pysnooper.snoop(depth=2)
Man kann den Pysnoopers temporär ausschalten, in dem man auf der Befehlszeile die Umgebungsvariable dementsprechend setzt: export PYSNOOPER_DISABLED=1
Diese Variable ist aber sehr sorgfältig einzusetzen, sie kann leicht dazu führen, dass man nach dem „Debugging“ vergisst, die Decorater-Zeile aus dem Code zu entfernen.

Weitere Informationen zu Pysnopper findet man auf der Github-Seite des Projektes.

Beispiel:

import pysnooper


@pysnooper.snoop()
def number_to_bits(number):
    if number:
        bits = []
        while number:
            number, remainder = divmod(number, 2)
            bits.insert(0, remainder)
        return bits
    else:
        return [0]

number_to_bits(6)

Ausgabe:

Ausgabe Pysnooper mit Docorator

Möchte man keine komplette Funktion verfolgen, sondern nur einen bestimmten Code-Abschnitt, dann kann man einen with-Block verwenden.

Beispiel:

import random

import pysnooper
def foo():
    lst = []
    for i in range(10):
        lst.append(random.randrange(1, 1000))

    with pysnooper.snoop():
        lower = min(lst)
        upper = max(lst)
        mid = (lower + upper) / 2
        print(lower, mid, upper)


foo()

Ausgabe:

Ausgabe Pysnooper mit with-Befehl