with ignored

Ian Kelly ian.g.kelly at gmail.com
Mon Apr 8 00:53:05 EDT 2013


On Sun, Apr 7, 2013 at 6:40 PM, Barrett Lewis <musikal.fusion at gmail.com> wrote:
> I was recently watching that Raymond Hettinger video on creating Beautiful
> Python from this years PyCon.
> He mentioned pushing up the new idiom
>
> with ignored(<ignored_exceptions>):
>      # do some work
>
> I tracked down his commit here http://hg.python.org/cpython/rev/406b47c64480
>
> But am unsure how the yield works in the given situation.
>
> I know about creating generators with yield and have read the docs on how it
> maintains state.
>
> I think it works because it is returning control back to the caller while
> maintaining the try so if the caller throws it is caught by the context. Is
> this correct? I would love an in depth explanation of how this is working. I
> am trying to learn as much as possible about the actual python internals.

The first thing to understand is that ignored() is a context manager
and how context managers work.  A context manager is an object with an
__enter__ method and an __exit__ method.  For example, the ignored()
context manager could have been written as:

class ignored:
    def __init__(self, *exceptions):
        self.__exceptions = exceptions
    def __enter__(self):
        return
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is not None:
            return issubclass(exc_type, self.__exceptions)

The __enter__ method is called when the with block is entered and sets
up the context.  The __exit__ method is called when the with block is
exited and is passed the exception if one was raised.  It returns True
to suppress the exception, or False to continue propagating it.

However, ignored() is actually implemented as a generator function
with the @contextmanager decorator shortcut.  This decorator takes a
generator function and wraps it up as a class with the necessary
__enter__ and __exit__ methods.  The __enter__ method in this case
calls the .next() method of the generator and returns after it yields
once.  The __exit__ method calls it again -- or it calls the .throw()
method if an exception was passed in -- and expects the generator to
exit as a result.

So from the perspective of the generator it does its context setup (in
this case, setting up a try block) prior to the yield, and then does
the cleanup (in this case, selectively catching and suppressing the
exceptions) after the yield.



More information about the Python-list mailing list