Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional with statement in Python

Is there a way to begin a block of code with a with statement, but conditionally?

Something like:

if needs_with():     with get_stuff() as gs:  # do nearly the same large block of stuff, # involving gs or not, depending on needs_with() 

To clarify, one scenario would have a block encased in the with statement, while another possibility would be the same block, but not encased (i.e., as if it wasn't indented)

Initial experiments of course give indentation errors..

like image 505
norman Avatar asked Jan 06 '15 16:01

norman


2 Answers

Python 3.3 introduced contextlib.ExitStack for just this kind of situation. It gives you a "stack", to which you add context managers as necessary. In your case, you would do this:

from contextlib import ExitStack  with ExitStack() as stack:     if needs_with():         gs = stack.enter_context(get_stuff())      # do nearly the same large block of stuff,     # involving gs or not, depending on needs_with() 

Anything that is entered to stack is automatically exited at the end of the with statement as usual. (If nothing is entered, that's not a problem.) In this example, whatever is returned by get_stuff() is exited automatically.

If you have to use an earlier version of python, you might be able to use the contextlib2 module, although this is not standard. It backports this and other features to earlier versions of python. You could even do a conditional import, if you like this approach.

like image 63
Mike Avatar answered Sep 20 '22 15:09

Mike


If you want to avoid duplicating code and are using a version of Python prior to 3.7 (when contextlib.nullcontext was introduced) or even 3.3 (when contextlib.ExitStack was introduced), you could do something like:

class dummy_context_mgr():     def __enter__(self):         return None     def __exit__(self, exc_type, exc_value, traceback):         return False 

or:

import contextlib  @contextlib.contextmanager def dummy_context_mgr():     yield None 

and then use it as:

with get_stuff() if needs_with() else dummy_context_mgr() as gs:    # do stuff involving gs or not 

You alternatively could make get_stuff() return different things based on needs_with().

(See Mike's answer or Daniel's answer for what you can do in later versions.)

like image 33
jamesdlin Avatar answered Sep 22 '22 15:09

jamesdlin