如何在Traceback之后检查变量?

19

我的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()

1
你可能需要pdb - aruisdante
1
Python有locals函数,这可能会很有帮助。 - aisbaa
@ColonelPanic,你知道在IDLE中有一个Python调试器,可以用它来逐步执行所有变量声明(本地或全局),同时查看错误和异常吗? - Malik Brahimi
我还发现安装像flake8这样的代码检查工具可以避免一些错误。 - kristianp
8个回答

13

如果只有异常时才想要进入调试器,您可以定义一个自定义 excepthook

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的帧。


别忘了提到pdb++。要安装它:pip安装pdbpp。它比普通的pdb好多了 :) - Markon
1
这意味着我知道错误将在哪里发生。我只想运行脚本,仅当有Traceback时,检查变量。 - Colonel Panic
在这种情况下,请检查 pm() 和 post_mortem() 函数,它们允许您在故障后检查变量。 - artemdevel

8
根据Python文档https://docs.python.org/3.4/library/pdb.htmlpdb.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'

哇嘿!


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

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 REPL,触发的异常将在本地变量['e']中,您将可以访问调用try块的范围。如果异常是在某个其他库中引发的,您可以使用此异常处理程序修改其他库的代码,使用PYTHONPATH环境变量指向已修改的库的版本,并保留原始版本。


0

我强烈推荐在这些情况下使用ipython和ipdb。

在ipython shell中,您可以输入以下命令:

run example.py

当未处理的异常将您返回到IPython shell时,您需要输入

%debug

这将使您进入ipdb,在抛出未处理异常的代码行上。然后,您可以通过对其进行评估来检查变量,就像在正常的Python shell中一样。

我的答案与已经被接受的Colonel Panic的答案类似。这里的主要优势是ipython和ipdb。出于各种原因,我更喜欢使用这些工具,其中最突出的是它们可以自动完成变量名称。一旦您习惯了自动完成,很难再离开它,因为它可以让您工作得更快。


0

使用pdb在调试每一行代码

import pdb; pdb.set_trace()

这将在每行之后给你一个换行符,并且你可以追踪你的代码

def main():
    import pdb; pdb.set_trace()
    message = "hello world"
    message[20]

main()

当调用main()函数时,这个函数将开始工作,您可以跟踪消息,调试它

您的终端将看起来像这样

> /home/admin/dxservices/example.py(3)main()
-> message = "hello world"
(Pdb) 
> /home/admin/dxservices/example.py(4)main()
-> message[20]
(Pdb) n
 IndexError: 'string index out of range'  
 > /home/admin/dxservices/example.py(4)main()
-> message[20]

0

您可以使用其他回答中提到的一些 Python调试器,或者尝试使用我的诊断库,它会为您存储更多详细的回溯信息,并生成HTML文件:)只需在cheaters.py文件旁创建名为log的目录,并通过以下代码启用异常挂钩:

from diagnostics import exception_hook
exception_hook.enable()

0

这取决于哪种方式最适合您。由于问题留下了一些解释的空间,这里提供一些替代方案。

以编程方式访问变量以在代码中使用

使用inspect模块

except ... as ...:
    x = inspect.trace()[-1][0].f_locals['x']

为了调试目的,传递某个变量值,例如记录日志

将该值添加到异常消息中。

raise ValueError("oops, x={}".format(x))

但是如果有外部代码引发异常,你就无法这样做。而且这需要时间,也不太实用。

只需打印导致异常的变量

尝试使用 traceback-with-variablespip install traceback-with-variables),这是它的明信片

enter image description here

或者使用tbvaccine,或者better-exceptions任何其他包

还有一种选择是使用调试器,例如python -m pdb。这允许仅在调试运行时打印信息,并且对生产没有帮助,而且需要更多时间。因此,我认为这个选项是最不可取的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接