我想用被contextlib.contextmanager
修饰的单一函数来替代类中的__enter__
/__exit__
函数,下面是代码:
class Test:
def __enter__(self):
self._cm_obj = self._cm()
self._cm_obj.__enter__()
def __exit__(self, exc_type, exc_val, exc_tb):
try:
# Here we first time caught exception,
# Pass it to _cm:
self._cm_obj.__exit__(exc_type, exc_val, exc_tb)
except:
# Here we should catch exception reraised by _cm,
# but it doesn't happen.
raise
else:
return True
@contextmanager
def _cm(self):
print('enter')
try:
yield
except:
# Here we got exception from __exit__
# Reraise it to tell __exit__ it should be raised.
raise
finally:
print('exit')
with Test():
raise Exception(123)
当我们在Test的__exit__中遇到异常时,我们将其传递给_cm的__exit__,它可以正常工作,我在_cm中看到了异常。但是,当我决定在_cm中重新引发异常时,它并没有发生:在Test的__exit__之后,我没有看到异常(代码运行不正确,没有异常)。
为什么在__exit__中不会重新引发异常?
如果这是正常行为,您能否建议我正确地使用contextmanager函数替换__enter__/__exit__的解决方案?
编辑:
cm_obj.__exit__在_cm内部发生异常时返回None,并在抑制异常(或根本未引发异常)时返回True。
另一方面,在Test.__exit__内部,我们可以返回None以传播当前异常,或返回True以抑制它。
看起来只需在Test.__exit__中返回cm_obj.__exit__的值即可完成工作,此代码按照我的要求工作:
class Test:
def __enter__(self):
self._cm_obj = self._cm()
self._cm_obj.__enter__()
def __exit__(self, exc_type, exc_val, exc_tb):
return self._cm_obj.__exit__(exc_type, exc_val, exc_tb)
@contextmanager
def _cm(self):
print('---- enter')
try:
yield
except:
raise # comment to suppess exception
pass
finally:
print('---- exit')
with Test():
raise Exception(123)
@contextmanager
要求你提供生成器的原因之一。 - user2357112generator.throw()
返回的。但是contextmanager.__exit__
不是一个生成器,因此在托管上下文中引发的异常不会在其中抛出。 - Martijn Pietersself._cm.__exit__()
的结果意味着您正确地包装了上下文管理器。 - Martijn Pieters