Conditional context manager

69 views
Skip to first unread message

Neil Girdhar

unread,
Oct 1, 2016, 2:07:25 PM10/1/16
to python-ideas
I'm just throwing this idea out there to get feedback.

Sometimes, I want to conditionally enter a context manager.  This simplest (afaik) way of doing that is:

    with ExitStack() as stack:
        if condition:
            cm = stack.enter_context(cm_function())
        suite()

I suggest a more compact notation:

    with cm_function() as cm if condition:
        suite()

I'm not sure that this is possible within the grammar.  (For some reason with with_expr contains '"as" expr' rather than '"as" NAME'?

I realize this comes up somewhat rarely.  I use context managers a lot, and it comes up maybe 1 in 5k lines of code.

For some extensions of this notation, an else clause could bind a value to cm in the case that condition is false.

Best,

Neil

MRAB

unread,
Oct 1, 2016, 3:43:14 PM10/1/16
to python...@python.org
If you defined a null context manager, you could then write:

with (cm_function() if condition else cm_null()) as cm:
suite()

Do you need 'cm' itself? Its type changes depending on the condition, so
I don't see how it could be useful.

If it's not needed, then that shortens a little to:

with cm_function() if condition else cm_null():
suite()

_______________________________________________
Python-ideas mailing list
Python...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Neil Girdhar

unread,
Oct 1, 2016, 4:01:46 PM10/1/16
to python...@googlegroups.com, python...@python.org
FYI: There is a null context manager: ExitStack().

--

---
You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/dcu3O1qaC3E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-ideas...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Paul Moore

unread,
Oct 1, 2016, 4:14:39 PM10/1/16
to Neil Girdhar, Python-Ideas
Resending, because Google Groups messes up replying to the list :-(

On 1 October 2016 at 21:09, Paul Moore <p.f....@gmail.com> wrote:
> On 1 October 2016 at 19:07, Neil Girdhar <miste...@gmail.com> wrote:
>> Sometimes, I want to conditionally enter a context manager. This simplest
>> (afaik) way of doing that is:
>>
>> with ExitStack() as stack:
>> if condition:
>> cm = stack.enter_context(cm_function())
>> suite()
>>
>> I suggest a more compact notation:
>>
>> with cm_function() as cm if condition:
>> suite()
>
> This sounds like exactly the sort of situation ExitStack was designed
> for. I'm not sure the situation is common enough to need dedicated
> syntax beyond that. I actually find the ExitStack version easier to
> understand, as the condition is more visible when it's not tucked away
> at the end of the with statement.
>
> If compactness is that important, you could refactor the code to use a
> custom context manager that encapsulates the "cm_function if condition
> else nothing" pattern.
>
> Paul

Chris Angelico

unread,
Oct 1, 2016, 6:46:29 PM10/1/16
to python-ideas
On Sun, Oct 2, 2016 at 5:07 AM, Neil Girdhar <miste...@gmail.com> wrote:
> I suggest a more compact notation:
>
> with cm_function() as cm if condition:
> suite()
>

The simplest way would be to make a conditional version of the context manager.

@contextlib.contextmanager
def maybe_cm(state):
if state:
with cm_function() as cm:
yield cm
else:
yield None

I believe that'll work.

ChrisA

Nick Coghlan

unread,
Oct 2, 2016, 6:31:57 AM10/2/16
to python...@python.org
Forwarding to the list, since I took the broken Google Group cc out of
the reply list, but didn't added the proper one.


---------- Forwarded message ----------
From: Nick Coghlan <ncog...@gmail.com>
Date: 2 October 2016 at 17:45
Subject: Re: [Python-ideas] Conditional context manager
To: Neil Girdhar <miste...@gmail.com>


On 2 October 2016 at 04:07, Neil Girdhar <miste...@gmail.com> wrote:
> I'm just throwing this idea out there to get feedback.
>
> Sometimes, I want to conditionally enter a context manager. This simplest
> (afaik) way of doing that is:
>
> with ExitStack() as stack:
> if condition:
> cm = stack.enter_context(cm_function())
> suite()
>
> I suggest a more compact notation:
>
> with cm_function() as cm if condition:
> suite()

As Chris notes, this is typically going to be better handled by
creating an *un*conditional CM that encapsulates the optionality so
you don't need to worry about it at the point of use.

If you wanted a generic version of that, then the stack creation and
cm creation can be combined into a single context manager:

@contextmanager
def optional_cm(condition, cm_factory, *args, **kwds):
stack = ExitStack()
cm = None
with stack:
if condition:
cm = stack.enter_context(cm_factory(*args, **kwds))
yield stack, cm

However, even simpler than both this and Chris's maybe_cm() example is
the plain ExitStack-as-the-null-context-manager function approach
already covered in the contextlib docs:
https://docs.python.org/3/library/contextlib.html#simplifying-support-for-single-optional-context-managers

Applying that approach to this particular pattern looks like:

def optional_cm(condition, cm_factory, *args, **kwds):
if condition:
return cm_factory(*args, **kwds)
return ExitStack()

Resulting in:

with optional_cm(condition, cm_function):
suite()

which is fine for a construct that is uncommon in general, but may be
popular in a particular code base.

Cheers,
Nick.

--
Nick Coghlan | ncog...@gmail.com | Brisbane, Australia


--
Nick Coghlan | ncog...@gmail.com | Brisbane, Australia
Reply all
Reply to author
Forward
0 new messages