使用IPython进行分步调试


170

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

  • 使用传统的调试器,例如pdbipdb。它支持诸如cfor continuenfor step-oversfor step-into等命令,但是您没有直接访问IPython shell的权限,这对于对象检查非常有用。

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

有什么办法可以融合两全其美?即

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

如在 MATLAB中一样进行 IPython调试:

在MATLAB中可以找到这种“增强调试”类型的示例,在该示例中,用户始终可以完全访问MATLAB引擎/外壳,并且她仍然可以逐步完成代码,定义条件断点等。我已经与其他用户讨论过,这是人们从MATLAB转移到IPython时最想念的调试功能。

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

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


pdb具有!在断点执行任何python命令的命令
Dmitry Galchinsky 2013年

5
我也在寻找类似于Matlab的python调试器!例如,我在python shell中做了很多原型设计。所有变量都与外壳一起保存。现在我遇到一个问题。我希望调试一小段代码,并将这些计算出的变量与外壳一起使用。但是,新的调试器无法访问旧变量。进行原型制作不方便。
user1914692 2013年

1
对于Emacs用户而言,RealGUD具有非常好的界面。
克莱门特

1
谢谢@Clément,我在过去的一个月中一直在关注该仓库,对此项目我感到非常兴奋:)我还没有尝试过,但是一旦我这样做(或者如果您这样做),就可以在这里随意写一个答案,也许显示了如何完成要求的工作。对于其他参考,URL为github.com/rocky/emacs-dbgr
Amelio Vazquez-Reina

@Clément另外,如果您对RealGUD和ipdb有任何经验,我尝试按照github.com/rocky/emacs-dbgr/issues/96的说明使用它,但是没有运气。
阿梅里奥·瓦兹奎兹·雷纳

Answers:


61

您可以使用IPython的%pdb魔力。只需调用%pdbIPython,当发生错误时,您会自动转到ipdb。虽然您没有立即迈出一步,但您ipdb之后就进入了。

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

%pdb是线魔术。呼之为%pdb on%pdb 1%pdb off%pdb 0。如果在不带参数的情况下调用它,则它将作为切换。


6
^这是真正的答案
Cesar

pdb是否具有类似IPython的功能,而最初不包含在IPython中,是否有类似的东西?
卢卡斯

4
您也可以使用启动程序,ipython --pdb file.py -- args并在出现异常时将其放到ipdb中。可能值得添加答案。
sebastian

110

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提示符工具包,因此很容易为您的项目获得相同的名称。


10
这是这样做的方法。
j08lue 2015年

我的答案现在包括如何ipdb在Emacs中使用,RealGUD以及isend-mode如何精确执行OP的要求。
阿梅里奥·瓦兹奎兹·雷纳

1
Jupyter不是IPython的替代品,而是IPython Notebook的替代品。Jupyter笔记本在后台使用内核。对于Python笔记本,内核通常是IPython内核。该IPython项目继续进一步。
FA

1
breakpoint()太好了 在PyCharm中,它甚至使您进入PyCharm调试器。这也是从粘贴到控制台的函数进入PyCharm调试器的快速方法。
理查德·莫恩(RichardMöhn)

40

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

对于Emacs中的任何人,此线程都说明了如何使用

  1. Emacs中一个称为RealGUD的新的重要调试器,可以与任何调试器(包括ipdb)一起使用。
  2. Emacs软件包isend-mode

这两个软件包的组合非常强大,可以使它们完全重新创建OP中描述的行为,甚至可以做更多的事情。

有关RealGUD for ipdb 的Wiki文章的更多信息。


原始答案:

在尝试了多种不同的调试Python方法(包括本线程中提到的所有内容)之后,我使用IPython调试Python的首选方法之一是使用嵌入式外壳程序。

定义定制的嵌入式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 Shell(上面的案例2)运行代码,那么新的IPython Shell将嵌套在您从中调用它的外壳内,这很好,但是请注意。无论哪种方式,一旦解释器停在的位置ipsh,我都可以检查a2)的值,查看定义了哪些函数和对象,等等。

问题:

上面的解决方案可以使Python在代码中所需的任何位置停止,然后将您带入完整的IPython解释器。不幸的是,调用脚本后,它不允许您添加或删除断点,这非常令人沮丧。在我看来,这是阻止IPython成为Python出色的调试工具的唯一原因

您目前可以做的最好的事情是:

一种解决方法是ipsh()在要Python解释程序启动IPython Shell的不同位置(即breakpoint)放置先验先验。然后,您可以使用以下命令在不同的预定义,硬编码的“断点”之间“跳转”Ctrl-D,这将退出当前的嵌入式IPython shell,并且每当解释器单击下一个调用时再次停止ipsh()

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


2
每当您找到更好的解决方案时,请进行更新。但这看起来很棒。我会用它。
Phani

1
您在哪里/如何定义/导入cfg,banner_msg,exit_msg和检查?
Gordon Bean 2014年


1
我需要先添加import inspect才能起作用。命令行自定义似乎对我来说很困难。
Pascal 2015年

7
为什么不只是使用import ipdb; ipdb.set_trace()?也许我错过了一些事情,但是我看不出您使用更复杂的方法的优势。
luator 2015年

19

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

顺便说一句,ipdb在幕后使用IPython,您实际上可以使用IPython功能,例如TAB完成和魔术命令(以开头的命令%)。如果你是IPDB OK,你可以从IPython中使用命令,如启动%run%debug。从某种意义上讲,ipdb会话实际上比普通的IPython会话要好,因为您可以在堆栈跟踪中上下移动。ipdb中“对象检查”缺少什么?

另外,与Emacs> = 24.3捆绑在一起的python.el具有不错的ipdb支持。


1
谢谢tkf。我是您的Emacs-Jedi软件包的忠实粉丝。当您说Emacs 24.3对ipdb有很好的支持时,您介意进行详细说明吗?我通常在一个单独的M-x ansi-term缓冲区中启动IPython ,然后使用isend-mode将源缓冲区绑定到IPython缓冲区,以便我可以使用键盘快捷键将代码发送到IPython解释器,该快捷键会自动将%paste魔术发送到IPython缓冲区。这使我可以快速测试IPython中的区域。我总是从IPython shell运行我的程序,run并使用它们embed()来停止。
Amelio Vazquez-Reina 2013年

3
例如,当您单步执行代码时,将在另一个缓冲区中打开源代码,并用箭头指向当前执行点。您还具有send-region命令,就像使用isend-mode一样。
tkf

谢谢@tkf我该如何启动ipdb调试会话python.el并使用诸如此类的东西send-region到相应的shell?一般来说,在哪里可以找到更多信息?
阿梅里奥·瓦兹克斯·雷纳

我使用%run%debug。我想import ipdb; ipdb.set_trace()也可以。我认为您无法向ipdb发送多行。那是ipdb的局限。但是可能会%paste起作用。您可能要向IPython开发人员发送功能请求。
tkf


6

前缀“!” 在pdb中键入命令的符号似乎与在IPython shell中执行操作具有相同的效果。这适用于访问某些功能甚至变量名的帮助。也许这会对您有所帮助。例如,

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

但是!help(numpy.transpose)将为您提供有关numpy.transpose的预期帮助页面。同样,对于变量名,假设您有一个变量l,在pdb中键入“ l”会列出代码,但是!l会打印出l的值。


2
这是一个令人讨厌的pdb陷阱,非常值得了解。
Wilfred Hughes 2014年

4

您是否尝试过此技巧

或者更好的是,使用ipython并调用:

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

那你就可以用

debug_here()

每当你想设置一个断点


4

您可以IPython 从内部 开始ipdb

引入ipdb调试器1

import idpb; ipdb.set_trace()

输入IPython的从内部ipdb>控制台2

from IPython import embed; embed()

ipdb>从以下位置返回到控制台IPython

exit

如果您有幸使用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()

1全部一行,易于删除。由于imports仅发生一次,因此此表单确保ipdb将在您需要时将其导入,而不会产生额外的开销。

2,您可以通过导入自己节省一些打字IPython 的内.pdbrc文件

try:
    from IPython import embed
except:
    pass

这使您可以简单地embed()从内部进行调用ipdb(当然,仅在安装IPython时)。


3

一种选择是使用像Spyder这样的IDE ,它应该允许您在调试时与代码进行交互(实际上是使用IPython控制台)。实际上,Spyder非常类似于MATLAB,我认为这是故意的。其中包括变量检查器,变量编辑,对文档的内置访问等。


3

该问题的正确,简单,酷爽的准确答案是将%run宏与-d标志一起使用。

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

这应该更高。如果需要在代码中(模块内)插入一行,则需要重新启动IPython以重新加载模块。为此,我可以只%save /tmp/foo.py x-y执行我想运行和调试的代码,然后%run -d /tmp/foo.py在我喜欢的任何地方设置断点。好答案!
罗密欧·瓦伦丁

2

如果在embed()控制台中键入exit(),代码将继续并转到下一个embed()行。


2

Pyzo IDE具有与OP问类似的功能。您不必以调试模式启动。与MATLAB相似,命令在shell中执行。在某些源代码行中设置断点时,IDE会在此处停止执行,并且您还可以调试和发出常规IPython命令。

但是,除非您设置了另一个断点,否则似乎单步执行(还好吗?)效果不佳(即在一行中停下来,然后步入另一功能)。

仍然来自MATLAB,这似乎是我找到的最佳解决方案。


2

在python 3.2中,您具有interact命令,该命令可让您访问完整的python / ipython命令空间。



1

开发新代码

在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中运行命令?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.