Python sys.exc_info in 终于打印出无法捕获的异常



我有以下代码:

import contextlib
import sys
class CC:
def __enter__(self):
pass

def __exit__(self, *args):
return True
@contextlib.contextmanager
def ctx():
try:
with CC():
yield
except:
print("has exception")
raise
finally:
print("finally")
print(sys.exc_info())
def test():
with ctx():
raise ValueError()
test()

这有点令人费解。但我的想法是在上下文ctx中提出一个异常。因为CC在退出时返回True,所以应该抑制异常。因此,我预计except子句不会执行,finally子句中的sys.exc_info()不会打印任何内容。

但实际情况是,except子句不执行,但finally子句中的sys.exc_info()打印原始异常。这让我很困惑,因为__exit__返回True不是应该抑制异常吗?为什么exceptsys.exc_info()看到的异常之间存在差异?

我找到了问题的答案。

因此,在上下文管理器(使用@contextmanager或显式定义的__exit__(中,当出现异常时,它会从yield中抛出。基本上,代码的其余部分在抛出代码的except中执行。上下文管理器当然可以捕获并抑制重新引发的异常,但它不会改变它在except子句中的事实,因此它仍然在处理原始异常。这意味着,即使重新引发的异常被捕获并抑制,sys.exc_info()仍将显示原始异常。

举个例子,这意味着

@contextmanager
def ctx():
try:
try:
yield
except:
pass
except:
assert False
finally:
print(sys.exc_info())
with ctx():
raise ValueError()

将在finally中打印原始ValueError,尽管外部except不会捕获任何内容。

此外,我认为这意味着上下文管理器并不是代码重构的完全替代品。

with ctx:
try:
foo()
except:
...

不能总是转换成

@contextmanager
def fun():
with ctx:
try:
yield
except:
...
with fun():
do_something()

最新更新