首页 > 解决方案 > Python sys.exc_info in finally 打印出无法捕获的异常

问题描述

我有以下代码:

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()

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

但实际情况是,except子句并没有执行,而是sys.exc_info()finally子句中打印出原来的异常。这让我感到困惑,因为不__exit__返回 True 应该抑制异常?为什么那个exceptsys.exc_info()看到的异常之间有差异?

标签: python

解决方案


我找到了我的问题的答案。

因此,在上下文管理器中(使用@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()

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

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

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

不能总是转化为

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

with fun():
    do_something()

推荐阅读