Context Manager without Yield

python context manager
python context manager without with
python __exit__
python timeout context manager
python ignore exception context manager
python wrap context manager
python context manager exception
python nested context managers

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.'

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

what does yield without value do in context manager, yield expression returns control to the whatever is using the generator. The generator pauses at this point, which means that the  26. Context Managers¶ Context managers allow you to allocate and release resources precisely when you want to. The most widely used example of context managers is the with statement. Suppose you have two related operations which you’d like to execute as a pair, with a block of code in between. Context managers allow you to do specifically that.

Another option would be to just use a regular generator rather than a contextmanager; a plain generator won't have this restriction. But you'll have to use it with a "for" construct rather than using "with":

def MayNotYield(to_yield):
   if to_yield:
      yield

for _ in MayNotYield(True):
   print('This prints.')

for _ in MayNotYield(False):
   print('This does not.')

contextlib — Utilities for with-statement contexts, For more information see also Context Manager Types and With Statement to define a factory function for with statement context managers, without needing At the point where the generator yields, the block nested in the with statement is  contextlib contains tools for creating and working with context managers. One nice shortcut to creating a context manager from a class is to use the @contextmanager decorator. To use it, decorate a generator function that calls yield exactly once. Everything before the call to yield is considered the code for __enter__().

Given the goal of having the conditional within the context manager implementation, there is another possibility in cases where it is possible to extract the the contents of the with-block into its own function. You can pass this callable to the context manager and have the context manager return the passed callable or a dummy do-nothing callable from the context manager depending on the boolean. The with-block will always execute, but the action may or may not be invoked.

def do_something():
    print("This works!")

@contextlib.contextmanager
def conditional_on(condition, f):
    if condition:
        # Acquire resources here
        pass
    else:
        # Replace the callable with a do-nothing dummy
        f = lambda x: x
    try:
        yield f
    finally:
        if condition:
            # Release resources here
            pass    

with conditional_on(True, do_something) as f:
    f()  # Prints message

with conditional_on(False, do_something) as f:
    f()  # Does nothing

You will need to tailor this solution depending on which, if any, resources the context manager is managing, and on the required signature of of the callable.

28.7. contextlib — Utilities for with-statement contexts, For more information see also Context Manager Types and With Statement to define a factory function for with statement context managers, without needing At the point where the generator yields, the block nested in the with statement is  For more information see also Context Manager Types and With Statement Context Managers. Functions provided: contextlib.contextmanager (func) ¶ This function is a decorator that can be used to define a factory function for with statement context managers, without needing to create a class or separate __enter__() and __exit__() methods.

Python Context Managers, Although context managers seem a little strange at first, when we really dive into them, that lets them implement the same functionality without all the boilerplate. This is Like the code above, we can simply define a function that yield s the  Context managers created using contextmanager() are also single use context managers, and will complain about the underlying generator failing to yield if an attempt is made to use them a second time:

contextlib – Context manager utilities, The contextlib module contains utilities for working with context managers and The generator should initialize the context, yield exactly one time, then clean up block, though, this adds to the indention level without giving any real benefit. An asynchronous context manager is a context manager that is able to suspend execution in its enter and exit methods. To make this possible, a new protocol for asynchronous context managers is proposed. Two new magic methods are added: __aenter__ and __aexit__. Both must return an awaitable. An example of an asynchronous context manager:

Context Managers, Nowadays, files are context managers in Python, meaning that it is possible to write file processing code without explicitly closing the files you open. Using a decorator to a generator function that contains precisely one yield expression. 29.6.1. Utilities¶. Functions and classes provided: @contextlib.contextmanager¶ This function is a decorator that can be used to define a factory function for with statement context managers, without needing to create a class or separate __enter__() and __exit__() methods.

Comments
  • What are you actually trying to achieve?
  • @jonrsharpe: My use case is that I put initialization and clean up code in a context manager. The user of the context manager runs their code in the body. My context manager also forks a few processes and only one process is left to the user. All the other processes are to be handled by the library itself.
  • @shaoyl85: That still doesn't say why you wouldn't yield. It looks like you're trying to combine an if statement with a context manager to get around the inability to assign in an if statement, not trying to manage resources.
  • Could you explain this in the question, and give a less abstract example? It may help clarify whether this is an XY problem and get you better answers.
  • @jonrsharpe: Thank you! I believe my problem can have tons of solutions. My current question is just about whether it is possible to skip the yield. And I think the answer is indeed like what Blckknght said, the "with" statement itself does not have an option to skip the body. It is not the problem of the contextlib.contextmanager. I'm good with that.