shaoyl85 shaoyl85 - 4 years ago 178
Python Question

Context Manager without Yield

Can I have a context manager which occasionally does not yield, and in which case the code within the with statement are simply not executed?

import contextlib

@contextlib.contextmanager
def MayNotYield(to_yield):
if to_yield:
yield

with MayNotYield(True):
print 'This works.'

with MayNotYield(False):
print 'This errors.'


I could ask the user to wrap the with statement with a try-catch, but that is not preferred. I could also do the following but it is ugly too.

import contextlib

@contextlib.contextmanager
def AlwaysYields(to_yield):
if to_yield:
yield 1
else:
yield 2

with AlwaysYields(True) as result:
if result == 1:
print 'This works.'


UPDATE:

As a conclusion, the answer is that the "with" statement does not have an option to skip the body. Whatever the
__enter__
and
__exit__
does, as long as
__enter__
does not throw an exception, the body will always be executed. However, the post Python context manager: conditionally executing body? provided a few hacky ways as workaround. In particular, the user could use the
for
statement instead.

Answer Source

Unfortunately, the context manager protocol does not give a context manager a way to say "Don't run the with block" (except raising an exception in __enter__). If you're using a context manager anyway, I think your second approach, which has __enter__ return a value to signal if the block should be run is the best approach. If you don't need a context manager for some other reason, you could just use a simple if statement:

if do_stuff:
    # do the stuff
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download