如何在变量中保存traceback / sys.exc_info()值?

时间:2011-11-23 07:00:31

标签: python exception-handling

我想将错误名称和回溯详细信息保存到变量中。这是我的尝试。

import sys
try:
    try:
        print x
    except Exception, ex:
        raise NameError
except Exception, er:
    print "0", sys.exc_info()[0]
    print "1", sys.exc_info()[1]
    print "2", sys.exc_info()[2]

输出:

0 <type 'exceptions.NameError'>
1 
2 <traceback object at 0xbd5fc8>

期望输出:

0 NameError
1
2 Traceback (most recent call last):
  File "exception.py", line 6, in <module>
    raise NameError

P.S。我知道这可以使用traceback模块轻松完成,但我想知道sys.exc_info()[2]对象的用法。

5 个答案:

答案 0 :(得分:137)

我就是这样做的:

>>> import traceback
>>> try:
...   int('k')
... except:
...   var = traceback.format_exc()
... 
>>> print var
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'k'

你应该看一下traceback documentation,因为你可能会发现更合适的方法,这取决于你之后如何处理你的变量...

答案 1 :(得分:18)

sys.exc_info()返回一个包含三个值(类型,值,跟踪)的元组。

  1. 此类型获取正在处理的异常的异常类型
  2. value是传递给异常类
  3. 的构造函数的参数
  4. traceback包含堆栈信息,例如发生异常的位置等。
  5. 例如,在以下程序中

    try:
    
        a = 1/0
    
    except Exception,e:
    
        exc_tuple = sys.exc_info()
    

    现在如果我们打印元组,那么值就是这个。

    1. exc_tuple [0]值将为“ ZeroDivisionError
    2. exc_tuple [1]值将为“整数除法或模数为零”(作为参数传递给异常类的字符串)
    3. exc_tuple [2]值将是“引用对象(某些内存地址)
    4. 只需以字符串格式打印异常,即可获取上述详细信息。

      print str(e)
      

答案 2 :(得分:16)

如果您想方便地访问模块和功能名称以及行号,请使用traceback.extract_stack()

如果您只想要一个看起来像''.join(traceback.format_stack())输出的字符串,请使用traceback.print_stack()

请注意,即使使用''.join(),您也会获得多行字符串,因为format_stack()的元素包含\n。见下面的输出。

请记住import traceback

这是traceback.extract_stack()的输出。添加格式以提高可读性。

>>> traceback.extract_stack()
[
   ('<string>', 1, '<module>', None),
   ('C:\\Python\\lib\\idlelib\\run.py', 126, 'main', 'ret = method(*args, **kwargs)'),
   ('C:\\Python\\lib\\idlelib\\run.py', 353, 'runcode', 'exec(code, self.locals)'),
   ('<pyshell#1>', 1, '<module>', None)
]

这是''.join(traceback.format_stack())的输出。添加格式以提高可读性。

>>> ''.join(traceback.format_stack())
'  File "<string>", line 1, in <module>\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 126, in main\n
       ret = method(*args, **kwargs)\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 353, in runcode\n
       exec(code, self.locals)\n  File "<pyshell#2>", line 1, in <module>\n'

答案 3 :(得分:1)

从异常处理程序中取出异常对象或回溯对象时要小心,因为这会导致循环引用,并且gc.collect()将无法收集。在ipython / jupyter笔记本环境中,这似乎是一个特殊的问题,在该环境中无法在正确的时间清除回溯对象,甚至在gc.collect()部分中对finally的显式调用也不起作用。这就是一个很大的问题,如果您有一些大的对象由于这个原因而无法回收它们的内存(例如,CUDA内存不足的异常,而该解决方案不需要完整的内核重新启动才能恢复)。

通常,如果要保存回溯对象,则需要从对locals()的引用中清除它,如下所示:

import sys, traceback, gc
type, val, tb = None, None, None
try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
# some cleanup code
gc.collect()
# and then use the tb:
if tb:
    raise type(val).with_traceback(tb)

对于jupyter笔记本而言,您至少必须在异常处理程序中执行此操作:

try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
    raise type(val).with_traceback(tb)
finally:
    # cleanup code in here
    gc.collect()

经过python 3.7测试。

p.s。 ipython或jupyter notebook env的问题在于它具有%tb魔术,可以保存回溯并在以后的任何时候使用。结果,参与回溯的所有帧中的任何locals()都不会释放,直到笔记本退出,否则另一个异常将覆盖以前存储的回溯。这是很成问题的。它不应存储没有清洗其框架的回溯。修复提交的here

答案 4 :(得分:0)

该对象可用作Exception.with_traceback()函数中的参数:

except Exception as e:
    tb = sys.exc_info()
    print(e.with_traceback(tb[2]))