使用IPython逐步调试

问题描述 投票:149回答:13

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

  • 使用传统的调试器,如pdbipdb。这支持ccontinuenstep-oversstep-into等命令,但是你没有直接访问IPython shell,这对于对象检查非常有用。
  • 使用embedding的IPython在代码中使用IPython shell。你可以做from ipython import embed,然后在你的代码中使用embed()。当您的程序/脚本命中embed()语句时,您将被放入IPython shell中。这允许使用所有IPython好东西对对象进行全面检查并测试Python代码。但是,使用embed()时,您无法使用便捷的键盘快捷键逐步完成代码。

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

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

IPython debugging as in MATLAB:

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

IPython debugging in Emacs and other editors:

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

python debugging emacs ipython pdb
13个回答
32
投票

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

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


2
投票

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


2
投票

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

然而,看起来步入也不会(但是?)运行良好(即停在一行然后进入另一个功能),除非你设置另一个断点。

不过,来自MATLAB,这似乎是我发现的最佳解决方案。


1
投票

从Emacs的IPython-shell内部运行,通过pdb.set_trace()设置的断点应该可以运行。

检查python-mode.el,M-x ipython RET等。


1
投票

从python 3.2开始,您可以使用interact命令,它可以访问完整的python / ipython命令空间。


100
投票

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

import ipdb; ipdb.set_trace()

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

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

参见ipdb repoa list of commandsIPython现在被称为(编辑:部分)Jupyter


ps:请注意,ipdb命令优先于python代码。因此,为了写list(foo)你需要print list(foo)

此外,如果你喜欢ipython提示(它的emacs和vim模式,历史,完成,......),你的项目很容易获得相同的,因为它基于python prompt toolkit


40
投票

(Update on May 28, 2016) Using RealGUD in Emacs

对于Emacs中的任何人,this thread展示了如何使用OP(以及更多)中描述的所有内容

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

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

有关ipg的RealGUD的the wiki article的更多信息。


Original answer:

在尝试了许多调试Python的不同方法之后,包括这个线程中提到的所有方法,我用IPython调试Python的首选方法之一是使用嵌入式shell。

Defining a custom embedded 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

Using it:

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的位置,我就可以检查a(它是2)的值,看看定义了哪些函数和对象,等等。

The problem:

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

The best you can do for now:

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

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


19
投票

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

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

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


11
投票

看起来像@ gaborous的回答is deprecated的方法。

新方法似乎是:

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

def buggy_method():
    debug()

6
投票

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

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

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


4
投票

你尝试过this tip吗?

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

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

然后你可以使用

debug_here()

每当你想设置一个断点


3
投票

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


3
投票

这个问题的正确,简单,酷,准确的答案是使用%run macro with -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
© www.soinside.com 2019 - 2024. All rights reserved.