如何从堆栈跟踪中提取局部变量?
How can I extract local variables from a stack trace?
假设我有一个引发意外异常的函数,所以我将它包装在 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
当异常发生时,异常对象显然仍然有对堆栈的引用。我可以在此处访问 x
和 y
,即使堆栈已展开?
原来可以从回溯对象中提取变量。
要手动提取值:
ipdb> !import sys
ipdb> !tb = sys.exc_info()[2]
ipdb> p tb.tb_next.tb_frame.f_locals
{'y': 0, 'x': 2}
更好的是,您可以使用异常在该堆栈上显式执行 post-mortem 调试:
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
您也可以使用上下文管理器
with ipdb.launch_ipdb_on_exception():
main()
这是一个易于使用的包装器,使用 ipdb.post_mortem
。
根据您的需要,有 2 个通用的最佳实践。
只需修改最少的代码即可打印变量
看看一些相关的包。为了简单使用,您可以选择 traceback-with-variables (pip install traceback-with-variables
),这是明信片
或尝试tbvaccine, or better-exceptions, or any other package
以编程方式访问变量以在您的代码中使用它们
使用inspect
模块
except ... as ...:
x = inspect.trace()[-1][0].f_locals['x']
调试器呢?
调试器用于 step-by-step 执行和断点。用它来检查异常原因确实很不方便,应该避免。您可以使用两个提到的最佳实践来自动化调试会话。
假设我有一个引发意外异常的函数,所以我将它包装在 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
当异常发生时,异常对象显然仍然有对堆栈的引用。我可以在此处访问 x
和 y
,即使堆栈已展开?
原来可以从回溯对象中提取变量。
要手动提取值:
ipdb> !import sys
ipdb> !tb = sys.exc_info()[2]
ipdb> p tb.tb_next.tb_frame.f_locals
{'y': 0, 'x': 2}
更好的是,您可以使用异常在该堆栈上显式执行 post-mortem 调试:
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
您也可以使用上下文管理器
with ipdb.launch_ipdb_on_exception():
main()
这是一个易于使用的包装器,使用 ipdb.post_mortem
。
根据您的需要,有 2 个通用的最佳实践。
只需修改最少的代码即可打印变量
看看一些相关的包。为了简单使用,您可以选择 traceback-with-variables (pip install traceback-with-variables
),这是明信片
或尝试tbvaccine, or better-exceptions, or any other package
以编程方式访问变量以在您的代码中使用它们
使用inspect
模块
except ... as ...:
x = inspect.trace()[-1][0].f_locals['x']
调试器呢?
调试器用于 step-by-step 执行和断点。用它来检查异常原因确实很不方便,应该避免。您可以使用两个提到的最佳实践来自动化调试会话。