How to stop statements within a Python with context from executing?
Question:
I’ve some code like this:
with SomeContext(args):
<statement1>
.
.
<statementN>
I want this code to behave like this:
if some_condition(args):
f1()
else:
<statement1>
.
.
<statementN>
Statements in else block will also need an access to args.
But I want to hide f1
and some_condition
from user of this abstraction, so using an if-else block is out of option. Also I don’t want to force a constraint on the user to wrap all statements in a function. Is it possible to do some python magic in the with
context that allows this?
Answers:
Yes, you can easily create such a context manager as follows.
import contextlib
@contextlib.contextmanager
def SomeContext(args):
if some_condition(args):
f1()
else:
yield
The user’s wrapped code gets executed at the point of the yield
. I don’t think it’s a problem that the context manager sometimes does not execute the user’s code, but I haven’t checked.
The closest I can get is to use two nested context managers, something like the following:
class SkippedException(Exception):
pass
class SkipContext:
def __enter__(self):
pass
def __exit__(self, type, value, tb):
return type is SkippedException
class SomeContext:
def __init__(self, arg):
self.arg = arg
def __enter__(self):
if self.arg == 1:
print "arg", self.arg
raise SkippedException()
def __exit__(self, type, value, tb):
pass
with SkipContext(), SomeContext(1):
print "body"
The SkipContext
manager essentially catches the SkippedException
raised by the inner SomeContext
manager in the case where arg == 1
.
Note that the syntax of multiple context expressions is only supported in Python 2.7 or later. In earlier versions, you would have to write:
with SkipContext():
with SomeContext(1):
print "body"
The contextlib.nested
context manager, despite claims in the documentation, doesn’t exactly match the semantics of the above nested with
statement when exceptions are thrown from within __enter__
, so it doesn’t work in this case.
It should be noted that PEP 343 mentions that macros (like context managers) that hide flow control should be discouraged, and references Raymond Chen’s rant against hidden flow control.
I’ve some code like this:
with SomeContext(args):
<statement1>
.
.
<statementN>
I want this code to behave like this:
if some_condition(args):
f1()
else:
<statement1>
.
.
<statementN>
Statements in else block will also need an access to args.
But I want to hide f1
and some_condition
from user of this abstraction, so using an if-else block is out of option. Also I don’t want to force a constraint on the user to wrap all statements in a function. Is it possible to do some python magic in the with
context that allows this?
Yes, you can easily create such a context manager as follows.
import contextlib
@contextlib.contextmanager
def SomeContext(args):
if some_condition(args):
f1()
else:
yield
The user’s wrapped code gets executed at the point of the yield
. I don’t think it’s a problem that the context manager sometimes does not execute the user’s code, but I haven’t checked.
The closest I can get is to use two nested context managers, something like the following:
class SkippedException(Exception):
pass
class SkipContext:
def __enter__(self):
pass
def __exit__(self, type, value, tb):
return type is SkippedException
class SomeContext:
def __init__(self, arg):
self.arg = arg
def __enter__(self):
if self.arg == 1:
print "arg", self.arg
raise SkippedException()
def __exit__(self, type, value, tb):
pass
with SkipContext(), SomeContext(1):
print "body"
The SkipContext
manager essentially catches the SkippedException
raised by the inner SomeContext
manager in the case where arg == 1
.
Note that the syntax of multiple context expressions is only supported in Python 2.7 or later. In earlier versions, you would have to write:
with SkipContext():
with SomeContext(1):
print "body"
The contextlib.nested
context manager, despite claims in the documentation, doesn’t exactly match the semantics of the above nested with
statement when exceptions are thrown from within __enter__
, so it doesn’t work in this case.
It should be noted that PEP 343 mentions that macros (like context managers) that hide flow control should be discouraged, and references Raymond Chen’s rant against hidden flow control.