Я хочу, чтобы заменить __enter__
/__exit__
функции класса с одной функцией, украшенной, как contextlib.contextmanager
, вот код:Почему исключение для ретранслятора контенту не требуется?
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
, этого не происходит: после теста __exit__
я не вижу исключения (и код работает неправильно, без исключения).
Почему не exeprion ререйз внутри __exit__
?
Если это нормальное поведение, не могли бы вы посоветовать мне решение правильно заменить __enter__
/__exit__
с функцией contextmanager
?
Редактировать:
cm_obj.__exit__
возвращает None
на исключение внутри _cm
и True
, если исключение было подавлено (или не поднятая вообще).
С другой стороны, внутри Test.__exit__
мы можем вернуть None
для распространения текущего исключения или True
для его подавления.
Похоже, просто возвращается значение cm_obj.__exit__
«s внутри Test.__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)
«возвращает истину, чтобы обеспечить исключение не воскреснет» - в этом случае каждое исключение будет подавлен, но я хочу его быть подавлен только в том случае, если он был подавлен внутри _cm() и ререйзирован, если он не был подавлен внутри _cm() –
@germn: вы не можете. Подавленные исключения в блоке не доходят до контекстного менеджера. –
«вы не можете возбудить исключение в другом методе (кроме генератора)» - это одна из причин, по которой '@ contextmanager' хочет, чтобы вы дали ему генератор. – user2357112