【问题标题】:Unable to access local variables interactively after Python errorPython 错误后无法交互访问局部变量
【发布时间】:2016-01-05 15:12:26
【问题描述】:

如果在交互 (-i) 模式下运行 python 脚本,以帮助调试,如何在发生错误时访问堆栈帧?

脚本

# pytest.py
def foo(x):
    assert False

foo(24601)

命令会话

D:\>python.exe -i pytest.py
Traceback (most recent call last):
  File "pytest.py", line 4, in <module>
    foo(3)
  File "pytest.py", line 2, in foo
    assert False
AssertionError
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

在这种情况下,如果断言失败,如何访问 x 的值?

【问题讨论】:

    标签: python interactive


    【解决方案1】:

    使用 python 调试器 - https://docs.python.org/2/library/pdb.html

    def foo(x):
        import pdb; pdb.set_trace()   #add this line to set a breakpoint
        assert False
    

    (你不需要它 -i 标志)

    【讨论】:

    • 有没有一种方法可以实现这一点,而不必将上述代码行添加到我要调试的每个函数中?
    【解决方案2】:

    http://code.activestate.com/recipes/52215-get-more-information-from-tracebacks/

    Python 调试器的答案听起来不错。但是,如果你想尝试“别的东西”。 我找到了这个东西 - 它看起来就像你正在寻找的答案。您可以使用 traceback 模块构建一个函数,在发生错误时逐帧打印出局部变量。从那里,我想你可以从日志中提取数据。

    如链接中的示例所述:

    import sys, traceback
    
    def print_exc_plus():
        """
        Print the usual traceback information, followed by a listing of all the
        local variables in each frame.
        """
        tb = sys.exc_info()[2]
        while 1:
            if not tb.tb_next:
                break
            tb = tb.tb_next
        stack = []
        f = tb.tb_frame
        while f:
            stack.append(f)
            f = f.f_back
        stack.reverse()
        traceback.print_exc()
        print "Locals by frame, innermost last"
        for frame in stack:
            print
            print "Frame %s in %s at line %s" % (frame.f_code.co_name,
                                                 frame.f_code.co_filename,
                                                 frame.f_lineno)
            for key, value in frame.f_locals.items():
                print "\t%20s = " % key,
                #We have to be careful not to cause a new error in our error
                #printer! Calling str() on an unknown object could cause an
                #error we don't want.
                try:
                    print value
                except:
                    print "<ERROR WHILE PRINTING VALUE>"
    
    
    if __name__ == '__main__':
        #A simplistic demonstration of the kind of problem this approach can help
        #with. Basically, we have a simple function which manipulates all the
        #strings in a list. The function doesn't do any error checking, so when
        #we pass a list which contains something other than strings, we get an
        #error. Figuring out what bad data caused the error is easier with our
        #new function.
    
        data = ["1", "2", 3, "4"] #Typo: We 'forget' the quotes on data[2]
        def pad4(seq):
            """
            Pad each string in seq with zeros, to four places. Note there
            is no reason to actually write this function, Python already
            does this sort of thing much better.
            Just an example.
            """
            return_value = []
            for thing in seq:
                return_value.append("0" * (4 - len(thing)) + thing)
            return return_value
    
        #First, show the information we get from a normal traceback.print_exc().
        try:
            pad4(data)
        except:
            traceback.print_exc()
        print
        print "----------------"
        print
    
        #Now with our new function. Note how easy it is to see the bad data that
        #caused the problem. The variable 'thing' has the value 3, so we know
        #that the TypeError we got was because of that. A quick look at the
        #value for 'data' shows us we simply forgot the quotes on that item.
        try:
            pad4(data)
        except:
            print_exc_plus()
    

    执行代码后,我得到了包含局部变量及其值的输出!

    TypeError: object of type 'int' has no len()
                    __name__ =  __main__
                        data =  ['1', '2', 3, '4']
                     __doc__ =  None
              print_exc_plus =  <function print_exc_plus at 0x7ffb8ee86668>
    
    Frame pad4 in /home/robert.sawicki/PycharmProjects/python_challenge/test.py at line 55
                       thing =  3
                return_value =  ['0001', '0002']
                         seq =  ['1', '2', 3, '4']
    

    【讨论】:

      【解决方案3】:

      您可以在交互模式下直接使用python调试器

      D:>python.exe -m pdb pytest.py

      使用

      【讨论】:

        【解决方案4】:

        您也可以在交互式 Python shell 中使用 pdb - 您只需将代码封装在适当的函数中:

        bruno@bigb:~/Work/playground$ cat pytest.py
        # pytest.py
        def foo(x):
            assert False
        
        def main():
            print foo(24601)
        
        
        if __name__ == "__main__":
            main()
        
        bruno@bigb:~/Work/playground$ python
        Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
        Type "help", "copyright", "credits" or "license" for more information.
        >>> import pytest
        >>> import pdb
        >>> pdb.runcall(pytest.main)
        > /home/bruno/Work/playground/pytest.py(6)main()
        -> print foo(24601)
        (Pdb) s
        --Call--
        > /home/bruno/Work/playground/pytest.py(2)foo()
        -> def foo(x):
        (Pdb) l
          1     # pytest.py
          2  -> def foo(x):
          3         assert False
          4     
          5     def main():
          6         print foo(24601)
          7     
          8     
          9     if __name__ == "__main__":
         10         main()
        [EOF]
        (Pdb) n
        > /home/bruno/Work/playground/pytest.py(3)foo()
        -> assert False
        (Pdb) !x
        24601
        (Pdb) c
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
          File "/usr/lib/python2.7/pdb.py", line 1248, in runcall
            return Pdb().runcall(*args, **kwds)
          File "/usr/lib/python2.7/bdb.py", line 436, in runcall
            res = func(*args, **kwds)
          File "pytest.py", line 6, in main
            print foo(24601)
          File "pytest.py", line 3, in foo
            assert False
        AssertionError
        >>> 
        

        【讨论】:

          猜你喜欢
          • 2020-07-30
          • 2012-10-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多