【问题标题】:How can I extract local variables from a stack trace?如何从堆栈跟踪中提取局部变量?
【发布时间】:2016-04-12 16:54:20
【问题描述】:

假设我有一个引发意外异常的函数,所以我将它包装在 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,即使堆栈已展开?

【问题讨论】:

    标签: python pdb ipdb


    【解决方案1】:

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

    手动提取值:

    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
    

    【讨论】:

      【解决方案2】:

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

      with ipdb.launch_ipdb_on_exception():
          main()
      

      这是一个使用 ipdb.post_mortem 的易于使用的包装器。

      【讨论】:

        【解决方案3】:

        根据您的需要,有 2 种通用最佳做法。

        用最少的代码编辑打印变量

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

        或者试试tbvaccine,或者better-exceptions,或者any other package

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

        使用inspect模块

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

        调试器呢?

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-10
          • 1970-01-01
          相关资源
          最近更新 更多