Mulai python debugger secara otomatis saat terjadi kesalahan

216

Ini adalah pertanyaan yang telah saya tanyakan selama beberapa waktu, namun saya belum pernah menemukan solusi yang cocok. Jika saya menjalankan skrip dan menemukan, katakanlah IndexError, python mencetak garis, lokasi, dan deskripsi cepat tentang kesalahan dan keluar. Apakah mungkin untuk memulai pdb secara otomatis ketika kesalahan terjadi? Saya tidak menentang memiliki pernyataan impor tambahan di bagian atas file, atau beberapa baris kode tambahan.

Jeremy
sumber
12
Sudahkah Anda mempertimbangkan untuk mengubah jawaban yang diterima?
Joost

Jawaban:

127

Anda dapat menggunakan traceback.print_exc untuk mencetak pengecualian traceback. Kemudian gunakan sys.exc_info untuk mengekstrak traceback dan akhirnya memanggil pdb.post_mortem dengan traceback itu

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)

Jika Anda ingin memulai baris perintah interaktif dengan code.interact menggunakan penduduk lokal dari frame di mana pengecualian berasal Anda bisa melakukannya

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
sumber
solusi pertama dibahas lebih lanjut di buku resep python
dirkjot
3
mengapa ada orang yang lebih memilih codelebih pdbkarena yang terakhir tampaknya memperluas mantan?
K3 --- rnc
Saya punya pertanyaan yang sama? Mengapa Anda lebih suka code?
ARH
2
Kemudian gunakan sys.exc_infountuk mengekstrak traceback dan akhirnya memanggil pdb.post_mortemdengan traceback itu . Anda tidak perlu meneruskan objek traceback ke pdb.post_mortem. Dari dokumen : Jika tidak ada traceback yang diberikan, ia menggunakan salah satu pengecualian yang sedang ditangani (pengecualian harus ditangani jika default akan digunakan).
Piotr Dobrogost
2
@PiotrDobrogost Poin bagus. Saya pikir lebih membantu untuk mengetahui bahwa Anda dapat melewatkan objek tb, karena lebih baik menunjukkan API. Baik untuk mengetahui kedua opsi yang ada.
davidA
453
python -m pdb -c continue myscript.py

Jika Anda tidak memberikan -c continuetanda maka Anda harus memasukkan 'c' (untuk Lanjutkan) saat eksekusi dimulai. Kemudian akan berjalan ke titik kesalahan dan memberi Anda kontrol di sana. Seperti yang disebutkan oleh eqzx , flag ini adalah tambahan baru di python 3.2 sehingga memasukkan 'c' diperlukan untuk versi Python sebelumnya (lihat https://docs.python.org/3/library/pdb.html ).

Catherine Devlin
sumber
5
Terima kasih telah menyebutkan " enter 'c' " - Saya biasanya digunakan untuk memasukkan 'r' (untuk "run"), digunakan untuk itu dari gdb; dan ketika Anda memasukkan 'r' pdb, program memang berjalan, tetapi TIDAK berhenti (atau menghasilkan backtrace) pada kesalahan; membuat saya bingung sampai saya membaca ini. Bersulang!
sdaau
3
Vineet, itu akan memulai Anda dengan debugger aktif, jadi masukkan "cont" dan itu akan berjalan sampai kesalahan ditemukan. Dari sana Anda dapat memeriksa variabel, dll. Seperti pada sesi pdb lainnya.
Catherine Devlin
40
Tolong OP, terimalah ini sebagai jawaban. Ini adalah yang paling berguna dan saya sudah membuang waktu 5 menit membaca yang lain sampai saya menekan yang ini ... ini harus menjadi yang pertama!
jhegedus
4
Ini juga berfungsi sama dengan ipdb; dan tentu saja argumen dapat ditambahkan setelah skrip!
tutuDajuju
20
Ini tidak berfungsi dengan Python 2.7. docs.python.org/3/library/pdb.html : "Baru dalam versi 3.2: pdb.py sekarang menerima opsi -c yang mengeksekusi perintah"
eqzx
68

Gunakan modul berikut:

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

Beri nama debug(atau apa pun yang Anda suka) dan letakkan di suatu tempat di jalur python Anda.

Sekarang, di awal skrip Anda, tambahkan saja import debug.

tzot
sumber
2
Ini harus menjadi jawaban yang diterima - itu tidak memerlukan modifikasi kode yang ada atau membungkus segala sesuatu dalam try-catchyang hanya IMO jelek.
cyphar
Saya suka jawaban ini agak sering, tapi lebih suka pudblebih pdb. Terus kembali ke copy-and-paste, yang pasti mengatakan sesuatu tentang kurangnya ketertiban dalam hidup saya.
Stabledog
47

Ipython memiliki perintah untuk mengubah perilaku ini: % pdb . Itu tidak persis seperti yang Anda jelaskan, bahkan mungkin sedikit lebih (memberi Anda backtraces lebih informatif dengan penyorotan sintaksis dan penyelesaian kode). Pasti patut dicoba!

Latanius
sumber
3
Dan itu satu-satunya jawaban yang masuk akal untuk ini.
Michael
4
Didokumentasikan di ipython.readthedocs.io/en/stable/interactive/…
matthiash
4
Perhatikan bahwa - sebagaimana dicatat dalam docs @matthiash linked - %debugmemungkinkan seseorang untuk membuka debugger setelah menemukan kesalahan. Saya sering lebih suka ini %pdb. (Trade-off hanya mengetik qsetiap kali Anda tidak ingin debug kesalahan vs mengetik %debugsetiap kali Anda melakukan ingin debug kesalahan.)
Braham Snyder
1
Perhatikan juga bahwa pengaturan c.InteractiveShell.pdb = Truepada ipython_config.pygiliran seseorang dihidupkan %pdbsecara otomatis untuk setiap sesi.
Braham Snyder
33

Ini bukan debugger, tapi mungkin sama bermanfaatnya (?)

Saya tahu saya mendengar Guido menyebutkan ini dalam suatu pidato di suatu tempat.

Saya baru saja memeriksa python - ?, dan jika Anda menggunakan perintah -i Anda dapat berinteraksi di mana skrip Anda berhenti.

Jadi diberikan skrip ini:

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

Anda bisa mendapatkan hasil ini!

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

Sejujurnya saya belum pernah menggunakan ini, tetapi saya harus, tampaknya sangat berguna.

monkut
sumber
Ringan, tetapi seringkali hanya apa yang dibutuhkan
Casebash
8
Hampir tidak berguna, diluncurkan ke ruang lingkup global. Tidak bisa melihat-lihat fungsi apa pun yang macet.
pixelpax
21

IPython membuatnya mudah di baris perintah:

python myscript.py arg1 arg2

dapat ditulis ulang menjadi

ipython --pdb myscript.py -- arg1 arg2

Atau, sama halnya, jika memanggil modul:

python -m mymodule arg1 arg2

dapat ditulis ulang menjadi

ipython --pdb -m mymodule -- arg1 arg2

Perhatikan --untuk menghentikan IPython dari membaca argumen skrip sebagai miliknya.

Ini juga memiliki keuntungan menggunakan debugger IPython yang ditingkatkan (ipdb) daripada pdb.

wodow
sumber
9

Jika Anda menggunakan ipython, setelah meluncurkan jenis%pdb

In [1]: %pdb
Automatic pdb calling has been turned ON
Willemoes
sumber
Jupyter misalnya
Blaztix
6

Jika Anda menggunakan lingkungan IPython, Anda bisa menggunakan% debug dan shell akan membawa Anda kembali ke garis yang menyinggung dengan lingkungan ipdb untuk inspeksi dll. Opsi lain seperti yang ditunjukkan di atas adalah menggunakan sihir iPython% pdb yang secara efektif tidak sama.

Jehandad
sumber
Perhatikan bahwa jika kesalahan terjadi dalam fungsi modul, Anda dapat menavigasi melalui frame dengan updan downperintah untuk kembali ke baris kode Anda yang menghasilkan kesalahan.
Jean Paul
4

Anda dapat meletakkan baris ini dalam kode Anda:

import pdb ; pdb.set_trace()

Info lebih lanjut: Mulai python debugger di baris mana pun

Rory
sumber
8
Ini menghentikan kode dan memulai debugger di baris di mana Anda menempatkan perintah ini, bukan pada baris di mana pengecualian terjadi
blueFast
3

Untuk menjalankannya tanpa harus mengetikkan c di awal gunakan:

python -m pdb -c c <script name>

Pdb memiliki argumen baris perintahnya sendiri: -cc akan mengeksekusi perintah c (ontinue) pada awal eksekusi dan program akan berjalan tanpa gangguan sampai kesalahan.

Zlatko Karakaš
sumber
3

python -m pdb script.py di python2.7 tekan terus untuk memulai dan itu akan berjalan ke kesalahan dan istirahat di sana untuk debug.

Hannah
sumber
1

Jika Anda menjalankan modul:

python -m mymodule

Dan sekarang Anda ingin memasukkan pdbketika pengecualian terjadi, lakukan ini:

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

(atau perpanjang Anda PYTHONPATH). The PYTHONPATHdiperlukan agar modul ditemukan di jalan, karena Anda menjalankan pdbmodul sekarang.

blueFast
sumber
0

Letakkan breakpoint di dalam konstruktor kelas pengecualian teratas dalam hierarki, dan sebagian besar waktu Anda akan melihat di mana kesalahan itu muncul.

Puting breakpoint berarti apa pun yang Anda inginkan: Anda dapat menggunakan IDE, atau pdb.set_trace, atau apa pun

vlad-ardelean
sumber