Langkah-langkah debugging dengan IPython

170

Dari apa yang saya baca, ada dua cara untuk men-debug kode dengan Python:

  • Dengan debugger tradisional seperti pdbatau ipdb. Ini mendukung perintah seperti cuntuk continue, nuntuk step-over, suntuk step-intodll), tetapi Anda tidak memiliki akses langsung ke IPython shell yang dapat sangat berguna untuk pemeriksaan objek.

  • Menggunakan IPython dengan menyematkan shell IPython dalam kode Anda. Anda dapat melakukannya from ipython import embed, lalu gunakan embed()dalam kode Anda. Ketika program / skrip Anda mencapai sebuah embed()pernyataan, Anda dimasukkan ke dalam shell IPython. Ini memungkinkan pemeriksaan penuh objek dan pengujian kode Python menggunakan semua barang IPython. Namun, saat menggunakan embed()Anda tidak bisa lagi selangkah-selangkah melewati kode dengan pintasan keyboard yang praktis.

Apakah ada cara untuk menggabungkan yang terbaik dari kedua dunia? Yaitu

  1. Mampu selangkah demi selangkah melalui kode Anda dengan pintasan keyboard pdb / ipdb yang praktis.
  2. Pada langkah seperti itu (misalnya pada pernyataan yang diberikan), memiliki akses ke shell IPython penuh .

IPython debugging seperti pada MATLAB:

Contoh dari jenis "peningkatan debugging" ini dapat ditemukan di MATLAB, di mana pengguna selalu memiliki akses penuh ke mesin / shell MATLAB, dan dia masih bisa selangkah demi selangkah melalui kodenya, menentukan breakpoint bersyarat, dll. Dari apa yang telah saya diskusikan dengan pengguna lain, ini adalah fitur debugging yang paling banyak orang lewatkan ketika berpindah dari MATLAB ke IPython.

Debugging IPython di Emacs dan editor lain:

Saya tidak ingin membuat pertanyaan terlalu spesifik, tetapi saya bekerja sebagian besar di Emacs, jadi saya bertanya-tanya apakah ada cara untuk membawa fungsi ini ke dalamnya. Idealnya , Emacs (atau editor) akan memungkinkan programmer untuk mengatur breakpoints di mana saja pada kode dan berkomunikasi dengan penerjemah atau debugger untuk menghentikannya di lokasi pilihan Anda, dan membawa ke penerjemah IPython penuh di lokasi itu.

Amelio Vazquez-Reina
sumber
pdb memiliki !perintah yang mengeksekusi perintah python di breakpoint
Dmitry Galchinsky
5
Saya mencari debugger python mirip dengan Matlab, juga! Sebagai contoh, saya melakukan banyak prototyping di shell python. Semua variabel disimpan dengan shell. Sekarang saya menemui masalah. Saya berharap untuk men-debug satu bagian kecil kode, dengan variabel yang dihitung dengan shell. Namun, debugger baru tidak dapat mengakses variabel lama. Tidak nyaman untuk membuat prototipe.
user1914692
1
Untuk pengguna Emacs, RealGUD memiliki antarmuka yang sangat bagus.
Clément
1
Terima kasih @ Clément Saya telah mengikuti repo selama sebulan terakhir dan saya sangat senang dengan proyek ini :) Saya belum mencobanya, tetapi begitu saya melakukannya (atau jika Anda melakukannya) jangan ragu untuk menulis jawaban di sini bahwa mungkin menunjukkan cara mencapai apa yang diminta. Untuk referensi lain, URL-nya adalah github.com/rocky/emacs-dbgr
Amelio Vazquez-Reina
@ Clément Juga, jika Anda memiliki pengalaman dengan RealGUD & ipdb, saya mencoba menggunakannya seperti dijelaskan di sini github.com/rocky/emacs-dbgr/issues/96 tanpa hasil.
Amelio Vazquez-Reina

Jawaban:

61

Anda dapat menggunakan %pdbsihir IPython . Panggil saja %pdbIPython dan ketika kesalahan terjadi, Anda secara otomatis dijatuhkan ke ipdb. Meskipun Anda tidak memiliki langkah segera, Anda masuk ipdbsetelahnya.

Ini membuat debugging fungsi individu mudah, karena Anda bisa memuat file dengan %loaddan kemudian menjalankan fungsi. Anda dapat memaksa kesalahan dengan assertposisi yang tepat.

%pdbadalah sihir garis. Sebut saja sebagai %pdb on, %pdb 1, %pdb offatau %pdb 0. Jika dipanggil tanpa argumen itu berfungsi sebagai toggle.

sebastian
sumber
6
^ Ini adalah jawaban yang sebenarnya
Cesar
Apakah ada sesuatu yang serupa di mana pdb memiliki fungsionalitas seperti IPython tanpa di IPython pada awalnya?
Lucas
4
Anda juga dapat memulai program dengan ipython --pdb file.py -- argsdan dijatuhkan ke ipdb dengan pengecualian. Mungkin layak ditambahkan ke jawabannya.
sebastian
110

Bagaimana dengan ipdb.set_trace ()? Dalam kode Anda:

import ipdb; ipdb.set_trace()

pembaruan : sekarang dengan Python 3.7, kita dapat menulis breakpoint(). Ia bekerja sama, tetapi juga mematuhi PYTHONBREAKPOINTvariabel lingkungan. Fitur ini berasal dari PEP ini .

Ini memungkinkan untuk inspeksi penuh kode Anda, dan Anda memiliki akses ke perintah seperti c(melanjutkan), n(jalankan baris berikutnya), s(masuk ke metode pada titik) dan seterusnya.

Lihat repo ipdb dan daftar perintah . IPython sekarang disebut (sunting: bagian dari) Jupyter .


ps: perhatikan bahwa perintah ipdb lebih diutamakan daripada kode python. Jadi untuk menulis yang list(foo)Anda butuhkan print list(foo).

Juga, jika Anda menyukai ipython prompt (emacs dan vim mode, histori, penyelesaian, ...) mudah untuk mendapatkan yang sama untuk proyek Anda karena didasarkan pada toolkit python prompt .

Ehvince
sumber
10
Inilah cara untuk melakukannya.
j08lue
Jawaban saya sekarang termasuk cara menggunakan ipdbdi Emacs menggunakan RealGUDdan isend-modeuntuk melakukan persis apa yang diminta OP.
Amelio Vazquez-Reina
1
Jupyter bukan pengganti IPython, tetapi untuk IPython Notebook. Notebook Jupyter menggunakan kernel di latar belakang. Untuk notebook Python, kernel biasanya adalah kernel IPython. Proyek IPython berlanjut lebih jauh.
FA
1
breakpoint()sangat bagus. Di PyCharm bahkan menjatuhkan Anda ke dalam debugger PyCharm. Ini juga cara cepat untuk memasukkan debugger PyCharm dari fungsi yang disisipkan ke konsol.
Richard Möhn
40

(Pembaruan pada 28 Mei 2016) Menggunakan RealGUD di Emacs

Bagi siapa pun di Emacs, utas ini menunjukkan cara menyelesaikan semua yang dijelaskan dalam OP (dan banyak lagi) menggunakan

  1. debugger penting baru di Emacs yang disebut RealGUD yang dapat beroperasi dengan debugger apa pun (termasuk ipdb).
  2. Paket Emacs isend-mode.

Kombinasi kedua paket ini sangat kuat dan memungkinkan seseorang untuk menciptakan kembali persis perilaku yang dijelaskan dalam OP dan melakukan lebih banyak lagi.

Info lebih lanjut tentang artikel wiki dari RealGUD untuk ipdb.


Jawaban asli:

Setelah mencoba berbagai metode untuk men-debug Python, termasuk semua yang disebutkan dalam utas ini, salah satu cara yang saya sukai untuk men-debug Python dengan IPython adalah dengan shell yang tertanam.

Mendefinisikan shell IPython tertanam yang kustom:

Tambahkan berikut ini pada skrip ke Anda PYTHONPATH, sehingga metode ipsh()menjadi tersedia.

import inspect

# First import the embed function
from IPython.terminal.embed import InteractiveShellEmbed
from IPython.config.loader import Config

# Configure the prompt so that I know I am in a nested (embedded) shell
cfg = Config()
prompt_config = cfg.PromptManager
prompt_config.in_template = 'N.In <\\#>: '
prompt_config.in2_template = '   .\\D.: '
prompt_config.out_template = 'N.Out<\\#>: '

# Messages displayed when I drop into and exit the shell.
banner_msg = ("\n**Nested Interpreter:\n"
"Hit Ctrl-D to exit interpreter and continue program.\n"
"Note that if you use %kill_embedded, you can fully deactivate\n"
"This embedded instance so it will never turn on again")   
exit_msg = '**Leaving Nested interpreter'

# Wrap it in a function that gives me more context:
def ipsh():
    ipshell = InteractiveShellEmbed(config=cfg, banner1=banner_msg, exit_msg=exit_msg)

    frame = inspect.currentframe().f_back
    msg   = 'Stopped at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)

    # Go back one level! 
    # This is needed because the call to ipshell is inside the function ipsh()
    ipshell(msg,stack_depth=2)

Kemudian, setiap kali saya ingin men-debug sesuatu dalam kode saya, saya menempatkan ipsh()tepat di lokasi di mana saya perlu melakukan pemeriksaan objek, dll. Misalnya, katakan saya ingin men-debug di my_functionbawah ini

Menggunakannya:

def my_function(b):
  a = b
  ipsh() # <- This will embed a full-fledged IPython interpreter
  a = 4

dan kemudian saya memohon my_function(2)dalam salah satu cara berikut:

  1. Baik dengan menjalankan program Python yang menjalankan fungsi ini dari shell Unix
  2. Atau dengan memintanya langsung dari IPython

Terlepas dari bagaimana saya memintanya, penerjemah berhenti di baris yang mengatakan ipsh(). Setelah selesai, Anda bisa melakukannya Ctrl-Ddan Python akan melanjutkan eksekusi (dengan pembaruan variabel apa pun yang Anda buat). Perhatikan bahwa, jika Anda menjalankan kode dari IPython biasa, shell IPython (kasus 2 di atas), shell IPython yang baru akan disarangkan di dalam yang Anda gunakan, yang benar-benar baik-baik saja, tetapi perlu diperhatikan. Bagaimanapun, begitu penerjemah berhenti di lokasi ipsh, saya dapat memeriksa nilai a(yang 2), melihat fungsi dan objek apa yang didefinisikan, dll

Masalah:

Solusi di atas dapat digunakan untuk menghentikan Python di mana pun Anda inginkan dalam kode Anda, dan kemudian menjatuhkan Anda ke penerjemah IPython sepenuhnya. Sayangnya itu tidak memungkinkan Anda menambah atau menghapus breakpoints setelah Anda menjalankan skrip, yang sangat membuat frustrasi. Menurut pendapat saya, ini adalah satu - satunya hal yang mencegah IPython menjadi alat debugging yang bagus untuk Python.

Yang terbaik yang dapat Anda lakukan untuk saat ini:

Solusinya adalah menempatkan ipsh()apriori di lokasi yang berbeda di mana Anda ingin juru bahasa Python meluncurkan shell IPython (yaitu a breakpoint). Anda kemudian dapat "melompat" di antara "breakpoint" berbeda yang ditentukan sebelumnya, dengan kode keras Ctrl-D, yang akan keluar dari shell IPython yang tertanam saat ini dan berhenti lagi setiap kali penerjemah menghantam panggilan berikutnya ipsh().

Jika Anda menggunakan rute ini, salah satu cara untuk keluar dari "mode debugging" dan mengabaikan semua breakpoints berikutnya, adalah dengan menggunakan ipshell.dummy_mode = Trueyang akan membuat Python mengabaikan instantiations berikutnya dari ipshellobjek yang kita buat di atas.

Amelio Vazquez-Reina
sumber
2
Perbarui ini setiap kali Anda menemukan solusi yang lebih baik. Tapi ini terlihat hebat. Saya akan menggunakannya.
Phani
1
Di mana / bagaimana Anda mendefinisikan / mengimpor cfg, banner_msg, exit_msg, dan memeriksa?
Gordon Bean
1
Ini sepertinya bekerja dengan baik untuk saya: github.com/kdodia/snippets/blob/master/ipsh.py
karan.dodia
1
Saya perlu menambahkan import inspectsebelum berfungsi. Kustomisasi baris perintah tampaknya rusak untuk saya.
Pascal
7
Kenapa tidak pakai saja import ipdb; ipdb.set_trace()? Mungkin saya melewatkan sesuatu, tetapi saya tidak melihat keuntungan dari metode Anda yang jauh lebih rumit.
luator
19

Anda dapat memulai sesi IPython dari pudb dan kembali ke sesi debug yang Anda inginkan.

BTW, ipdb menggunakan IPython di belakang layar dan Anda benar-benar dapat menggunakan fungsionalitas IPython seperti penyelesaian TAB dan perintah ajaib (yang dimulai dengan %). Jika Anda OK dengan ipdb, Anda bisa memulainya dari IPython menggunakan perintah seperti %rundan %debug. Sesi ipdb sebenarnya lebih baik daripada IPython biasa dalam arti Anda dapat naik dan turun di tumpukan jejak dll. Apa yang hilang di ipdb untuk "inspeksi objek"?

Juga, python.el yang dibundel dengan Emacs> = 24.3 memiliki dukungan ipdb yang bagus.

tkf
sumber
1
Terima kasih tkf. Saya penggemar paket Emacs-Jedi Anda. Ketika Anda mengatakan bahwa Emacs 24.3 memiliki dukungan yang bagus untuk ipdb, maukah Anda menguraikannya? Saya biasanya memulai IPython dalam M-x ansi-termbuffer terpisah , dan kemudian saya menggunakan mode-isend untuk mengikat buffer sumber saya ke buffer IPython sehingga saya dapat mengirim kode ke interpreter IPython dengan pintasan keyboard yang secara otomatis mengirimkan %pastekeajaiban ke buffer IPython. Ini memungkinkan saya untuk dengan cepat menguji wilayah di IPython. Saya selalu menjalankan program saya dari shell IPython ini rundan menggunakannya embed()untuk berhenti.
Amelio Vazquez-Reina
3
Misalnya, ketika Anda melangkah melalui kode, kode sumber dibuka di buffer lain dengan panah yang menunjuk titik eksekusi saat ini. Anda juga memiliki perintah kirim-daerah, seperti yang Anda lakukan dengan mode isend.
tkf
Terima kasih @tkf Bagaimana saya bisa memulai ipdbsesi debug menggunakan python.eldan memiliki hal-hal seperti send-regiondll ke shell yang sesuai? Secara umum, di mana saya dapat menemukan lebih banyak info tentang ini?
Amelio Vazquez-Reina
Saya menggunakan %runatau %debug. Saya kira import ipdb; ipdb.set_trace()bekerja juga. Saya tidak berpikir Anda dapat mengirim banyak baris ke ipdb. Itu batasan ipdb. Tapi mungkin %pastetriknya berhasil. Anda mungkin ingin mengirim permintaan fitur ke IPython dev.
tkf
13

Sepertinya pendekatan dalam jawaban @ gaborous sudah usang .

Pendekatan baru tampaknya:

from IPython.core import debugger
debug = debugger.Pdb().set_trace

def buggy_method():
    debug()
Elliot Larson
sumber
Apakah ini memungkinkan Anda untuk menggunakan ipdbpernyataan dalam shell ipython, saat debugging?
alpha_989
6

Awalan "!" simbol untuk perintah yang Anda ketik pdb tampaknya memiliki efek yang sama dengan melakukan sesuatu di shell IPython. Ini berfungsi untuk mengakses bantuan untuk fungsi tertentu, atau bahkan nama variabel. Mungkin ini akan membantu Anda sampai batas tertentu. Sebagai contoh,

ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)

Tetapi! Help (numpy.transpose) akan memberi Anda halaman bantuan yang diharapkan di numpy.transpose. Demikian pula untuk nama variabel, misalkan Anda memiliki variabel l, mengetik "l" di pdb mencantumkan kode, tetapi! L mencetak nilai l.

pengguna1953384
sumber
2
Ini adalah pcha pdb yang jahat, dan sangat berharga untuk diketahui.
Wilfred Hughes
4

Apakah Anda mencoba kiat ini ?

Atau lebih baik lagi, gunakan ipython, dan hubungi:

from IPython.Debugger import Tracer; debug_here = Tracer()

maka Anda bisa menggunakan

debug_here()

kapan pun Anda ingin menetapkan breakpoint

gaborous
sumber
4

Anda bisa mulai IPython dari dalam ipdb !

Menginduksi ipdbdebugger 1 :

import idpb; ipdb.set_trace()

Masukkan IPython dari dalam di ipdb>konsol 2 :

from IPython import embed; embed()

Kembali ke ipdb>konsol dari dalam IPython:

exit

Jika Anda cukup beruntung menggunakan Emacs, hal-hal dapat dibuat lebih nyaman!

Ini membutuhkan penggunaan M-x shell. Menggunakan yasnippetdan bm , tentukan cuplikan berikut. Ini akan menggantikan teks ipdbdalam editor dengan set-tracebaris. Setelah memasukkan potongan, garis akan disorot sehingga mudah terlihat dan dinavigasi. Gunakan M-x bm-nextuntuk menavigasi.

# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# expand-env: ((yas-after-exit-snippet-hook #'bm-toggle))
# --
import ipdb; ipdb.set_trace()

1 Semua dalam satu baris untuk penghapusan mudah. Karena importshanya terjadi sekali, formulir ini memastikan ipdbakan diimpor ketika Anda membutuhkannya tanpa biaya tambahan.

2 Anda dapat menyimpan sendiri beberapa pengetikan dengan mengimpor di IPython dalam .pdbrcfile Anda :

try:
    from IPython import embed
except:
    pass

Ini memungkinkan Anda untuk hanya menelepon embed()dari dalam ipdb(tentu saja, hanya ketika IPython diinstal).

Lorem Ipsum
sumber
3

Salah satu opsi adalah menggunakan IDE seperti Spyder yang seharusnya memungkinkan Anda untuk berinteraksi dengan kode Anda saat debugging (sebenarnya menggunakan konsol IPython). Faktanya, Spyder sangat mirip MATLAB, yang saya duga disengaja. Itu termasuk pengawas variabel, pengeditan variabel, akses built-in ke dokumentasi, dll.

Benjamin B.
sumber
3

jawaban yang tepat, mudah, keren, tepat untuk pertanyaannya adalah menggunakan% run macro dengan -d flag.

In [4]: run -d myscript.py
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.        
> /cygdrive/c/Users/mycodefolder/myscript.py(4)<module>()
      2                                                            
      3                        
----> 4 a=1                                            
      5 b=2
Ping
sumber
Ini harus lebih tinggi. Jika saya perlu memasukkan baris ke dalam kode saya (dalam sebuah modul) saya perlu me-restart IPython untuk memuat kembali modul. Dengan ini, saya hanya bisa %save /tmp/foo.py x-yKode yang ingin saya jalankan dan debug dan kemudian %run -d /tmp/foo.pymengatur breakpoint di mana pun saya suka. Jawaban bagus!
Romeo Valentin
2

Jika Anda mengetikkan exit () di konsol embed () kode lanjutkan dan pergi ke baris embed () berikutnya.

TurinTurambar
sumber
2

The Pyzo IDE memiliki kemampuan yang sama seperti OP minta. Anda tidak harus memulai dalam mode debug. Sama halnya dengan MATLAB, perintah dieksekusi di shell. Ketika Anda mengatur break-point di beberapa baris kode sumber, IDE menghentikan eksekusi di sana dan Anda dapat men-debug dan mengeluarkan perintah IPython biasa juga.

Namun tampaknya langkah-ke-dalam (belum?) Berfungsi dengan baik (yaitu berhenti di satu baris dan kemudian melangkah ke fungsi lain) kecuali jika Anda mengatur break-point lain.

Namun, datang dari MATLAB, ini sepertinya solusi terbaik yang saya temukan.

pengguna6715080
sumber
2

Dari python 3.2, Anda memiliki interactperintah, yang memberi Anda akses ke ruang perintah python / ipython penuh.

alpha_989
sumber
1

Menjalankan dari dalam IPython-shell dan breakpoint yang diatur Emacs melalui pdb.set_trace () harus bekerja.

Diperiksa dengan python-mode.el, Mx ipython RET dll.

Andreas Röhler
sumber
1

Mengembangkan Kode Baru

Debugging di dalam IPython

  1. Gunakan eksekusi sel Jupyter / IPython untuk mempercepat iterasi percobaan
  2. Gunakan debug %% untuk melangkah

Contoh Sel:

%%debug
...: for n in range(4):
...:    n>2

Debugging Kode yang Ada

IPython di dalam debugging

  1. Debugging unit test yang rusak: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Debugging di luar kasus uji: breakpoint(), python -m ipdb, dll
  3. IPython.embed () untuk fungsionalitas IPython penuh di mana diperlukan saat dalam debugger

Pikiran tentang Python

Saya setuju dengan OP bahwa banyak hal yang MATLAB lakukan dengan baik Python masih belum memiliki dan benar-benar harus karena hampir semua hal dalam bahasa mendukung kecepatan pengembangan daripada kecepatan produksi. Mungkin suatu hari nanti saya akan berkontribusi lebih dari perbaikan bug sepele untuk CPython.

https://github.com/ipython/ipython/commit/f042f3fea7560afcb518a1940daa46a72fbcfa68

Lihat juga Apakah mungkin menjalankan perintah di IPython dengan debugging?

SwimBikeRun
sumber