簡體   English   中英

使用 IPython 逐步調試

[英]Step-by-step debugging with IPython

根據我的閱讀,有兩種方法可以在 Python 中調試代碼:

  • 使用傳統的調試器,例如pdbipdb 這支持諸如c表示continuen表示step-overs表示step-into等命令),但您無法直接訪問 IPython shell,這對於對象檢查非常有用。

  • 通過在代碼中嵌入IPython shell 來使用IPython 您可以使用from IPython import embed ,然后在您的代碼中使用embed() 當您的程序/腳本遇到embed()語句時,您將被放入 IPython shell。 這允許使用所有 IPython 好東西來全面檢查對象和測試 Python 代碼。 但是,在使用embed()您無法再使用方便的鍵盤快捷鍵來逐步完成代碼。

有什么方法可以結合兩全其美嗎? IE

  1. 能夠使用方便的 pdb/ipdb 鍵盤快捷鍵逐步完成您的代碼。
  2. 在任何這樣的步驟(例如在給定的語句上),都可以訪問成熟的IPython shell

IPython的調試MATLAB:

可以在 MATLAB 中找到此類“增強調試”的示例,其中用戶始終可以完全訪問 MATLAB 引擎/shell,並且她仍然可以逐步執行她的代碼、定義條件斷點等。來自我和其他用戶討論過的,這是人們從 MATLAB 遷移到 IPython 時最懷念的調試功能。

Emacs 和其他編輯器中的 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 ,當發生錯誤時,您會自動ipdbipdb 雖然您沒有立即步進, ipdb之后您就在ipdb

這使得調試單個函數變得容易,因為您可以使用%load加載一個文件,然后運行一個函數。 您可以在正確的位置使用assert強制錯誤。

%pdb是一個行魔法。 將其稱為%pdb on%pdb 1%pdb off%pdb 0 如果不帶參數調用它作為一個切換。

(2016 年 5 月 28 日更新)在 Emacs 中使用 RealGUD

對於 Emacs 中的任何人,該線程展示了如何使用

  1. Emacs 中一個新的重要調試器,稱為RealGUD ,它可以與任何調試器(包括ipdb )一起運行。
  2. Emacs 包isend-mode

這兩個包的組合非常強大,允許一個人准確地重新創建 OP 中描述的行為並做更多的事情。

有關 ipdb 的 RealGUD維基文章的更多信息。


原答案:

在嘗試了許多不同的 Python 調試方法(包括本主題中提到的所有方法)之后,我使用 IPython 調試 Python 的首選方法之一是使用嵌入式 shell。

定義一個自定義的嵌入式 IPython 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)

  1. 通過運行從 Unix shell 調用此函數的 Python 程序
  2. 或者直接從 IPython 調用它

無論我如何調用它,解釋器都會停在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 使用yasnippetbm ,定義以下代碼段。 這將用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 中調試

  1. 使用 Jupyter/IPython 單元執行加速實驗迭代
  2. 使用 %%debug 進行單步調試

單元格示例:

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

調試現有代碼

IPython里面調試

  1. 調試損壞的單元測試: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. 在測試用例之外調試: breakpoint()python -m ipdb等。
  3. IPython.embed() 在調試器中需要時提供完整的 IPython 功能

對 Python 的思考

我同意 OP 的觀點,MATLAB 做得很好,Python 仍然沒有並且確實應該這樣做,因為語言中的幾乎所有東西都支持開發速度而不是生產速度。 也許有一天我會為 CPython 貢獻的不僅僅是一些微不足道的錯誤修復。

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

另請參閱是否可以通過調試在 IPython 中運行命令?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM