Python 从异常对象提取回溯信息

Python 从异常对象提取回溯信息,python,debugging,exception-handling,Python,Debugging,Exception Handling,给定一个异常对象(来源未知),是否有方法获取其回溯?我有这样的代码: def stuff(): try: ..... return useful except Exception as e: return e result = stuff() if isinstance(result, Exception): result.traceback <-- How? def stuff(): 尝试: ..... 返回有用的 例外情

给定一个异常对象(来源未知),是否有方法获取其回溯?我有这样的代码:

def stuff():
   try:
       .....
       return useful
   except Exception as e:
       return e

result = stuff()
if isinstance(result, Exception):
    result.traceback <-- How?
def stuff():
尝试:
.....
返回有用的
例外情况除外,如e:
返回e
结果=stuff()
如果存在(结果、异常):

result.traceback这个问题的答案取决于您使用的Python版本

在Python 3中 很简单:异常带有一个包含回溯的
\uuuuuuuuuuuuuu
属性。此属性也是可写的,可以使用异常的
with_traceback
方法方便地设置:

raise Exception("foo occurred").with_traceback(tracebackobj)
这些特性作为文档的一部分进行了最低限度的描述

答案的这一部分应该归功于维克托,他是谁。我把它包括在这里只是因为这个答案停留在顶部,而Python 3正变得越来越普遍

在Python 2中 复杂得令人烦恼。回溯的问题在于,它们引用了堆栈帧,堆栈帧引用了回溯,而回溯引用了堆栈帧。这会导致垃圾收集器出现问题。(感谢您首先指出这一点。)

解决这个问题的好方法是在离开
except
子句后进行手术,这是Python3所做的。Python2的解决方案要难看得多:提供了一个特殊函数,它只在
子句中起作用,而不在
子句中起作用。它返回一个元组,其中包含异常、异常类型以及当前正在处理的任何异常的回溯

因此,如果您在
except
子句中,可以使用
sys.exc_info()
的输出以及模块来执行各种有用的操作:

>>> import sys, traceback
>>> def raise_exception():
...     try:
...         raise Exception
...     except Exception:
...         ex_type, ex, tb = sys.exc_info()
...         traceback.print_tb(tb)
...     finally:
...         del tb
... 
>>> raise_exception()
  File "<stdin>", line 3, in raise_exception
导入系统,回溯 >>>def raise_异常(): ... 尝试: ... 引发异常 ... 除例外情况外: ... ex_type,ex,tb=sys.exc_info() ... 回溯打印tb(tb) ... 最后: ... 德尔结核 ... >>>引发异常() 文件“”,第3行,在raise\u异常中
但是,正如您的编辑所指出的,您正在尝试在异常已经处理之后,获取如果您的异常未被处理,则会打印的回溯。这是一个更难的问题。不幸的是,
sys.exc_info
在未处理异常时返回
(无,无,无)
。其他相关的
sys
属性也没有帮助
sys.exc_traceback
在未处理异常时已弃用且未定义
sys.last_traceback
看起来很完美,但它似乎只在交互式会话中定义

如果可以控制异常的引发方式,则可以使用和来存储一些信息。但我不完全确定这将如何运作


说实话,捕获并返回异常是一件不寻常的事情。这可能是您无论如何都需要重构的一个迹象。

有一个很好的理由,回溯不存储在异常中;因为回溯持有对其堆栈局部变量的引用,这将导致循环引用和(临时)内存泄漏,直到循环GC启动。(这就是你应该这样做的原因。)

我能想到的唯一一件事就是让你去monkeypatch
填充
的全局变量,这样当它认为它在捕获
异常
时,它实际上是在捕获一个特殊类型,异常作为调用方传播给你:

module_containing_stuff.Exception = type("BogusException", (Exception,), {})
try:
    stuff()
except Exception:
    import sys
    print sys.exc_info()
由于内置类有一个
\uuuu traceback\uuu
属性,该属性包含一个
回溯对象(使用Python 3.2.3):

>>试试:
...     引发异常()
... 例外情况除外,如e:
...     tb=e.\u回溯__
...
>>>结核病
问题是,过了一段时间后,我只找到了几篇文章,但没有一篇文章描述了您是否应该(或为什么不)使用
\uuuuuuu traceback\uuuu

然而,报告说:

当引发异常并将其作为
\uu traceback\uuu
属性附加到该异常时,通常会自动创建回溯对象,该属性是可写的


因此,我假设它是要被使用的。

一种从Python 3中的异常对象获取字符串回溯的方法:

import traceback

# `e` is an exception object that you get from somewhere
traceback_str = ''.join(traceback.format_tb(e.__traceback__))

traceback.format_tb(…)
返回字符串列表<代码>''。连接(…)
将它们连接在一起。有关更多参考信息,请访问:

作为旁白,如果您希望实际获得打印到终端的完整回溯,您需要:

>>> try:
...     print(1/0)
... except Exception as e:
...     exc = e
...
>>> exc
ZeroDivisionError('division by zero')
>>> tb_str = traceback.format_exception(etype=type(exc), value=exc, tb=exc.__traceback__)
>>> tb_str
['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: division by zero\n']
>>> print("".join(tb_str))
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
您可以使用返回
str

或打印到标准输出

import traceback

try:
    b"x81".decode()
except UnicodeError:
    traceback.print_exc() # prints to stdout
    my_traceback = traceback.format_exc() # returns a str
print(my_traceback)
import traceback

try:
    b"x81".decode()
except UnicodeError as exc:
    # etype is inferred from `value` since python3.5 so no need to pass a value...
    # format_exception returns a list
    my_traceback = "".join(traceback.format_exception(etype=None, value=exc, tb=exc.__traceback__))
    traceback.print_exception(etype=None, value=exc, tb=exc.__traceback__)
如果您需要从实际的异常中获取它(尽管我不明白为什么)

返回一个
str

打印到标准输出

import traceback

try:
    b"x81".decode()
except UnicodeError:
    traceback.print_exc() # prints to stdout
    my_traceback = traceback.format_exc() # returns a str
print(my_traceback)
import traceback

try:
    b"x81".decode()
except UnicodeError as exc:
    # etype is inferred from `value` since python3.5 so no need to pass a value...
    # format_exception returns a list
    my_traceback = "".join(traceback.format_exception(etype=None, value=exc, tb=exc.__traceback__))
    traceback.print_exception(etype=None, value=exc, tb=exc.__traceback__)
小心 不要存储对
\uuu traceback\uuu
(或
exc
)的引用以供以后使用,因为回溯对象包含对所有堆栈帧对象的引用,这些对象包括调用堆栈,每个堆栈帧包含对其所有局部变量的引用。因此,可从回溯对象访问的对象的传递闭包的大小可能非常大。如果维护该引用,这些对象将不会被垃圾收集。更愿意将回溯呈现为另一种形式,以便在内存中进行短期存储。”


罗伯特Simulthy- Python超越基本- 11例外和错误- Traceback对象< /P>我同意返回异常是某种非常规的,但看到一些背后的理论基础。@ THG435,好,这是更有意义的。请考虑我的上述解决方案使用<代码> sys。ExcIfnIs<代码>结合我建议您的O另一个问题。关于回溯对象的更多信息(几乎没有):这是错误的。Python 3确实将回溯对象置于异常状态,如

e.。\uuuu traceback\uuuuu
@GlennMaynard Python 3通过在退出时删除异常目标来解决此问题