如何在eval()或exec()中以编程方式退出pdb而不显示输出

4 投票
1 回答
1369 浏览
提问于 2025-04-16 05:40

在我的Python代码中,有这么一行:

try:
    result = eval(command, self.globals, self.locals)
except SyntaxError:
    exec(command, self.globals, self.locals)

这个command变量可以是任何字符串。因此,Python调试器pdb可能会在eval/exec中启动,并且在eval/exec返回时仍然处于活动状态。我想要做的是确保在从eval/exec返回时,正常的程序执行能够继续。给你一个大概念,这就是我想要的行为:

try:
    result = eval(command, self.globals, self.locals)
    try: self.globals['pdb'].run('continue')
    except: pass
except SyntaxError:
    exec(command, self.globals, self.locals)
    try: self.globals['pdb'].run('continue')
    except: pass

但是,try这一行在执行之前就已经在调试器中显示出来了,我并不想让调试器显示我的代码。而且,这样也并没有真正解决问题……我重复代码的原因是为了减少我代码中的调试工作,否则我可以在except块之后再处理。

那么我该怎么做呢?

顺便提一下:

如果你尝试在IPython或bpython解释器中输入以下几行,你会发现它们有同样的问题,你可以进入它们的代码。

import pdb
pdb.set_trace()
next

但是如果你在标准的CPython解释器中这样做,你会返回到Python提示符。这显然是因为前两者是用Python实现的,而最后一个不是。但我希望即使所有代码都是Python时,也能得到相同的行为。

1 个回答

4

虽然我有点担心你在执行一个你无法控制的字符串,但我假设你已经考虑过这个问题。

我觉得最简单的方法是让调试工具(pdb)在每一步检查一下当前的调用状态,并在你回到想要的层级时自动继续。你可以通过简单的修补来实现这一点。在下面的代码中,我把它简化成了一个简单的执行,因为你真正想要的只是让调试工具在返回到特定函数时自动继续。在你不想追踪的函数里调用 Pdb().resume_here()。需要注意的是,这个继续是全局的,只有一个继续点,但我相信如果你想的话,可以修改这个。

如果你运行这段代码,你会在函数 foo() 里进入调试器,然后你可以逐步调试,但一旦你返回到 bar(),代码就会自动继续执行。

例如:

import sys
from pdb import Pdb

def trace_dispatch(self, frame, event, arg):
    if frame is self._resume_frame:
        self.set_continue()
        return
    return self._original_trace_dispatch(frame, event, arg)

def resume_here(self):
    Pdb._resume_frame = sys._getframe().f_back

# hotfix Pdb
Pdb._original_trace_dispatch = Pdb.trace_dispatch
Pdb.trace_dispatch = trace_dispatch
Pdb.resume_here = resume_here
Pdb._resume_frame = None

def foo():
    import pdb
    pdb.set_trace()
    print("tracing...")
    for i in range(3):
        print(i)

def bar():
    Pdb().resume_here()
    exec("foo();print('done')")
    print("returning")

bar()

撰写回答