193

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

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

  • 通过在代码中嵌入IPython shell 来使用 IPython 。你可以做,然后在你的代码中使用。当您的程序/脚本遇到语句时,您将被放入 IPython shell。这允许使用所有 IPython 好东西来全面检查对象和测试 Python 代码。但是,在使用时,您不能再使用方便的键盘快捷键逐步完成代码。from IPython import embedembed()embed()embed()

有什么方法可以结合两全其美?IE

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

MATLAB中进行IPython 调试:

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

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

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

4

15 回答 15

117

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 提示符工具包

于 2013-06-03T10:46:18.050 回答
80

你可以使用 IPython 的%pdb魔法。只需%pdb在 IPython 中调用,当发生错误时,您将自动下降到ipdb. 虽然您没有立即采取措施,但您在ipdb之后。

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

%pdb是线魔法。将其称为%pdb on%pdb 1或。如果在没有参数的情况下调用它,它将作为切换。%pdb off%pdb 0

于 2017-04-14T14:14:04.773 回答
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)

然后,每当我想在我的代码中调试某些东西时,我都会将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,我就可以检查a(which be 2) 的值,查看定义了哪些函数和对象等。

问题:

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

你现在能做的最好的事情:

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

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

于 2014-04-30T12:28:48.020 回答
19

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

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

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

于 2013-06-01T19:10:09.077 回答
13

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

新方法似乎是:

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

def buggy_method():
    debug()
于 2017-03-20T19:51:12.203 回答
7

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

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

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

于 2013-06-14T15:50:31.863 回答
5

你可以IPython 从内部 ipdb开始。

诱导ipdb调试器1

import idpb; ipdb.set_trace()

ipdb>控制台2中输入 IPython :

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 时)。

于 2019-07-10T19:04:11.900 回答
4

你试过这个技巧吗?

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

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

那么你可以使用

debug_here()

每当你想设置断点

于 2015-07-26T01:32:22.703 回答
4

该问题的正确、简单、酷、准确的答案是使用带有 -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
于 2018-02-20T04:51:04.673 回答
3

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

于 2013-06-01T03:42:56.610 回答
2

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

于 2015-10-21T21:55:30.340 回答
2

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

然而,除非您设置另一个断点,否则单步执行似乎(还没有?)效果很好(即在一行中停止然后进入另一个函数)。

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

于 2016-08-14T14:49:57.620 回答
2

从 python 3.2 开始,你有了interact命令,它可以让你访问完整的 python/ipython 命令空间。

于 2018-01-01T19:53:14.013 回答
1

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

用 python-mode.el、Mx ipython RET 等检查。

于 2013-06-01T07:52:38.423 回答
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 中运行命令?

于 2020-05-23T21:14:37.580 回答