(2016年5月28日更新) 在Emacs中使用RealGUD
针对任何在Emacs中的用户,这个帖子展示了如何使用RealGUD完成原帖中提到的所有功能(甚至更多),这是一个可以与任何调试器(包括ipdb
)一起使用的重要的新调试器。
- Emacs包
isend-mode
这两个软件包的组合极其强大,可精确地重现原帖中描述的行为,并且可以做得更多。
有关RealGUD进行ipdb调试的更多信息,请参阅wiki文章。
原来的回答:
尝试了许多不同的Python调试方法(包括本帖中提到的所有方法)后,我发现使用嵌入式shell的IPython调试Python是我的首选之一。
定义自定义嵌入式IPython shell:
将以下内容添加到脚本中的PYTHONPATH
,以便使方法ipsh()
可用。
import inspect
from IPython.terminal.embed import InteractiveShellEmbed
from IPython.config.loader import Config
cfg = Config()
prompt_config = cfg.PromptManager
prompt_config.in_template = 'N.In <\\#>: '
prompt_config.in2_template = ' .\\D.: '
prompt_config.out_template = 'N.Out<\\#>: '
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'
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)
ipshell(msg,stack_depth=2)
那么,每当我想要调试代码时,我会将 ipsh()
放置在需要进行对象检查的位置。例如,假设我想要调试下面的my_function
:
如何使用:
def my_function(b):
a = b
ipsh()
a = 4
然后我以以下一种方式之一调用my_function(2)
:
- 通过从Unix shell运行调用此函数的Python程序
- 或直接从IPython中调用
无论我如何调用它,解释器都会在代码中出现ipsh()
代码行处停止。完成后,您可以执行Ctrl-D
,Python将恢复执行(包括您进行的任何变量更新)。请注意,如果您从常规IPython(上述情况2)运行代码,则新的IPython shell将嵌套在您调用它的shell内部,这是完全可以的,但最好要知道。无论哪种方式,一旦解释器停在ipsh()
位置,我都可以检查a
的值(应该是2
),查看定义的函数和对象等。
问题:
上述解决方案可用于使Python在代码中的任何位置停止,并让您进入一个功能完备的IPython解释器。不幸的是,它不允许您在调用脚本后添加或删除断点,这非常令人沮丧。在我看来,这是阻止IPython成为Python的重要调试工具的唯一原因。
目前可行的最佳解决方案:
一种解决方法是预先将ipsh()
放置在您希望Python解释器启动IPython shell(即断点)的不同位置。然后,您可以使用Ctrl-D
在不同的预定义硬编码的“断点”之间“跳转”,这将退出当前嵌入式IPython shell,并再次在解释器遇到下一个ipsh()
调用时停止。
如果您选择这条路线,一种退出“调试模式”并忽略所有后续断点的方法是使用ipshell.dummy_mode = True
,这将使Python忽略我们上面创建的ipshell
对象的任何后续实例化。
!
命令,可以在断点处执行任何Python命令。 - Dmitry Galchinsky