使用IPython逐步调试

Ame*_*ina 156 python debugging emacs ipython pdb

根据我的阅读,有两种方法可以在Python中调试代码:

  • 使用传统的调试器,如pdbipdb.这支持诸如cfor continue,nfor step-over,sfor step-into等命令,但是你没有直接访问IPython shell,这对于对象检查非常有用.

  • 使用 IPython的通过嵌入代码中的一个IPython的壳.您可以这样做from ipython import embed,然后embed()在您的代码中使用.当您的程序/脚本命中一个embed()语句时,您将被放入一个IPython shell中.这允许使用所有IPython好东西对对象进行全面检查并测试Python代码.但是,在使用时,embed()您无法使用便捷的键盘快捷键逐步完成代码.

有没有办法结合两全其美?即

  1. 能够 使用方便的pdb/ipdb键盘快捷键逐步完成代码.
  2. 在任何此类步骤(例如,在给定语句上),都可以访问完整的IPython shell.

MATLAB中进行IPython调试:

这种类型的"增强调试"的一个例子可以在MATLAB中找到,用户总是可以完全访问MATLAB引擎/ shell,她仍然可以逐步完成代码,定义条件断点等.我与其他用户讨论的内容,这是人们在从MATLAB迁移到IPython时最想念的调试功能.

在Emacs和其他编辑器中进行IPython调试:

我不想让问题太具体,但我主要在Emacs工作,所以我想知道是否有任何方法可以将此功能纳入其中.理想情况下,Emacs(或编辑器)将允许程序员在代码的任何位置设置断点,并与解释器或调试器通信以使其停在您选择的位置,并在该位置引入完整的IPython解释器.

Ehv*_*nce 104

那么ipdb.set_trace()呢?在你的代码中:

import ipdb; ipdb.set_trace()

更新:现在在Python 3.7中,我们可以编写breakpoint().它的工作原理相同,但它也遵循PYTHONBREAKPOINT环境变量.此功能来自此PEP.

这允许对代码进行全面检查,并且您可以访问诸如c(继续),n(执行下一行),s(逐步进入方法)等命令.

请参阅ipdb repo命令列表.IPython现在被称为(编辑:部分)Jupyter.


ps:请注意,ipdb命令优先于python代码.所以为了写list(foo)你需要的print list(foo).

此外,如果你喜欢ipython提示符(它的emacs和vim模式,历史记录,完成程序......),你可以很容易地为你的项目获得相同的内容,因为它基于python prompt工具包.

  • 这是做到这一点的方法. (10认同)
  • `breakpoint()` 非常棒。在 PyCharm 中,它甚至会将您带入 PyCharm 调试器。这也是从粘贴到控制台的函数进入 PyCharm 调试器的快速方法。 (3认同)

seb*_*ian 46

还没有人提到IPython的%pdb旗帜.只需%pdb在IPython中调用,当发生错误时,您将自动删除ipdb.虽然你没有马上踩到,但你ipdb之后就是这样.

这使得调试单个函数变得容易,因为您只需加载文件%load然后运行函数即可.您可以assert在正确的位置强制执行错误.

  • ^这是真正的答案 (5认同)
  • 您也可以使用`ipython --pdb file.py --args`来启动程序,并在出现异常时删除到ipdb。可能值得添加答案。 (2认同)

Ame*_*ina 40

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

对于Emacs中的任何人,此线程显示如何使用OP(以及更多)中描述的所有内容

  1. Emacs中一个名为RealGUD的新型重要调试器,可以与任何调试器(包括ipdb)一起运行.
  2. Emacs包isend-mode.

这两个软件包的组合非常强大,允许人们重新创建OP中描述的行为,并做更多.

关于RealGUD for ipdb 的wiki文章的更多信息.


原始答案:

在尝试了许多调试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)
Run Code Online (Sandbox Code Playgroud)

然后,每当我想在我的代码中调试某些东西时,我就把它放在ipsh()我需要进行对象检查等的位置.例如,我想在my_function下面调试

使用它:

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

然后我通过my_function(2)以下方式之一调用:

  1. 通过运行从Unix shell调用此函数的Python程序
  2. 或者直接从IPython调用它

无论我如何调用它,解释器都停在那条线上ipsh().一旦完成,您就可以执行Ctrl-D并且Python将继续执行(使用您所做的任何变量更新).请注意,如果您从常规IPython运行IPython shell(上面的案例2)中的代码,新的IPython shell将嵌套在您调用它的内部,这非常好,但是要注意这一点很好.无论如何,一旦解释器停在该位置ipsh,我就可以检查a(是2)的值,看看定义了哪些函数和对象,等等.

问题:

上面的解决方案可以让Python在代码中的任何位置停止,然后将您带入一个成熟的IPython解释器.不幸的是,一旦你调用脚本,它就不允许你添加或删除断点,这非常令人沮丧.在我看来,这是唯一阻止IPython成为Python的优秀调试工具的东西.

你现在可以做的最好的事情:

解决方法是将ipsh()先验放在您希望Python解释器启动IPython shell(即a breakpoint)的不同位置.然后,您可以在不同的预定义,硬编码"断点"之间"跳转" Ctrl-D,这将退出当前嵌入的IPython shell,并在解释器遇到下一次调用时再次停止ipsh().

如果你走这条路,退出"调试模式"并忽略所有后续断点的一种方法就是使用ipshell.dummy_mode = True,这将使Python忽略ipshell我们在上面创建的对象的任何后续实例化.

  • 为什么不使用`import ipdb; ipdb.set_trace()`?也许我想念一些东西,但我没有看到你更复杂的方法的优势. (7认同)
  • 每当您找到更好的解决方案时,请更新此信息.但这看起来很棒.我会用它. (2认同)

tkf*_*tkf 19

您可以从pudb启动IPython会话,然后根据需要返回调试会话.

BTW,ipdb在幕后使用IPython,你可以实际使用IPython功能,如TAB完成和魔术命令(一开始%).如果您对ipdb没问题,可以使用%run和等命令从IPython启动它%debug.ipdb会话实际上比普通IPython更好,你可以在堆栈跟踪中上下等等.在ipdb中缺少什么"对象检查"?

此外,与Emacs> = 24.3捆绑在一起的python.el具有良好的ipdb支持.

  • 例如,当您单步执行代码时,源代码将在另一个缓冲区中打开,箭头指向当前执行点.你也有send-region命令,就像你使用isend-mode一样. (3认同)

Ell*_*son 12

看起来@gaborous的答案中的方法已被弃用.

新方法似乎是:

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

def buggy_method():
    debug()
Run Code Online (Sandbox Code Playgroud)


Lor*_*sum 8

你可以IPython 从内部 ipdb开始。

引入ipdb调试器1

import idpb; ipdb.set_trace()
Run Code Online (Sandbox Code Playgroud)

ipdb>控制台2中输入 IPython :

from IPython import embed; embed()
Run Code Online (Sandbox Code Playgroud)

ipdb>从内部返回控制台IPython

exit
Run Code Online (Sandbox Code Playgroud)

如果您有幸使用 Emacs,事情会变得更加方便。

这需要使用M-x shell. 使用yasnippetbm,定义以下代码片段。ipdb这会将编辑器中的文本替换为该set-trace行。插入代码片段后,该行将突出显示,以便于识别和导航。用于M-x bm-next导航。

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

1全部在一行,方便删除。由于imports仅发生一次,因此该表单ipdb可确保在您需要时导入,而无需额外开销。

2您可以通过IPython .pdbrc文件中导入来节省一些打字时间:

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

embed()这允许您简单地从内部调用ipdb(当然,仅当安装了 IPython 时)。


use*_*384 6

前缀"!" 您在pdb中键入命令的符号似乎与在IPython shell中执行某些操作具有相同的效果.这适用于访问某个函数甚至变量名称的帮助.也许这会在某种程度上帮助你.例如,

ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)
Run Code Online (Sandbox Code Playgroud)

但是!help(numpy.transpose)将为您提供numpy.transpose上的预期帮助页面.类似地,对于变量名称,假设您有一个变量l,在pdb中键入"l"会列出代码,但是!l会打印l的值.

  • 这是一个令人讨厌的 pdb 问题,非常值得了解。 (2认同)

小智 5

这个问题的正确、简单、酷、准确的答案是使用带有 -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
Run Code Online (Sandbox Code Playgroud)