给定一个Exception对象(来源不明)有没有办法获得它的追溯?我有这样的代码:
def stuff():
try:
.....
return useful
except Exception as e:
return e
result = stuff()
if isinstance(result, Exception):
result.traceback <-- How?
如果我拥有它,我如何从Exception对象中提取回溯?
答案 0 :(得分:68)
这个问题的答案取决于您正在使用的Python版本。
这很简单:异常配备了包含回溯的__traceback__
属性。此属性也是可写的,可以使用with_traceback
例外方法方便地设置:
raise Exception("foo occurred").with_traceback(tracebackobj)
这些功能最低限度地描述为raise
文档的一部分。
答案的这一部分应归功于Vyctor,first posted this information。我把它包括在这里只是因为这个答案停留在顶部,而Python 3正变得越来越普遍。
这很烦人。回溯的问题在于它们具有对堆栈帧的引用,并且堆栈帧具有对引用堆栈帧that have references to...的回溯的引用。这会导致垃圾收集器出现问题。 (感谢ecatmur首先指出这一点。)
解决这个问题的好方法是在离开{3}}子句后进行手术break the cycle,这就是Python 3所做的。 Python 2解决方案更加丑陋:为您提供了一个特殊功能sys.exc_info()
,其中仅在 except
子句中有效。它返回一个包含异常,异常类型和当前正在处理的异常的回溯的元组。
因此,如果您在except
子句中,则可以使用except
的输出和traceback
模块来执行各种有用的操作:
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
在没有处理异常时返回sys.exc_info
。其他相关的(None, None, None)
属性也无济于事。当没有处理异常时,sys
被弃用并且未定义; sys.exc_traceback
似乎很完美,但似乎只在交互式会话期间定义。
如果您可以控制引发异常的方式,则可以使用inspect
和custom exception来存储某些信息。但我不完全确定这会如何起作用。
说实话,捕获并返回异常是一件不寻常的事情。这可能表明您无论如何都需要重构。
答案 1 :(得分:53)
由于Python 3.0[PEP 3109]内置类Exception
具有__traceback__
属性,其中包含traceback object
(使用Python 3.2.3):
>>> try:
... raise Exception()
... except Exception as e:
... tb = e.__traceback__
...
>>> tb
<traceback object at 0x00000000022A9208>
问题是,在Googling __traceback__
一段时间之后,我发现只有少数文章,但没有一篇文章说明您是否应该(不)使用__traceback__
。
然而,Python 3 documentation for raise
说:
通常会在引发异常时自动创建回溯对象,并将其作为
__traceback__
属性附加到该属性,该属性是可写的。
所以我认为它意味着要使用。
答案 2 :(得分:13)
从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(...)
返回字符串列表。 ''.join(...)
将他们加在一起。如需更多参考,请访问:https://docs.python.org/3/library/traceback.html#traceback.format_exc
答案 3 :(得分:8)
有一个很好的理由,回溯没有存储在异常中;因为回溯保持对其堆栈本地的引用,这将导致循环引用和(临时)内存泄漏,直到循环GC启动。(这就是为什么你应该never store the traceback in a local variable。)
关于我能想到的唯一一件事就是你为monkeypatch stuff
的全局变量,这样当它认为它正在捕捉Exception
时它实际上捕获了一个特殊类型,并且异常传播给你来电者:
module_containing_stuff.Exception = type("BogusException", (Exception,), {})
try:
stuff()
except Exception:
import sys
print sys.exc_info()
答案 4 :(得分:6)
顺便说一句,如果您希望像在终端上看到的那样获得完整的追溯,则需要这样做:
>>> 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
如果您按照上述答案使用format_tb
,则建议您获得的信息较少:
>>> tb_str = "".join(traceback.format_tb(exc.__traceback__))
>>> print("".join(tb_str))
File "<stdin>", line 2, in <module>
答案 5 :(得分:2)
您可以使用 traceback.format_exc
返回 str
或 traceback.print_exc
打印到标准输出
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)
如果您需要从实际异常中获取它(虽然我不明白为什么)
traceback.format_exception
返回一个 str
traceback.print_exception
打印到标准输出
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__)
不要存储对__traceback__
(或exc
)的引用以备后用,因为回溯对象包含对构成调用堆栈的所有堆栈帧对象的引用,并且每个堆栈帧都包含引用到它的所有局部变量。因此,从回溯对象可达的对象的传递闭包的大小可能非常大。如果您维护该引用,这些对象将不会被垃圾收集。更喜欢将回溯呈现为另一种形式,以便在内存中进行短期存储。”
Robert Smallshire - Python Beyond the Basics - 11 - 异常和错误 - 回溯对象