根据我的阅读,有两种方法可以在 Python 中调试代码:
使用传统的调试器,例如 pdb 或 ipdb。这支持诸如 c 表示继续、n 表示步进、s 表示步进等命令),但您无法直接访问 IPython shell,这对于对象检查非常有用。
通过在代码中嵌入 IPython shell 来使用 IPython。您可以从 IPython 导入嵌入,然后在代码中使用 embed()。当您的程序/脚本遇到 embed() 语句时,您将被放入 IPython shell。这允许使用所有 IPython 好东西来全面检查对象和测试 Python 代码。但是,当使用 embed() 时,您不能再使用方便的键盘快捷键逐步完成代码。
有什么方法可以结合两全其美? IE
能够使用方便的 pdb/ipdb 键盘快捷键逐步完成您的代码。在任何这样的步骤(例如,在给定的语句上),都可以访问成熟的 IPython shell。
在 MATLAB 中进行 IPython 调试:
可以在 MATLAB 中找到此类“增强调试”的示例,其中用户始终可以完全访问 MATLAB 引擎/shell,并且她仍然可以逐步完成她的代码、定义条件断点等。我和其他用户讨论过的,这是人们从 MATLAB 迁移到 IPython 时最怀念的调试功能。
在 Emacs 和其他编辑器中进行 IPython 调试:
我不想让这个问题太具体,但我主要在 Emacs 中工作,所以我想知道是否有任何方法可以将这个功能引入其中。理想情况下,Emacs(或编辑器)将允许程序员在代码的任何位置设置断点,并与解释器或调试器通信,使其停在您选择的位置,并在该位置引入完整的 IPython 解释器。
!
命令在断点处执行任何 python 命令
ipdb.set_trace() 怎么样?在您的代码中:
import ipdb; ipdb.set_trace()
更新:现在在 Python 3.7 中,我们可以编写 breakpoint()
。它的工作原理相同,但它也服从 PYTHONBREAKPOINT
环境变量。此功能来自 this PEP。
这允许对您的代码进行全面检查,并且您可以访问诸如 c
(继续)、n
(执行下一行)、s
(进入该方法)等命令。
请参阅 ipdb repo 和 a list of commands。 IPython 现在称为(编辑:部分)Jupyter。
ps:请注意,ipdb 命令优先于 python 代码。因此,要编写 list(foo)
,您需要 print(list(foo))
或 !list(foo)
。
此外,如果您喜欢 ipython 提示符(它的 emacs 和 vim 模式、历史记录、完成...),您的项目很容易获得相同的提示,因为它基于 python prompt toolkit。
您可以使用 IPython 的 %pdb
magic。只需在 IPython 中调用 %pdb
,当发生错误时,您会自动转到 ipdb
。虽然您没有立即进行步进,但您在 ipdb
之后。
这使得调试单个函数变得容易,因为您只需使用 %load
加载文件,然后运行函数。您可以在正确的位置使用 assert
强制出错。
%pdb
是一个线魔法。将其称为 %pdb on
、%pdb 1
、%pdb off
或 %pdb 0
。如果在没有参数的情况下调用它,它将作为切换。
ipython --pdb file.py -- args
启动程序,并在出现异常时将其丢弃到 ipdb。可能值得添加到答案中。
(2016 年 5 月 28 日更新)在 Emacs 中使用 RealGUD
对于 Emacs 中的任何人,this thread 展示了如何使用
Emacs 中一个新的重要调试器,称为 RealGUD,它可以与任何调试器(包括 ipdb)一起运行。 Emacs 包是end-mode。
这两个包的组合是非常强大的,它允许一个人准确地重新创建 OP 中描述的行为并做更多的事情。
有关 RealGUD for ipdb 的 the wiki article 的更多信息。
原答案:
在尝试了许多不同的 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)
:
通过运行从 Unix shell 调用此函数的 Python 程序或直接从 IPython 调用它
无论我如何调用它,解释器都会停在 ipsh()
行。完成后,您可以执行 Ctrl-D
,Python 将继续执行(使用您所做的任何变量更新)。请注意,如果您从常规 IPython IPython shell(上面的案例 2)运行代码,新的 IPython shell 将 嵌套 在您调用它的那个中,这非常好,但是很高兴知道。不管怎样,一旦解释器停在 ipsh
的位置,我就可以检查 a
的值(即 2
),查看定义了哪些函数和对象等。
问题:
上面的解决方案可用于让 Python 在代码中的任何位置停止,然后将您放入成熟的 IPython 解释器。不幸的是,一旦调用脚本,它就不允许您添加或删除断点,这非常令人沮丧。在我看来,这是阻止 IPython 成为 Python 的出色调试工具的唯一原因。
你现在能做的最好的事情:
一种解决方法是将 ipsh()
先验放置在您希望 Python 解释器启动 IPython shell 的不同位置(即 breakpoint
)。然后,您可以使用 Ctrl-D
在不同的预定义硬编码“断点”之间“跳转”,这将退出当前嵌入式 IPython shell,并在解释器下一次调用 ipsh()
时再次停止。
如果您走这条路,退出“调试模式”并忽略所有后续断点的一种方法是使用 ipshell.dummy_mode = True
,这将使 Python 忽略我们在上面创建的 ipshell
对象的任何后续实例化。
import inspect
。不过,命令行自定义对我来说似乎被打破了。
import ipdb; ipdb.set_trace()
?也许我错过了一些东西,但我没有看到你更复杂的方法的优势。
您可以从 pudb 启动 IPython 会话,然后根据需要返回调试会话。
顺便说一句,ipdb 在幕后使用 IPython,您实际上可以使用 IPython 功能,例如 TAB 完成和魔术命令(以 %
开头的命令)。如果您对 ipdb 没问题,您可以使用 %run
和 %debug
等命令从 IPython 启动它。 ipdb 会话实际上比普通的 IPython 会话更好,因为您可以在堆栈跟踪等中上下移动。ipdb 中缺少什么用于“对象检查”?
此外,与 Emacs >= 24.3 捆绑的 python.el 具有很好的 ipdb 支持。
M-x ansi-term
缓冲区中启动 IPython,然后使用 isend-mode 将源缓冲区绑定到 IPython 缓冲区,以便我可以使用键盘快捷键将代码发送到 IPython 解释器,该快捷键会自动发送 %paste
魔法到 IPython 缓冲区。这让我可以在 IPython 中快速测试区域。我总是使用 run
从这个 IPython shell 运行我的程序并使用 embed()
停止。
python.el
启动 ipdb
调试会话并将 send-region
等内容添加到相应的 shell?一般来说,我在哪里可以找到有关此的更多信息?
%run
或 %debug
。我猜 import ipdb; ipdb.set_trace()
也可以。我认为您不能将多行发送到 ipdb。这是 ipdb 的限制。但可能 %paste
技巧有效。您可能希望向 IPython 开发人员发送功能请求。
看起来像@gaborous 的回答 is deprecated 中的方法。
新方法似乎是:
from IPython.core import debugger
debug = debugger.Pdb().set_trace
def buggy_method():
debug()
ipdb
语句?
前缀“!”您在 pdb 中键入的命令的符号似乎与在 IPython shell 中执行某些操作具有相同的效果。这适用于访问某个函数的帮助,甚至是变量名。也许这会在一定程度上帮助你。例如,
ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)
但是 !help(numpy.transpose) 将为您提供有关 numpy.transpose 的预期帮助页面。同样对于变量名称,假设您有一个变量 l,在 pdb 中键入“l”会列出代码,但 !l 会打印 l 的值。
您可以IPython
从内部 ipdb
开始。
诱导 ipdb
调试器1:
import idpb; ipdb.set_trace()
在 ipdb>
控制台2 中从内部输入 IPython:
from IPython import embed; embed()
从 IPython
中返回到 ipdb>
控制台:
exit
如果你有幸使用 Emacs,事情会变得更加方便。
这需要使用 M-x shell
。使用 yasnippet
和 bm,定义以下代码段。这会将编辑器中的文本 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
within your .pdbrc
file 节省一些输入:
try:
from IPython import embed
except:
pass
这允许您从 ipdb
中简单地调用 embed()
(当然,只有在安装了 IPython 时)。
你试过 this tip 吗?
或者更好的是,使用 ipython,然后调用: from IPython.Debugger import Tracer; debug_here = Tracer() 那么只要你想设置断点就可以使用 debug_here()
ModuleNotFoundError: No module named 'IPython.Debugger'
该问题的正确、简单、酷、准确的答案是使用带有 -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
%save /tmp/foo.py x-y
我想要运行和调试的代码,然后 %run -d /tmp/foo.py
在任何我喜欢的地方设置断点。很好的答案!
一种选择是使用像 Spyder 这样的 IDE,它应该允许您在调试时与代码交互(实际上是使用 IPython 控制台)。事实上,Spyder 非常类似于 MATLAB,我认为这是故意的。这包括变量检查器、变量编辑、对文档的内置访问等。
如果您在 embed() 控制台中键入 exit(),代码将继续并转到下一个 embed() 行。
Pyzo IDE 具有与 OP 要求类似的功能。您不必在调试模式下启动。与 MATLAB 类似,命令在 shell 中执行。当您在某个源代码行中设置断点时,IDE 会在那里停止执行,您也可以调试和发出常规 IPython 命令。
然而,除非您设置另一个断点,否则单步执行似乎(还没有?)效果很好(即在一行中停止然后进入另一个函数)。
不过,来自 MATLAB,这似乎是我找到的最佳解决方案。
从 python 3.2 开始,您拥有 interact
命令,可让您访问完整的 python/ipython 命令空间。
从 Emacs 的 IPython-shell 内部运行并通过 pdb.set_trace() 设置断点应该可以工作。
用 python-mode.el、Mx ipython RET 等检查。
开发新代码
在 IPython 中调试
使用 Jupyter/IPython 单元执行来加速实验迭代使用 %%debug 进行单步执行
单元格示例:
%%debug
...: for n in range(4):
...: n>2
调试现有代码
IPython里面调试
调试损坏的单元测试:pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb 在测试用例之外调试:breakpoint()、python -m ipdb 等 IPython.embed() 用于完整的 IPython 功能在调试器中需要的地方
对 Python 的思考
我同意 OP 的观点,即 MATLAB 做得很好 Python 仍然没有而且确实应该做的很多事情,因为该语言中的几乎所有内容都倾向于开发速度而不是生产速度。也许有一天我会为 CPython 贡献比琐碎的错误修复更多的东西。
https://github.com/ipython/ipython/commit/f042f3fea7560afcb518a1940daa46a72fbcfa68
另请参阅Is it possible to run commands in IPython with debugging?
ipdb
使用RealGUD
和isend-mode
来完全按照 OP 的要求进行操作。breakpoint()
很棒。在 PyCharm 中,它甚至会将您放入 PyCharm 调试器中。这也是从粘贴到控制台的函数中进入 PyCharm 调试器的快速方法。PYTHONBREAKPOINT=IPython.embed
然后breakpoint()
将完成这项工作