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..
You can use
contextlib.nested
to put 0 or more context managers into a singlewith
statement.This solution has its quirks and I just noticed that as of 2.7 its been deprecated. I wrote my own context manager to handle juggling multiple context managers. Its worked for me so far, but I haven't really considered edge conditons
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 (whencontextlib.ExitStack
was introduced), you could do something like:or:
and then use it as:
You alternatively could make
get_stuff()
return different things based onneeds_with()
.See Mike or Daniel's answers for what you can do in later versions
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:Anything that is entered to
stack
is automaticallyexit
ed at the end of thewith
statement as usual. (If nothing is entered, that's not a problem.) In this example, whatever is returned byget_stuff()
isexit
ed 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.It was hard to find @farsil's nifty Python 3.3 one-liner, so here it is in its own answer:
Note that ExitStack should come first, otherwise
get_stuff()
will be evaluated.A third-party option to achieve exactly this:
https://pypi.python.org/pypi/conditional
As of Python 3.7 you can use
contextlib.nullcontext
:contextlib.nullcontext
is pretty much just a no-op context manager. You can pass it an argument that it will yield, if you depend on something existing after theas
:Otherwise it'll just return
None
:It's super neat, check out the docs for it here: https://docs.python.org/3/library/contextlib.html#contextlib.nullcontext