给定一个异常对象(来源不明)有没有办法获得它的回溯?我有这样的代码:
def stuff():
try:
.....
return useful
except Exception as e:
return e
result = stuff()
if isinstance(result, Exception):
result.traceback <-- How?
拥有 Exception 对象后,如何从它中提取回溯?
给定一个异常对象(来源不明)有没有办法获得它的回溯?我有这样的代码:
def stuff():
try:
.....
return useful
except Exception as e:
return e
result = stuff()
if isinstance(result, Exception):
result.traceback <-- How?
拥有 Exception 对象后,如何从它中提取回溯?
这个问题的答案取决于您使用的 Python 版本。
很简单:异常带有一个__traceback__
包含回溯的属性。该属性也是可写的,可以使用with_traceback
异常的方法方便地设置:
raise Exception("foo occurred").with_traceback(tracebackobj)
这些功能作为raise
文档的一部分进行了最低限度的描述。
这部分答案的所有功劳都应归功于首先发布此信息的维克多。我将它包括在这里只是因为这个答案停留在顶部,并且 Python 3 变得越来越普遍。
这是令人讨厌的复杂。回溯的问题在于它们具有对堆栈框架的引用,而堆栈框架具有对具有对具有对...的引用的堆栈框架的引用的回溯的引用。你明白了。这会导致垃圾收集器出现问题。(感谢ecatmur首先指出这一点。)
解决这个问题的好方法是在离开子句后通过手术打破循环except
,这就是 Python 3 所做的。Python 2 的解决方案要丑得多:为您提供了一个临时函数 ,sys.exc_info()
它只在 except
子句中起作用。它返回一个元组,其中包含异常、异常类型和当前正在处理的任何异常的回溯。
因此,如果您在except
子句中,您可以使用模块的输出sys.exc_info()
来做traceback
各种有用的事情:
>>> 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
当没有处理异常时被弃用和未定义;sys.last_traceback
看起来很完美,但它似乎只能在交互式会话中定义。
如果您可以控制引发异常的方式,则可以使用inspect
自定义异常来存储一些信息。但我不完全确定这将如何工作。
说实话,捕获并返回异常是一件不寻常的事情。这可能表明您无论如何都需要重构。
从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>
问题是,在谷歌搜索__traceback__
了一段时间后,我只找到了几篇文章,但没有一篇文章描述了您是否应该(或为什么)应该(不)使用__traceback__
.
但是,Python 3 文档raise
说:
回溯对象通常在引发异常时自动创建并作为
__traceback__
可写属性附加到它。
所以我认为它应该被使用。
一种从 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_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
如果您使用format_tb
上述答案建议您将获得更少的信息:
>>> tb_str = "".join(traceback.format_tb(exc.__traceback__))
>>> print("".join(tb_str))
File "<stdin>", line 2, in <module>
回溯没有存储在异常中是有充分理由的;因为回溯保存对其堆栈局部变量的引用,这将导致循环引用和(临时)内存泄漏,直到循环 GC 启动。(这就是为什么您永远不应该将回溯存储在局部变量中。)
关于我能想到的唯一一件事就是让你使用 monkeypatchstuff
的全局变量,这样当它认为它正在捕获时,Exception
它实际上是在捕获一个专门的类型,并且异常作为调用者传播给你:
module_containing_stuff.Exception = type("BogusException", (Exception,), {})
try:
stuff()
except Exception:
import sys
print sys.exc_info()
你可以使用traceback.format_exc
which 返回一个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 超越基础 - 11 - 异常和错误 - Traceback 对象