I encountered a strange behaviour in Python's with-statement recently. I have a code which uses Python's context managers to rollback configuration changes in __exit__
method. The manager had a return False
value in a finally block in __exit__
. I've isolated the case in following code - the only difference is with the indent of return statement:
class Manager1(object):
def release(self):
pass # Implementation not important
def rollback(self):
# Rollback fails throwing an exception:
raise Exception("A failure")
def __enter__(self):
print "ENTER1"
def __exit__(self, exc_type, exc_val, exc_tb):
print "EXIT1"
try:
self.rollback()
finally:
self.release()
return False # The only difference here!
class Manager2(object):
def release(self):
pass # Implementation not important
def rollback(self):
# Rollback fails throwing an exception:
raise Exception("A failure")
def __enter__(self):
print "ENTER2"
def __exit__(self, exc_type, exc_val, exc_tb):
print "EXIT2"
try:
self.rollback()
finally:
self.release()
return False # The only difference here!
In the code above the rollback fails of with an Exception. My question is, why Manager1
is behaving differently than Manager2
. The exception is not thrown outside of with-statement in Manager1
and why it IS thrown on exit in Manager2
.
with Manager1() as m:
pass # The Exception is NOT thrown on exit here
with Manager2() as m:
pass # The Exception IS thrown on exit here
According to documentation of __exit__
:
If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.
In my opinion in both cases the exit is not returning True, thus the exception should not be supressed in both cases. However in Manager1 it is. Can anyone explain that?
I use Python 2.7.6.