geo*_*org 95 python debugging exception-handling
给定一个Exception对象(来源不明)有没有办法获得它的追溯?我有这样的代码:
def stuff():
try:
.....
return useful
except Exception as e:
return e
result = stuff()
if isinstance(result, Exception):
result.traceback <-- How?
Run Code Online (Sandbox Code Playgroud)
有了它,如何从Exception对象中提取回溯?
sen*_*rle 78
这个问题的答案取决于您正在使用的Python版本.
这很简单:异常配备了__traceback__包含回溯的属性.此属性也是可写的,可以使用with_traceback异常方法方便地设置:
raise Exception("foo occurred").with_traceback(tracebackobj)
Run Code Online (Sandbox Code Playgroud)
这些功能最低限度地描述为raise文档的一部分.
这部分答案的所有功劳都归功于Vyctor,他首先发布了这些信息.我把它包括在这里只是因为这个答案停留在顶部,而Python 3正变得越来越普遍.
这很烦人.回溯的问题在于它们具有对堆栈帧的引用,并且堆栈帧具有对回溯的引用,这些回溯具有对引用了...的堆栈帧的引用.这会导致垃圾收集器出现问题.(感谢ecatmur首先指出这一点.)
解决这个问题的好方法是在离开子句后手术打破循环except,这就是Python 3所做的.Python 2解决方案更加丑陋:您可以使用ad-hoc函数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
Run Code Online (Sandbox Code Playgroud)
但是,随着您的编辑表示,你正在试图获得该回溯会,如果你的异常没有被处理的已打印,它之后已经被处理.这是一个更难的问题.不幸的是,没有处理异常时sys.exc_info返回(None, None, None).其他相关sys属性也无济于事.sys.exc_traceback在没有处理异常时被弃用和未定义; sys.last_traceback看起来很完美,但似乎只能在交互式会话期间定义.
如果你能控制异常怎么提高,你也许能够使用inspect和自定义异常来存储一些信息.但我不完全确定这是如何工作的.
说实话,捕获并返回异常是一件不寻常的事情.这可能表明您无论如何都需要重构.
Vyk*_*tor 60
从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>
Run Code Online (Sandbox Code Playgroud)
问题是,谷歌搜索__traceback__了一段时间后,我发现只有少数文章,但没有一个描述你是否应该(不)使用__traceback__.
但是,Python 3文档raise说:
通常会在引发异常时自动创建回溯对象,并将其作为
__traceback__属性附加到该属性,该属性是可写的.
所以我认为它意味着要使用.
Hie*_*ieu 21
从Python 3中的异常对象获取回溯作为字符串的方法:
import traceback
# `e` is an exception object that you get from somewhere
traceback_str = ''.join(traceback.format_tb(e.__traceback__))
Run Code Online (Sandbox Code Playgroud)
traceback.format_tb(...)返回字符串列表.''.join(...)将他们联系在一起.如需更多参考,请访问:https://docs.python.org/3/library/traceback.html#traceback.format_exc
Mar*_*ark 11
你可以使用traceback.format_exc它返回一个str
或traceback.print_exc打印到标准输出
import traceback\n\ntry:\n b"x81".decode()\nexcept UnicodeError:\n traceback.print_exc() # prints to stdout\n my_traceback = traceback.format_exc() # returns a str\nprint(my_traceback)\nRun Code Online (Sandbox Code Playgroud)\n如果您需要从实际异常中获取它(虽然我不明白为什么)
\ntraceback.format_exception返回一个str
traceback.print_exception打印到标准输出
import traceback\n\ntry:\n b"x81".decode()\nexcept UnicodeError as exc:\n # etype is inferred from `value` since python3.5 so no need to pass a value...\n # format_exception returns a list\n my_traceback = "".join(traceback.format_exception(etype=None, value=exc, tb=exc.__traceback__))\n traceback.print_exception(etype=None, value=exc, tb=exc.__traceback__)\nRun Code Online (Sandbox Code Playgroud)\n\n\n不要存储对
\n__traceback__(或exc) 的引用以供以后使用,因为回溯对象包含对所有堆栈帧对象(组成调用堆栈)的引用,并且每个堆栈帧都包含对其所有局部变量的引用。因此,从回溯对象可到达的对象的传递闭包的大小可能非常大。如果您维护该引用,这些对象将不会被垃圾\xe2\x80\x91收集。更喜欢将回溯呈现为另一种形式,即使是在内存中存储很短的\xe2\x80\x91term。”
Robert Smallshire - Python 超越基础知识 - 11 - 异常和错误 - 回溯对象
\nDan*_*ous 10
顺便说一句,如果您希望像在终端上看到的那样获得实际的完整追溯,则需要这样做:
>>> 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
Run Code Online (Sandbox Code Playgroud)
如果您使用format_tb上述答案,则建议您获得的信息较少:
>>> tb_str = "".join(traceback.format_tb(exc.__traceback__))
>>> print("".join(tb_str))
File "<stdin>", line 2, in <module>
Run Code Online (Sandbox Code Playgroud)
回溯未存储在异常中是有充分理由的; 因为回溯保持对其堆栈本地的引用,这将导致循环引用和(临时)内存泄漏,直到循环GC启动.(这就是为什么你永远不应该将回溯存储在局部变量中.)
关于我能想到的唯一一件事就是你为monkeypatch stuff的全局变量,这样当它认为它正在捕捉时Exception它实际上捕获了一个特殊的类型,并且异常传播给你作为调用者:
module_containing_stuff.Exception = type("BogusException", (Exception,), {})
try:
stuff()
except Exception:
import sys
print sys.exc_info()
Run Code Online (Sandbox Code Playgroud)