Seite wählen

NETWAYS Blog

Weekly Snap: RootCamp & RVM for Ruby, Puppet Webinar & PDB++ for Python

weekly snap7 – 11 January was packed with events from the upcoming RootCamp, OSDC and a Puppet webinar to the first company ski trip, plus Python and Ruby tips to boot.
Eva explained unconferences, barcamps and what is to be expected at RootCamp on 24-25 May in Berlin. She also counted down 100 days to the OSDC 2013 with Olivier Renault’s ‘Introduction to Eucalyptus’.
More on events, Martin announced the first German language Puppet webinar on 24 January while Vanessa shared photos from our first Netways skiing trip.
Ronny then reminded us to manage Ruby and gem updates with RVM and Johannes recommended pdb++ for Python debugging.
To close the week, Christian shared good news that Teltonika ModemUSB5/10 now comes with an x64 driver for Windows.

Besserer Python-Debugger gefällig?

Welcher Python-Programmierer hat sich nicht schon einmal über die kleinen Macken des builtin-debuggers (im folgenden der „Normale“) geärgert oder schmerzlich Syntax-highlightning und TAB-Vervollständigung vermisst? Nun, ich bin so einer und möchte euch deshalb heute pdb++ vorstellen. Einmal installiert ersetzt dieser Debugger den normalen aus der stdlib und wartet mit einigen sehr nützlichen Features auf. mehr lesen…

Johannes Meyer
Johannes Meyer
Lead Developer

Johannes ist seit 2011 bei uns und inzwischen, seit er 2014 die Ausbildung abgeschlossen hat, als Lead Developer für Icinga Web 2, Icinga DB Web sowie alle möglichen anderen Module und Bibliotheken im Web Bereich zuständig. Arbeitet er gerade mal nicht, macht er es sich bei schlechtem Wetter am liebsten zum zocken oder Filme/Serien schauen auf dem Sofa gemütlich. Passt das Wetter, geht's auch mal auf eines seiner Zweiräder. Motorisiert oder nicht.

Der Python Debugger

Das Python-Modul pdb stellt einen interaktiven Debugger für Python-Programme zur Verfügung. Es umfasst den Quelltext in Einzelschritten zu evaluieren, Variablen zu inspizieren, beliebigen Python-Code an jeder Stelle auszuführen und das sogenannte „post-mortem debugging“.
Debugger Starten
Frei nach dem Motto: „viele Wege führen nach Rom“ – gibt es verschiedene Wege den Debugger zu starten. Je nach Bedarf, wo und was zu debuggen ist, entscheidet der Benutzer, wann der Python-Interpreter den Debugger „betritt“.
Von der Kommandozeile
Die Eingabeaufforderung des Debuggers ist (Pdb).
Rufen wir den Debugger von der Kommandozeile aus auf, lädt dieser unser Skript pdb_test.py und stoppt bei der ersten Anweisung:

$ python -m pdb pdb_test.py
> .../pdb_test.py(3)()
-> def test():
(Pdb)

Im Interpreter
Experimentiert man im interaktiven Interpreter, ist es hilfreich den Debugger auch hier via pdb.run oder pdb.runeval starten zu können:

$ python
Python 2.7.2 (default, Oct 27 2011, 01:40:22)
[GCC 4.6.1 20111003 (Red Hat 4.6.1-10)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb
>>> import pdb_test
>>> pdb.run('pdb_test.test()')
> <string>(1)<module>()
(Pdb)

Im Programm
Die oben genannten Möglichkeiten sind in den meisten Fällen nur dann hilfreich, wenn das entwickelte Programm überschaubar ist oder Probleme schon am Anfang der Ausführung auftreten. Stößt man auf Fehler in größeren Programmen oder erst später in der Ausführung, ist es von Vorteil, den Debugger im Programm zu starten:

#!/usr/bin/env python
import pdb
def test():
    pdb.set_trace()
    print "Hallo!"
if __name__ == '__main__':
    test()

Der Funktionsaufruf pdb.set_trace() in Zeile 6 startet den Debugger, wenn die Instruktion erreicht wird:

$ python pdb_test.py
> .../pdb_test.py(7)test()
-> print "Hallo!"
(Pdb)

Nach einem Fehler
Programme nach einem Fehler zu debuggen, nennt sich „post-mortem debugging“. pdb unterstützt das durch die Funktionen pm() und post_mortem().

class Output(object):
    def __init__(self, output):
        self.the_output = output
    def echo(self):
        print self.output

In Zeile 7 versteckt sich ein AttributeError, da wir eigentlich self.the_output ausgeben müssten. Wird die besagte Zeile aufgerufen, stoppt die Ausführung mit dem entsprechenden Fehler. Danach können wir pdb.pm() aufrufen, um den Debugger an der Stelle zu starten, an der die Exception aufgetreten ist:

$ python
Python 2.7.2 (default, Oct 27 2011, 01:40:22)
[GCC 4.6.1 20111003 (Red Hat 4.6.1-10)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb_test
>>> pdb_test.Output("Hallo!").echo()
Traceback (most recent call last):
  File "", line 1, in 
  File "pdb_test.py", line 11, in echo
    print self.output
AttributeError: 'Output' object has no attribute 'output'
>>> import pdb
>>> pdb.pm()
> .../pdb_test.py(11)echo()
-> print self.output
(Pdb)

Debugger benutzen
Der Debugger versteht einen definierten Satz an Befehlen – alle anderen Anweisungen werden als Python-Code behandelt und im Kontext des Programm ausgeführt. So können sogar Variablen und Funktionen während dem Debugging verändert werden. Die Eingabe einer leeren Zeile wiederholt den letzten eingegeben Befehl.
Navigation
w(here): Anzeigen der Position im call stack und nächste auszuführende Instruktion:

(Pdb) where
  /usr/lib64/python2.7/bdb.py(387)run()
-> exec cmd in globals, locals
  (1)()
> .../pdb_test.py(3)()
-> import pdb

l(ist) [start[, ende]]: Auruf ohne Argumente zeigt 5 Zeilen vor und nach der aktuellen Zeile. Mit einem Argument werden 5 Zeilen vor und nach der angegebenen Zeile angezeigt und mit 2 Argumenten der angegebene Bereich:

(Pdb) list
  1  	#!/usr/bin/env python
  2
  3  ->	import pdb
  4
  5  	class Output(object):
  6
  7  	    def __init__(self, output):
  8  	        self.the_output = output
  9
 10  	    def echo(self):
 11  	        print self.output
(Pdb) l 13
  8  	        self.the_output = output
  9
 10  	    def echo(self):
 11  	        print self.output
 12
 13  	if __name__ == '__main__':
 14  	    Output("Hallo!").echo()
[EOF]
(Pdb) l 5, 14
  5  	class Output(object):
  6
  7  	    def __init__(self, output):
  8  	        self.the_output = output
  9
 10  	    def echo(self):
 11  	        print self.output
 12
 13  	if __name__ == '__main__':
 14  	    Output("Hallo!").echo()

u(p), d(own): Aktuelle frame im call stack nach oben (up, älter) oder unten (down, neuer) verschieben:

(Pdb) up
> .../pdb_test.py(14)()
-> Output("Hallo!").echo()
(Pdb) d
> .../pdb_test.py(11)echo()
-> print self.output

Schrittweise Ausfhüren
s(step): Führt die aktuelle Zeile aus und stoppt bei der nächsten aufzurufenden Funktion oder Zeile in der aktuellen Sequenz:

(Pdb) ...
> .../pdb_test.py(14)()
-> Output("Hallo!").echo()
(Pdb) s
--Call--
> .../pdb_test.py(7)__init__()
-> def __init__(self, output):

n(ext): Verhält sich ähnlich wie step, stoppt aber nicht bei Funktionen die von der auszuführenden Anweisung aufgerufen werden:

(Pdb) ...
> .../pdb_test.py(14)()
-> Output("Hallo!").echo()
(Pdb) n
AttributeError: "'Output' object has no attribute 'output'"
> /tmp/pdb_test.py(14)()
-> Output("Hallo!").echo()

r(eturn): Stoppt bei der nächsten return-Anweisung:

#!/usr/bin/env/python
import random
def randint(a, b):
    random_int = random.randint(a, b)
    return random_int
if __name__ == '__main__':
    random.seed()
    print randint(0, 100)
(Pdb) ...
> .../pdb_return.py(11)()
-> print randint(0, 100)
(Pdb) s
--Call--
> .../pdb_return.py(5)randint()
-> def randint(a, b):
(Pdb) r
--Return--
> .../pdb_return.py(7)randint()->58
-> return random_int

Variablen überprüfen
a(rgs): Zeigt die Argumente der aktuellen Funktion und
p expression: Evaluiert expression und zeigt dessen Ausgabe, zum Beispiel den Wert einer Variable:

(Pdb) ...
> .../pdb_return.py(11)()
-> print randint(0, 100)
(Pdb) s
--Call--
> .../pdb_return.py(5)randint()
-> def randint(a, b):
(Pdb) r
--Return--
> .../pdb_return.py(7)randint()->24
-> return random_int
(Pdb) args
a = 0
b = 100
(Pdb) p random_int
24

Natürlich unterstützt der Debugger auch Breakpoints und jump – dazu schreibe ich vielleicht etwas in einem zweiten Teil.
Weiterführende Informationen sind in der Python Dokumentation und den absolut genialen Abhandlungen von Doug Hellmann, namens Python Module of the Week, zu finden, dessen Gliederung ich hier mal ganz frei übernommen habe.

Eric Lippmann
Eric Lippmann
CTO

Eric kam während seines ersten Lehrjahres zu NETWAYS und hat seine Ausbildung bereits 2011 sehr erfolgreich abgeschlossen. Seit Beginn arbeitet er in der Softwareentwicklung und dort an den unterschiedlichen NETWAYS Open Source Lösungen, insbesondere inGraph und im Icinga Team an Icinga Web. Darüber hinaus zeichnet er für viele Kundenentwicklungen in der Finanz- und Automobilbranche verantwortlich.