-1

Lets's see this example:

with mycontextmanager(arg1='value', arg2=False):
    print 'Executed'

Is there a way to not execute code block (print 'Executed') within a context manager based on argument, eg: when arg2 is not False?

canni
  • 5,737
  • 9
  • 46
  • 68

2 Answers2

1

An other option is to use a special ConditionalExecution context manager whose __enter__ methods returns an action that conditionally raises a SkipExecution exception. The __exit__ method suppresses only this exception. Something like the following:

class SkipExecution(Exception): pass

class ConditionalExecution(object):
    def __init__(self, value, arg):
        self.value = value
        self.arg = arg
    def __enter__(self):
        def action():
            if not self.arg:
                raise SkipExecution()
        return action
    def __exit__(self, exc_type, exc_value, tb):
        if exc_type is SkipExecution:
            return True
        return False

Used as:

In [17]: with ConditionalExecution(1, True) as check_execution:
    ...:     check_execution()
    ...:     print('Hello')
    ...:     
Hello

In [18]: with ConditionalExecution(1, False) as check_execution:
    ...:     check_execution()
    ...:     print('Hello')

In [19]: 

However the problem is that you have to add a call to the value returned.

The problem is that __exit__ is called if and only if __enter__ returned succesfully, which means you can't raise an exception in __enter__ to block the execution of the code block. If you want you can modify this solution so that the call to check_execution() can be done in the first line, like:

In [29]: with ConditionalExecution(1, True) as check_execution, check_execution():
    ...:     print('Hello')
Hello

In [30]: with ConditionalExecution(1, False) as check_execution, check_execution():
    ...:     print('Hello')

Using a Skipper helper-context manager:

class SkipExecution(Exception): pass

class Skipper(object):
    def __init__(self, func):
        self.func = func
    def __call__(self):
        return self.func() or self
    def __enter__(self):
        return self
    def __exit__(self, *args):
        pass

class ConditionalExecution(object):
    def __init__(self, value, arg):
        self.value = value
        self.arg = arg
    def __enter__(self):
        def action():
            if not self.arg:
                raise SkipExecution()
        return Skipper(action)
    def __exit__(self, exc_type, exc_value, tb):
        if exc_type is SkipExecution:
            return True
        return False

I don't think there is anyway to do this without at least an explicit function call as in the above example.

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
0

Perhaps like this, assuming your context manager supports a "whatever" property which exposes the value it got in arg2 when initialized:

with mycontextmanager(arg1='value', arg2=False) as ctx:
    if ctx.whatever:
        print 'Executed'
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Currently this is my solution, I'm hoping that I might be missing something, witch would remove need for this additional statement – canni Mar 30 '14 at 12:00
  • Well you probably should have said so in your question. No, you can't get rid of the extra code, because that code is doing what you want. You could of course write some sort of wrapper function or use a global variable! – John Zwinck Mar 30 '14 at 12:01