如何在Traceback后检查变量?

19 投票
8 回答
2849 浏览
提问于 2025-04-17 22:33

我的Python脚本崩溃了。为了调试它,我在交互模式下运行了它,命令是 python -i example.py

Traceback (most recent call last):
  File "example.py", line 5, in <module>
    main()
  File "example.py", line 3, in main
    message[20]
IndexError: string index out of range

此时,我想查看变量 message 的值。我尝试了

>>> message
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'message' is not defined

可惜的是,message 这个变量不在当前的作用域里(不过 main 在)。这让我很沮丧。我该如何查看这个变量呢?有没有更实用的 python -i 版本,可以在崩溃时保留当前作用域的内容,而不是只显示最顶层的内容?


上面提到的 example.py 使用的代码。无需多说,这只是一个简化的例子。

def main():
    message = "hello world"
    message[20]

main()

8 个回答

0

我通常会用代码模块来处理这类问题。尽量早一点捕捉到异常,然后把所有信息都放到一个互动控制台里。

try:
    # something that might throw an error
except Exception as e:
    import code
    l={}
    l['global_vars'] = globals()
    l['local_vars'] = locals()
    code.InteractiveConsole(locals=l).interact()

这样会在异常处理器里面启动一个Python的交互式环境,抛出的异常会保存在local_vars['e']里,你可以访问到调用try块时的作用域。如果异常是在其他库里面抛出的,你可以用这个异常处理器来修改那个库的代码,使用PYTHONPATH环境变量指向你修改过的库版本,而把原来的库留着不动。

0

你可以使用一些Python调试工具,就像其他回答中提到的那样,或者试试我的diagnostics库,它可以为你保存更详细的错误追踪信息,并以HTML文件的形式呈现 :) 只需在cheaters.py文件旁边创建一个名为log的文件夹,然后通过下面的代码启用异常钩子:

from diagnostics import exception_hook
exception_hook.enable()
1

一个简单的替代方法是使用 cgitb 模块。

import cgitb; cgitb.enable(format='text')


def main():
    message = "hello world"
    message[20]

main()

现在,错误追踪信息会直接显示消息的值。

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 /home/anand/projects/python/ptb/tes.py in <module>()
    4 def main():
    5     message = "hello world"
    6     message[20]
    7 
    8 main()
main = <function main>

 /home/anand/projects/python/ptb/tes.py in main()
    4 def main():
    5     message = "hello world"
    6     message[20]
    7 
    8 main()
message = 'hello world'
IndexError: string index out of range
13

如果你想在出现错误时才进入调试器,可以定义一个自定义的异常处理函数

import sys
def excepthook(type_, value, tb):
    import traceback
    import pdb
    traceback.print_exception(type_, value, tb)
    pdb.post_mortem(tb)
sys.excepthook = excepthook

def main():
    message = "hello world"
    message[20]

main()

运行这个脚本时,如果有错误,就会进入调试器pdb,并停在引发错误的地方:

% script.py
Traceback (most recent call last):
  File "/home/unutbu/pybin/script.py", line 16, in <module>
    main()
  File "/home/unutbu/pybin/script.py", line 14, in main
    message[20]
IndexError: string index out of range
> /home/unutbu/pybin/script.py(14)main()
-> message[20]
(Pdb) p message
'hello world'
(Pdb) p message[20]
*** IndexError: IndexError('string index out of range',)
(Pdb) p len(message)
11

如果觉得定义这个异常处理函数代码太多,可以把它放在一个工具模块里,比如叫utils_debug.py:

import sys
def enable_pdb():
    def excepthook(type_, value, tb):
        import traceback
        import pdb
        traceback.print_exception(type_, value, tb)
        pdb.post_mortem(tb)
    sys.excepthook = excepthook

然后你只需要在你的

import utils_debug as UDBG
UDBG.enable_pdb()

script.py中添加一行代码。


或者,如果你在使用IPython,可以使用%pdb魔法函数(当出现错误时,它会让你进入ipdb调试器)。


不太清楚为什么在pdb提示符下查看size会出现名称错误(NameError)。如果能提供一个可运行的例子会很有帮助。你可以尝试使用bt(回溯)来查看当前的调用栈。如果size是在pdb当前所处的不同位置定义的,你可以使用u(向上)命令,回到定义size的那个位置。

9

根据Python的官方文档 https://docs.python.org/3.4/library/pdb.html

pdb.py 也可以作为一个脚本来调试其他脚本。比如说,你可以用 python -m pdb myscript.py 来运行。当作为脚本运行时,如果被调试的程序异常退出,pdb会自动进入事后调试模式。

不过,这个说法并不完全准确。实际上,它会在第一行就开始调试。

$ python -m pdb example.py
> example.py(1)<module>()
-> def main():

但是如果你输入 c,它就会继续运行到崩溃的地方。

(Pdb) c
Traceback (most recent call last):
  File "C:\Python34\lib\pdb.py", line 1661, in main
    pdb._runscript(mainpyfile)
  File "C:\Python34\lib\pdb.py", line 1542, in _runscript
    self.run(statement)
  File "C:\Python34\lib\bdb.py", line 431, in run
    exec(cmd, globals, locals)
  File "<string>", line 1, in <module>
  File "example.py", line 1, in <module>
    def main():
  File "example.py", line 3, in main
    message[20]
IndexError: string index out of range
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> example.py(3)main()

到那时,你可以输入 message 来查看变量的值。

-> message[20]
(Pdb) message
'hello world'

太棒了!

撰写回答