如何在Traceback后检查变量?
我的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 个回答
我通常会用代码模块来处理这类问题。尽量早一点捕捉到异常,然后把所有信息都放到一个互动控制台里。
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环境变量指向你修改过的库版本,而把原来的库留着不动。
你可以使用一些Python调试工具,就像其他回答中提到的那样,或者试试我的diagnostics库,它可以为你保存更详细的错误追踪信息,并以HTML文件的形式呈现 :) 只需在cheaters.py
文件旁边创建一个名为log
的文件夹,然后通过下面的代码启用异常钩子:
from diagnostics import exception_hook
exception_hook.enable()
一个简单的替代方法是使用 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
如果你想在出现错误时才进入调试器,可以定义一个自定义的异常处理函数:
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
的那个位置。
根据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'
太棒了!