[英]Step-by-step debugging with IPython
根據我的閱讀,有兩種方法可以在 Python 中調試代碼:
使用傳統的調試器,例如pdb
或ipdb
。 這支持諸如c
表示continue
, n
表示step-over
, s
表示step-into
等命令),但您無法直接訪問 IPython shell,這對於對象檢查非常有用。
通過在代碼中嵌入IPython shell 來使用IPython 。 您可以使用from IPython import embed
,然后在您的代碼中使用embed()
。 當您的程序/腳本遇到embed()
語句時,您將被放入 IPython shell。 這允許使用所有 IPython 好東西來全面檢查對象和測試 Python 代碼。 但是,在使用embed()
您無法再使用方便的鍵盤快捷鍵來逐步完成代碼。
有什么方法可以結合兩全其美嗎? IE
可以在 MATLAB 中找到此類“增強調試”的示例,其中用戶始終可以完全訪問 MATLAB 引擎/shell,並且她仍然可以逐步執行她的代碼、定義條件斷點等。來自我和其他用戶討論過的,這是人們從 MATLAB 遷移到 IPython 時最懷念的調試功能。
我不想讓問題太具體,但我主要在 Emacs 中工作,所以我想知道是否有任何方法可以將這個功能引入其中。 理想情況下,Emacs(或編輯器)將允許程序員在代碼的任何位置設置斷點並與解釋器或調試器通信以使其停在您選擇的位置,並在該位置引入完整的 IPython 解釋器。
ipdb.set_trace() 呢? 在您的代碼中:
import ipdb; ipdb.set_trace()
更新:現在在 Python 3.7 中,我們可以編寫breakpoint()
。 它的工作原理相同,但它也遵守PYTHONBREAKPOINT
環境變量。 此功能來自此 PEP 。
這允許對您的代碼進行全面檢查,並且您可以訪問諸如c
(繼續)、 n
(執行下一行)、 s
(在某個點進入方法)等命令。
請參閱ipdb 存儲庫和命令列表。 IPython現在被稱為(編輯:一部分) Jupyter 。
ps:請注意,ipdb 命令優先於 python 代碼。 所以為了寫list(foo)
你需要print(list(foo))
或!list(foo)
。
此外,如果您喜歡 ipython 提示(它的 emacs 和 vim 模式、歷史記錄、完成等),很容易為您的項目獲得相同的提示,因為它基於python 提示工具包。
您可以使用 IPython 的%pdb
魔法。 只需在 IPython 中調用%pdb
,當發生錯誤時,您會自動ipdb
到ipdb
。 雖然您沒有立即步進, ipdb
之后您就在ipdb
。
這使得調試單個函數變得容易,因為您可以使用%load
加載一個文件,然后運行一個函數。 您可以在正確的位置使用assert
強制錯誤。
%pdb
是一個行魔法。 將其稱為%pdb on
、 %pdb 1
、 %pdb off
或%pdb 0
。 如果不帶參數調用它作為一個切換。
對於 Emacs 中的任何人,該線程展示了如何使用
ipdb
)一起運行。isend-mode
。這兩個包的組合非常強大,允許一個人准確地重新創建 OP 中描述的行為並做更多的事情。
有關 ipdb 的 RealGUD維基文章的更多信息。
在嘗試了許多不同的 Python 調試方法(包括本主題中提到的所有方法)之后,我使用 IPython 調試 Python 的首選方法之一是使用嵌入式 shell。
將以下腳本添加到您的PYTHONPATH
,以便ipsh()
方法可用。
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)
然后,每當我想調試代碼中的某些內容時,我都會將ipsh()
放在我需要進行對象檢查等的位置。例如,假設我想在下面調試my_function
def my_function(b):
a = b
ipsh() # <- This will embed a full-fledged IPython interpreter
a = 4
然后我通過以下方式之一調用my_function(2)
:
無論我如何調用它,解釋器都會停在ipsh()
。 完成后,您可以執行Ctrl-D
,Python 將恢復執行(包括您所做的任何變量更新)。 請注意,如果您從常規 IPython 的 IPython 外殼(上面的情況 2)運行代碼,新的 IPython 外殼將嵌套在您從中調用它的外殼中,這很好,但最好注意。 Eitherway,一旦解釋器上的位置停止ipsh
,我可以檢查的值a
(其為2
),見函數和對象定義了什么等
上面的解決方案可用於讓 Python 在代碼中的任何位置停止,然后將您放入成熟的 IPython 解釋器中。 不幸的是,一旦您調用腳本,它就不允許您添加或刪除斷點,這非常令人沮喪。 在我看來,這是阻止 IPython 成為出色的 Python 調試工具的唯一原因。
解決方法是ipsh()
放置在您希望 Python 解釋器啟動 IPython shell 的不同位置(即breakpoint
)。 然后,您可以使用Ctrl-D
在不同的預定義、硬編碼“斷點”之間“跳轉”,這將退出當前嵌入的 IPython shell 並在解釋器下一次調用ipsh()
時再次停止。
如果你走這條路,退出“調試模式”並忽略所有后續斷點的一種方法是使用ipshell.dummy_mode = True
這將使 Python 忽略我們上面創建的ipshell
對象的任何后續實例化。
您可以從pudb啟動 IPython 會話,然后根據需要返回調試會話。
順便說一句,ipdb 在幕后使用 IPython,您實際上可以使用 IPython 功能,例如 TAB 完成和魔術命令(以%
開頭的命令)。 如果您對 ipdb 沒問題,您可以使用諸如%run
和%debug
命令從 IPython 啟動它。 ipdb 會話實際上比普通的 IPython 更好,因為您可以在堆棧跟蹤等中上下移動。 ipdb 中缺少什么用於“對象檢查”?
此外,與 Emacs >= 24.3 捆綁的 python.el 具有很好的 ipdb 支持。
看起來@gaborous 的答案中的方法已棄用。
新方法似乎是:
from IPython.core import debugger
debug = debugger.Pdb().set_trace
def buggy_method():
debug()
前綴“!” 您在 pdb 中鍵入的命令的符號似乎與在 IPython shell 中執行某些操作具有相同的效果。 這適用於訪問某個函數的幫助,甚至是變量名。 也許這會在一定程度上幫助你。 例如,
ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)
但是 !help(numpy.transpose) 將為您提供有關 numpy.transpose 的預期幫助頁面。 同樣對於變量名稱,假設您有一個變量 l,在 pdb 中鍵入“l”列出代碼,但 !l 打印 l 的值。
你試過這個技巧嗎?
或者更好的是,使用 ipython,然后調用:
from IPython.Debugger import Tracer; debug_here = Tracer()
那么你可以使用
debug_here()
每當你想設置斷點
這個問題的正確、簡單、酷、准確的答案是使用帶有 -d 標志的 %run 宏。
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
您可以從ipdb
啟動IPython
。
誘導ipdb
調試器1 :
import idpb; ipdb.set_trace()
在ipdb>
控制台2 中從內部輸入 IPython:
from IPython import embed; embed()
從IPython
返回ipdb>
控制台:
exit
如果您有幸使用 Emacs,事情會變得更加方便。
這需要使用Mx shell
。 使用yasnippet
和bm ,定義以下代碼段。 這將用set-trace
行替換編輯器中的文本ipdb
。 插入代碼段后,該行將突出顯示,以便於引人注意和導航。 使用Mx bm-next
進行導航。
# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# expand-env: ((yas-after-exit-snippet-hook #'bm-toggle))
# --
import ipdb; ipdb.set_trace()
1全部在一行上,以便於刪除。 由於imports
只發生一次,這種形式確保ipdb
將在您需要時被導入而沒有額外的開銷。
2您可以通過在.pdbrc
文件中導入IPython
來節省一些輸入:
try:
from IPython import embed
except:
pass
這允許您從ipdb
簡單地調用embed()
(當然,僅當安裝了 IPython 時)。
一種選擇是使用像Spyder這樣的 IDE,它應該允許您在調試時與代碼進行交互(實際上是使用 IPython 控制台)。 事實上,Spyder 非常像 MATLAB,我認為這是故意的。 這包括變量檢查器、變量編輯、對文檔的內置訪問等。
如果您在 embed() 控制台中鍵入 exit() 代碼將繼續並轉到下一個 embed() 行。
Pyzo IDE 具有與 OP 要求的類似功能。 您不必以調試模式啟動。 與 MATLAB 類似,命令在 shell 中執行。 當您在某些源代碼行中設置斷點時,IDE 會在那里停止執行,您也可以調試和發出常規 IPython 命令。
然而,除非您設置另一個斷點,否則 step-into 似乎(還?)效果不佳(即停在一行中,然后進入另一個函數)。
盡管如此,來自 MATLAB,這似乎是我找到的最佳解決方案。
從 python 3.2 開始,你有了interact
命令,它可以讓你訪問完整的 python/ipython 命令空間。
從 Emacs 的 IPython-shell 內部運行,並通過 pdb.set_trace() 設置斷點應該可以工作。
使用 python-mode.el、Mx ipython RET 等進行檢查。
在 IPython 中調試
單元格示例:
%%debug
...: for n in range(4):
...: n>2
IPython里面調試
pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
breakpoint()
、 python -m ipdb
等。我同意 OP 的觀點,MATLAB 做得很好,Python 仍然沒有並且確實應該這樣做,因為語言中的幾乎所有東西都支持開發速度而不是生產速度。 也許有一天我會為 CPython 貢獻的不僅僅是一些微不足道的錯誤修復。
https://github.com/ipython/ipython/commit/f042f3fea7560afcb518a1940daa46a72fbcfa68
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.