Python-Debugger wird bei einem Fehler automatisch gestartet

216

Dies ist eine Frage, über die ich mich schon seit einiger Zeit Gedanken gemacht habe, aber ich habe nie eine geeignete Lösung gefunden. Wenn ich ein Skript ausführe und auf einen IndexError stoße, druckt Python die Zeile, den Ort und die schnelle Beschreibung des Fehlers und beendet ihn. Ist es möglich, pdb automatisch zu starten, wenn ein Fehler auftritt? Ich bin nicht dagegen, eine zusätzliche Importanweisung oben in der Datei zu haben, noch ein paar zusätzliche Codezeilen.

Jeremy
quelle
12
Haben Sie darüber nachgedacht, die akzeptierte Antwort zu ändern?
Joost

Antworten:

127

Sie können traceback.print_exc verwenden , um den Ausnahme-Traceback zu drucken. Verwenden Sie dann sys.exc_info , um den Traceback zu extrahieren, und rufen Sie schließlich pdb.post_mortem mit diesem Traceback auf

import pdb, traceback, sys

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        extype, value, tb = sys.exc_info()
        traceback.print_exc()
        pdb.post_mortem(tb)

Wenn Sie eine interaktive Befehlszeile mit code.interact mit den Einheimischen des Frames starten möchten, von dem die Ausnahme stammt, können Sie dies tun

import traceback, sys, code

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        type, value, tb = sys.exc_info()
        traceback.print_exc()
        last_frame = lambda tb=tb: last_frame(tb.tb_next) if tb.tb_next else tb
        frame = last_frame().tb_frame
        ns = dict(frame.f_globals)
        ns.update(frame.f_locals)
        code.interact(local=ns)
Florian Bösch
quelle
Die erste Lösung wird im Python-Kochbuch
Dirkjot
3
warum sollte jemand lieber codeüber , pdbda diese auf dem ehemaligen auszudehnen scheint?
K3 --- rnc
Ich habe die gleiche Frage? Warum würdest du es vorziehen code?
ARH
2
Verwenden Sie dann sys.exc_info, um den Traceback zu extrahieren und schließlich pdb.post_mortemmit diesem Traceback aufzurufen . Sie müssen kein Traceback-Objekt an übergeben pdb.post_mortem. Aus Dokumenten : Wenn kein Traceback angegeben wird, wird die Ausnahme verwendet, die derzeit behandelt wird (eine Ausnahme muss behandelt werden, wenn die Standardeinstellung verwendet werden soll).
Piotr Dobrogost
2
@PiotrDobrogost Guter Punkt. Ich denke, es ist hilfreicher zu wissen, dass Sie ein TB-Objekt übergeben können, da dies die API besser demonstriert. Gut zu wissen, dass beide Optionen vorhanden sind.
Davida
453
python -m pdb -c continue myscript.py

Wenn Sie das -c continueFlag nicht angeben, müssen Sie zu Beginn der Ausführung 'c' (für Weiter) eingeben. Dann läuft es bis zur Fehlerstelle und gibt Ihnen dort die Kontrolle. Wie von eqzx erwähnt , ist dieses Flag eine neue Ergänzung in Python 3.2, sodass für frühere Python-Versionen die Eingabe von 'c' erforderlich ist (siehe https://docs.python.org/3/library/pdb.html ).

Catherine Devlin
quelle
5
Vielen Dank, dass Sie das " enter 'c' " erwähnt haben. Normalerweise habe ich 'r' (für "run") eingegeben, weil ich daran gewöhnt war gdb. und wenn Sie 'r' eingeben pdb, wird das Programm zwar ausgeführt, aber bei einem Fehler NICHT gestoppt (noch eine Rückverfolgung generiert). hatte mich verwirrt, bis ich das las. Prost!
Sdaau
3
Vineet, es startet Sie mit eingeschaltetem Debugger, geben Sie also "cont" ein und es wird ausgeführt, bis der Fehler auftritt. Von dort aus können Sie Variablen usw. wie in jeder anderen PDF-Sitzung überprüfen.
Catherine Devlin
40
Bitte OP, akzeptiere dies als Antwort. Dies ist die nützlichste und ich habe 5 Minuten damit verbracht, die anderen zu lesen, bis ich diese getroffen habe ... dies sollte die erste sein!
Jhegedus
4
Dies funktioniert auch mit ipdb; und natürlich können Argumente nach dem Skript hinzugefügt werden!
TutuDajuju
20
Dies funktioniert nicht mit Python 2.7. docs.python.org/3/library/pdb.html : "Neu in Version 3.2: pdb.py akzeptiert jetzt eine -c-Option, die Befehle ausführt"
eqzx
68

Verwenden Sie das folgende Modul:

import sys

def info(type, value, tb):
    if hasattr(sys, 'ps1') or not sys.stderr.isatty():
    # we are in interactive mode or we don't have a tty-like
    # device, so we call the default hook
        sys.__excepthook__(type, value, tb)
    else:
        import traceback, pdb
        # we are NOT in interactive mode, print the exception...
        traceback.print_exception(type, value, tb)
        print
        # ...then start the debugger in post-mortem mode.
        # pdb.pm() # deprecated
        pdb.post_mortem(tb) # more "modern"

sys.excepthook = info

Nennen Sie es debug (oder was auch immer Sie möchten) und fügen Sie es irgendwo in Ihren Python-Pfad ein.

Fügen Sie jetzt zu Beginn Ihres Skripts einfach eine hinzu import debug.

tzot
quelle
2
Dies sollte die akzeptierte Antwort sein - es erfordert keine Änderung des vorhandenen Codes oder das Einwickeln von allem in eine try-catchnur hässliche IMO.
Cyphar
Ich liebe diese Antwort ziemlich häufig, ziehe pudbsie aber vor pdb. Kehren Sie immer wieder zum Kopieren und Einfügen zurück, was sicherlich etwas über den Mangel an Ordnung in meinem Leben aussagt.
Stabledog
47

Ipython hat einen Befehl zum Umschalten dieses Verhaltens: % pdb . Es macht genau das, was Sie beschrieben haben, vielleicht sogar ein bisschen mehr (es bietet Ihnen informativere Backtraces mit Syntaxhervorhebung und Code-Vervollständigung). Es ist definitiv einen Versuch wert!

Latanius
quelle
3
Und das ist die einzig vernünftige Antwort darauf.
Michael
4
Dokumentiert unter ipython.readthedocs.io/en/stable/interactive/…
matthiash
4
Beachten Sie, dass - wie auch in den verknüpften Dokumenten @matthiash angegeben - %debugder Debugger nach Auftreten eines Fehlers geöffnet werden kann. Ich ziehe das oft vor %pdb. (Der Kompromiss besteht lediglich darin, qjedes Mal zu tippen, %debugwenn Sie keinen Fehler debuggen möchten, und jedes Mal, wenn Sie einen Fehler debuggen möchten.)
Braham Snyder
1
Beachten Sie auch, dass sich die Einstellung c.InteractiveShell.pdb = Truein jeder Sitzung automatisch ipython_config.pyeinschaltet %pdb.
Braham Snyder
33

Dies ist nicht der Debugger, aber wahrscheinlich genauso nützlich (?)

Ich weiß, ich habe gehört, wie Guido dies irgendwo in einer Rede erwähnt hat.

Ich habe gerade Python -? Geprüft und wenn Sie den Befehl -i verwenden, können Sie dort interagieren, wo Ihr Skript gestoppt wurde.

Also gegeben dieses Skript:

testlist = [1,2,3,4,5, 0]

prev_i = None
for i in testlist:
    if not prev_i:
        prev_i = i
    else:
        result = prev_i/i

Sie können diese Ausgabe erhalten!

PS D:\> python -i debugtest.py
Traceback (most recent call last):
  File "debugtest.py", line 10, in <module>
    result = prev_i/i
ZeroDivisionError: integer division or modulo by zero
>>>
>>>
>>> prev_i
1
>>> i
0
>>>

Um ehrlich zu sein, habe ich das nicht benutzt, aber ich sollte es sein, scheint sehr nützlich zu sein.

Mönch
quelle
Leicht, aber oft genau das, was benötigt wird
Casebash
8
Nicht annähernd so nützlich, startet in den globalen Geltungsbereich. Kann nicht in irgendeiner Funktion stöbern, die abgestürzt ist.
Pixelpax
21

IPython macht dies in der Befehlszeile einfach:

python myscript.py arg1 arg2

kann umgeschrieben werden

ipython --pdb myscript.py -- arg1 arg2

Oder ähnlich, wenn Sie ein Modul aufrufen:

python -m mymodule arg1 arg2

kann umgeschrieben werden

ipython --pdb -m mymodule -- arg1 arg2

Beachten Sie --, dass IPython die Argumente des Skripts nicht als eigene lesen soll.

Dies hat auch den Vorteil, dass anstelle von pdb der erweiterte IPython-Debugger (ipdb) aufgerufen wird.

Wodow
quelle
9

Wenn Sie verwenden ipython, geben Sie nach dem Start Folgendes ein%pdb

In [1]: %pdb
Automatic pdb calling has been turned ON
Willemoes
quelle
Jupyter zum Beispiel
Blaztix
6

Wenn Sie die IPython-Umgebung verwenden, können Sie einfach das% -Debug verwenden, und die Shell bringt Sie zur Inspektion usw. zurück zur betreffenden Zeile mit der ipdb-Umgebung. Eine weitere Option, wie oben angegeben, ist die Verwendung der iPython magic% pdb, die dies effektiv tut das Gleiche.

Jehandad
quelle
Beachten Sie, dass Sie, wenn der Fehler in einer Modulfunktion aufgetreten ist, mit den Befehlen upund downBefehlen durch die Frames navigieren können, um zur Zeile Ihres Codes zurückzukehren, der den Fehler generiert hat.
Jean Paul
3

Um es laufen zu lassen, ohne zu Beginn c eingeben zu müssen, verwenden Sie:

python -m pdb -c c <script name>

Pdb hat seine eigenen Befehlszeilenargumente: -cc führt zu Beginn der Ausführung den Befehl c (ontinue) aus und das Programm wird bis zum Fehler ununterbrochen ausgeführt.

Zlatko Karakaš
quelle
3

python -m pdb script.py in python2.7 drücken Sie weiter, um zu starten, und es wird der Fehler ausgeführt und dort zum Debuggen unterbrochen.

Hannah
quelle
1

Wenn Sie ein Modul ausführen:

python -m mymodule

Und jetzt möchten Sie eingeben, pdbwenn eine Ausnahme auftritt: Gehen Sie folgendermaßen vor:

PYTHONPATH="." python -m pdb -c c mymodule/__main__.py

(oder verlängern Sie Ihre PYTHONPATH). Das PYTHONPATHwird benötigt, damit das Modul im Pfad gefunden wird, da Sie das pdbModul jetzt ausführen.

blueFast
quelle
0

Fügen Sie einen Haltepunkt in den Konstruktor der obersten Ausnahmeklasse in der Hierarchie ein. In den meisten Fällen sehen Sie, wo der Fehler ausgelöst wurde.

Das Setzen eines Haltepunkts bedeutet, was immer Sie möchten: Sie können eine IDE verwenden oder pdb.set_traceoder was auch immer

vlad-ardelean
quelle