如何从堆栈跟踪中提取局部变量?

时间:2016-04-12 16:54:21

标签: python pdb ipdb

假设我有一个引发意外异常的函数,所以我将它包装在ipdb中:

def boom(x, y):
    try:
        x / y
    except Exception as e:
        import ipdb; ipdb.set_trace()

def main():
    x = 2
    y = 0
    boom(x, y)

if __name__ == '__main__':
    main()

我可以向上移动堆栈以找出x和y的值:

$ python crash.py 
> /tmp/crash.py(6)boom()
      5     except Exception as e:
----> 6         import ipdb; ipdb.set_trace()
      7 

ipdb> u
> /tmp/crash.py(11)main()
     10     y = 0
---> 11     boom(x, y)
     12 

ipdb> p y
0

但是,在调试时,我想把调试器放在最顶层:

def boom(x, y):
    x / y

def main():
    x = 2
    y = 0
    boom(x, y)

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        import ipdb; ipdb.set_trace()

我可以显示回溯,但是我无法查看名为的函数内的变量:

$ python crash.py 
> /tmp/crash.py(14)<module>()
     12         main()
     13     except Exception as e:
---> 14         import ipdb; ipdb.set_trace()

ipdb> !import traceback; traceback.print_exc(e)
Traceback (most recent call last):
  File "crash.py", line 12, in <module>
    main()
  File "crash.py", line 8, in main
    boom(x, y)
  File "crash.py", line 3, in boom
    x / y
ZeroDivisionError: integer division or modulo by zero
ipdb> d # I want to see what value x and y had!
*** Newest frame

异常发生时,异常对象显然仍然引用了堆栈。我可以在这里访问xy,即使堆栈已解开吗?

3 个答案:

答案 0 :(得分:3)

原来可以从traceback对象中提取变量。

手动提取值:

ipdb> !import sys
ipdb> !tb = sys.exc_info()[2]
ipdb> p tb.tb_next.tb_frame.f_locals
{'y': 0, 'x': 2}

更好的是,您可以使用异常在该堆栈上显式执行事后调试:

import sys

def boom(x, y):
    x / y

def main():
    x = 2
    y = 0
    boom(x, y)

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        # Most debuggers allow you to just do .post_mortem()
        # but see https://github.com/gotcha/ipdb/pull/94
        tb = sys.exc_info()[2]
        import ipdb; ipdb.post_mortem(tb)

让我们直接看到违规代码:

> /tmp/crash.py(4)boom()
      3 def boom(x, y):
----> 4     x / y
      5 

ipdb> p x
2

答案 1 :(得分:0)

您还可以使用上下文管理器

with ipdb.launch_ipdb_on_exception():
    main()

它是使用ipdb.post_mortem的易于使用的包装器。

答案 2 :(得分:0)

根据您的需求,有两种通用的最佳实践。

只需最少的代码编辑即可打印变量

看看一些相关的软件包。为了简单使用,您可以选择traceback-with-variablespip install traceback-with-variables),这是明信片

enter image description here

或者尝试tbvaccinebetter-exceptionsany other package

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

使用inspect模块

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

那调试器呢?

调试器用于逐步执行和断点。使用它检查异常原因确实很不方便,应该避免。您可以使用上述两个最佳实践来自动化调试会话。