在Python中调用代码中的__enter__中捕获异常

时间:2014-07-22 03:52:35

标签: python exception with-statement contextmanager

有没有办法可以在上下文管理器的__enter__方法中捕获异常而不将整个with块包含在try内?

class TstContx(object):
    def __enter__(self):
        raise Exception("I'd like to catch this exception")
    def __exit__(self, e_typ, e_val, trcbak):
        pass


with TstContx():
    raise Exception("I don't want to catch this exception")
    pass

我知道我可以在__enter__()本身内捕获异常,但是我可以从包含with语句的函数中访问该错误吗?

表面上问题Catching exception in context manager __enter__()似乎是一回事,但问题实际上是确保__exit__被调用,而不是将__enter__代码与块不同with语句包含。

......显然,动机应该更加清晰。 with语句正在为完全自动化的进程设置一些日志记录。如果程序在设置日志记录之前失败,那么我就不能依赖日志记录来通知我,所以我必须做一些特殊的事情。而且我宁愿达到效果,而不必添加更多缩进,如下所示:

try:
    with TstContx():
        try:
            print "Do something"
        except Exception:
            print "Here's where I would handle exception generated within the body of the with statement"
except Exception:
    print "Here's where I'd handle an exception that occurs in __enter__ (and I suppose also __exit__)"

使用两个try块的另一个缺点是,处理__enter__中的异常的代码位于处理with块的后续主体中的异常的代码之后。

2 个答案:

答案 0 :(得分:2)

您可以使用try内的except / __enter__来捕获异常,然后将异常实例保存为TstContx类的实例变量,以便您访问它位于with块内:

class TstContx(object):
    def __enter__(self):
        self.exc = None
        try:
            raise Exception("I'd like to catch this exception")
        except Exception as e:
            self.exc = e 
        return self

    def __exit__(self, e_typ, e_val, trcbak):
        pass    

with TstContx() as tst:
    if tst.exc:
        print("We caught an exception: '%s'" % tst.exc)
    raise Exception("I don't want to catch this exception")

输出:

We caught an exception: 'I'd like to catch this exception'.
Traceback (most recent call last):
  File "./torn.py", line 20, in <module>
    raise Exception("I don't want to catch this exception")
Exception: I don't want to catch this exception

不确定为什么你想要这样做,不过......

答案 1 :(得分:0)

您可以使用contextlib.ExitStack中概述的this doc example来分别检查<child-component>错误:

__enter__