Skipping execution of -with- block Skipping execution of -with- block python python

Skipping execution of -with- block


According to PEP-343, a with statement translates from:

with EXPR as VAR:    BLOCK

to:

mgr = (EXPR)exit = type(mgr).__exit__  # Not calling it yetvalue = type(mgr).__enter__(mgr)exc = Truetry:    try:        VAR = value  # Only if "as VAR" is present        BLOCK    except:        # The exceptional case is handled here        exc = False        if not exit(mgr, *sys.exc_info()):            raise        # The exception is swallowed if exit() returns truefinally:    # The normal and non-local-goto cases are handled here    if exc:        exit(mgr, None, None, None)

As you can see, there is nothing obvious you can do from the call to the __enter__() method of the context manager that can skip the body ("BLOCK") of the with statement.

People have done Python-implementation-specific things, such as manipulating the call stack inside of the __enter__(), in projects such as withhacks. I recall Alex Martelli posting a very interesting with-hack on stackoverflow a year or two back (don't recall enough of the post off-hand to search and find it).

But the simple answer to your question / problem is that you cannot do what you're asking, skipping the body of the with statement, without resorting to so-called "deep magic" (which is not necessarily portable between python implementations). With deep magic, you might be able to do it, but I recommend only doing such things as an exercise in seeing how it might be done, never in "production code".


If you want an ad-hoc solution that uses the ideas from withhacks (specifically from AnonymousBlocksInPython), this will work:

import sysimport inspectclass My_Context(object):    def __init__(self,mode=0):        """        if mode = 0, proceed as normal        if mode = 1, do not execute block        """        self.mode=mode    def __enter__(self):        if self.mode==1:            print 'Met block-skipping criterion ...'            # Do some magic            sys.settrace(lambda *args, **keys: None)            frame = inspect.currentframe(1)            frame.f_trace = self.trace    def trace(self, frame, event, arg):        raise    def __exit__(self, type, value, traceback):        print 'Exiting context ...'        return True

Compare the following:

with My_Context(mode=1):    print 'Executing block of code ...'

with

with My_Context(mode=0):    print 'Executing block of code ... '


A python 3 update to the hack mentioned by other answers fromwithhacks (specifically from AnonymousBlocksInPython):

class SkipWithBlock(Exception):    passclass SkipContextManager:    def __init__(self, skip):        self.skip = skip    def __enter__(self):        if self.skip:            sys.settrace(lambda *args, **keys: None)            frame = sys._getframe(1)            frame.f_trace = self.trace    def trace(self, frame, event, arg):        raise SkipWithBlock()    def __exit__(self, type, value, traceback):        if type is None:            return  # No exception        if issubclass(type, SkipWithBlock):            return True  # Suppress special SkipWithBlock exceptionwith SkipContextManager(skip=True):        print('In the with block')  # Won't be calledprint('Out of the with block')

As mentioned before by joe, this is a hack that should be avoided:

The method trace() is called when a new local scope is entered, i.e. right when the code in your with block begins. When an exception is raised here it gets caught by exit(). That's how this hack works. I should add that this is very much a hack and should not be relied upon. The magical sys.settrace() is not actually a part of the language definition, it just happens to be in CPython. Also, debuggers rely on sys.settrace() to do their job, so using it yourself interferes with that. There are many reasons why you shouldn't use this code. Just FYI.