Automatyczne uruchamianie debugera python po błędzie

216

To pytanie zastanawiałem się od dłuższego czasu, ale nigdy nie znalazłem odpowiedniego rozwiązania. Jeśli uruchomię skrypt i napotkam, powiedzmy, błąd indeksu, python wypisuje wiersz, lokalizację i szybki opis błędu i kończy działanie. Czy możliwe jest automatyczne uruchomienie pdb po napotkaniu błędu? Nie jestem przeciwny posiadaniu dodatkowej instrukcji importu na górze pliku ani kilku dodatkowych wierszy kodu.

Jeremy
źródło
12
Czy rozważałeś zmianę zaakceptowanej odpowiedzi?
Joost

Odpowiedzi:

127

Możesz użyć traceback.print_exc, aby wydrukować śledzenie wyjątków. Następnie użyj sys.exc_info, aby wyodrębnić śledzenie i wreszcie wywołać pdb.post_mortem z tym śledzeniem

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)

Jeśli chcesz uruchomić interaktywny wiersz poleceń za pomocą code.interact, używając ustawień lokalnych ramki, z której pochodzi wyjątek, możesz to zrobić

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
źródło
pierwsze rozwiązanie jest dalej omówione w książce kucharskiej Pythona
dirkjot
3
dlaczego ktoś wolałby codenad pdbponieważ ten ostatni wydaje się rozszerzać na byłego?
K3 --- rnc
Mam to samo pytanie? Dlaczego wolisz code?
ARH
2
Następnie użyj, sys.exc_infoaby wyodrębnić ślad zwrotny i na końcu wywołać pdb.post_mortemten ślad . Nie musisz przekazywać obiektu traceback pdb.post_mortem. Z dokumentacji : Jeśli nie podano śledzenia, wykorzystuje jeden z wyjątków, który jest obecnie obsługiwany (wyjątek musi być obsługiwany, jeśli ma być zastosowany domyślny).
Piotr Dobrogost
2
@PiotrDobrogost Dobra uwaga. Myślę jednak, że lepiej jest wiedzieć, że możesz przekazać obiekt tb, ponieważ lepiej pokazuje interfejs API. Dobrze wiedzieć, że istnieją obie opcje.
davidA,
454
python -m pdb -c continue myscript.py

Jeśli nie podasz -c continueflagi, musisz wpisać „c” (dla Kontynuuj), gdy rozpocznie się wykonywanie. Następnie przejdzie do punktu błędu i da ci kontrolę. Jak wspomniano w eqzx , ta flaga jest nowym dodatkiem w Pythonie 3.2, więc wpisanie „c” jest wymagane we wcześniejszych wersjach Pythona (patrz https://docs.python.org/3/library/pdb.html ).

Catherine Devlin
źródło
5
Dzięki za wzmiankę o „ enter” c ”- zwykle wpisywałem„ r ”(dla„ run ”), przywykłem do tego gdb; a kiedy wpiszesz „r” pdb, program rzeczywiście działa, ale NIE zatrzymuje się (ani nie generuje śladu wstecznego) po błędzie; zastanawiałem się, dopóki tego nie przeczytam. Twoje zdrowie!
sdaau
3
Vineet, uruchomi cię z włączonym debuggerem, więc wpisz „cont” i będzie działał do momentu napotkania błędu. Stamtąd możesz sprawdzać zmienne itp. Jak w każdej innej sesji pdb.
Catherine Devlin,
40
Proszę OP, zaakceptuj to jako odpowiedź. Jest to najbardziej przydatne i zmarnowałem 5 minut na czytanie pozostałych, aż trafiłem w ten ... To powinno być pierwsze!
jhegedus
4
Działa to również z ipdb; i oczywiście po skrypcie można dodawać argumenty!
tutuDajuju
20
To nie działa z Python 2.7. docs.python.org/3/library/pdb.html : „Nowość w wersji 3.2: pdb.py akceptuje teraz opcję -c, która wykonuje polecenia”
eqzx
68

Użyj następującego modułu:

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

Nazwij go debug(lub cokolwiek chcesz) i umieść go gdzieś na ścieżce Pythona.

Teraz, na początku skryptu, po prostu dodaj import debug.

tzot
źródło
2
To powinna być zaakceptowana odpowiedź - nie wymaga żadnej modyfikacji istniejącego kodu ani zawijania wszystkiego, try-catchco jest po prostu brzydkie IMO.
cyphar
Uwielbiam tę odpowiedź dość często, ale wolę pudbponad pdb. Powracam do kopiowania i wklejania, co z pewnością mówi coś o braku porządku w moim życiu.
Stabledog,
47

Ipython ma polecenie przełączające to zachowanie: % pdb . Robi dokładnie to, co opisałeś, może nawet nieco więcej (daje ci więcej informacji o śladach wstecznych z podświetlaniem składni i uzupełnianiem kodu). Zdecydowanie warto spróbować!

Latanius
źródło
3
I to jedyna rozsądna odpowiedź na to.
Michael
4
Udokumentowane na ipython.readthedocs.io/en/stable/interactive/…
matthiash
4
Zauważ, że - jak zauważono również w połączonej dokumentacji @matthiash - %debugpozwala otworzyć debugger po napotkaniu błędu. Często wolę to niż %pdb. (Kompromis polega jedynie na pisaniu za qkażdym razem, gdy nie chcesz debugować błędu, a pisaniu za %debugkażdym razem, gdy chcesz debugować błąd.)
Braham Snyder,
1
Zauważ też, że ustawienie c.InteractiveShell.pdb = Truew swoich ipython_config.pywłącza się %pdbautomatycznie dla każdej sesji.
Braham Snyder
33

To nie jest debugger, ale prawdopodobnie równie użyteczny (?)

Wiem, że słyszałem, jak Guido wspomniał o tym gdzieś w przemówieniu.

Właśnie sprawdziłem python - ?, a jeśli użyjesz polecenia -i, możesz wejść w interakcję w miejscu, w którym skrypt się zatrzymał.

Biorąc pod uwagę ten skrypt:

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

Możesz uzyskać ten wynik!

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
>>>

Szczerze mówiąc, nie korzystałem z tego, ale powinienem być, wydaje się bardzo przydatny.

monkut
źródło
Lekki, ale często tylko to, czego potrzeba
Casebash
8
Nie tak przydatny, wchodzi w zakres globalny. Nie można pogrzebać w jakiejkolwiek funkcji, która uległa awarii.
pixelpax
21

IPython upraszcza to w wierszu poleceń:

python myscript.py arg1 arg2

można przepisać na

ipython --pdb myscript.py -- arg1 arg2

Lub podobnie, jeśli wywołujesz moduł:

python -m mymodule arg1 arg2

można przepisać na

ipython --pdb -m mymodule -- arg1 arg2

Zwróć uwagę, --aby zatrzymać IPython przed odczytaniem argumentów skryptu jako własnych.

Ma to również tę zaletę, że wywołuje ulepszony debugger IPython (ipdb) zamiast pdb.

wodow
źródło
9

Jeśli używasz ipython, po uruchomieniu wpisz%pdb

In [1]: %pdb
Automatic pdb calling has been turned ON
Willemoes
źródło
Na przykład
Jupyter
6

Jeśli używasz środowiska IPython, możesz po prostu użyć% debugowania, a powłoka zabierze Cię z powrotem do linii obrażeń ze środowiskiem ipdb w celu przeprowadzenia kontroli itp. Inną opcją, jak wskazano powyżej, jest użycie iPython magic% pdb, który skutecznie działa to samo.

Jehandad
źródło
Zauważ, że jeśli błąd wystąpił w funkcji modułu, możesz poruszać się po ramkach za pomocą poleceń upi down, aby powrócić do wiersza kodu, który wygenerował błąd.
Jean Paul
4

Możesz umieścić tę linię w swoim kodzie:

import pdb ; pdb.set_trace()

Więcej informacji: Uruchom debuger w języku Python w dowolnej linii

Rory
źródło
8
To zatrzymuje kod i uruchamia debugger w wierszu, w którym umieściłeś to polecenie, a nie w wierszu, w którym wystąpił wyjątek
blueFast
3

Aby uruchomić go bez konieczności wpisywania c na początku, użyj:

python -m pdb -c c <script name>

Pdb ma własne argumenty wiersza poleceń: -cc wykona polecenie c (ontinue) na początku wykonywania, a program będzie działał nieprzerwanie aż do błędu.

Zlatko Karakaš
źródło
3

python -m pdb script.py w python2.7 naciśnij kontynuuj, aby uruchomić, a on uruchomi się do błędu i przerwie tam w celu debugowania.

Hanna
źródło
1

Jeśli korzystasz z modułu:

python -m mymodule

A teraz chcesz wprowadzić, pdbgdy wystąpi wyjątek, wykonaj następujące czynności:

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

(lub przedłuż swój PYTHONPATH). Jest PYTHONPATHto konieczne, aby moduł znalazł się na ścieżce, ponieważ pdbmoduł jest teraz uruchomiony .

blueFast
źródło
0

Umieść punkt przerwania wewnątrz konstruktora najwyższej klasy wyjątków w hierarchii i przez większość czasu zobaczysz, gdzie wystąpił błąd.

Umieszczenie punktu przerwania oznacza, co chcesz, aby to znaczyło: możesz użyć IDE pdb.set_trace, lub cokolwiek innego

vlad-ardelean
źródło