[Python-Dev] PEP 377 - allow __enter__() methods to skip the statement body

Guido van Rossum guido at python.org
Mon Mar 16 18:01:09 CET 2009

I have no right to speak because I haven't read through all the
details of the proposal, but reading this I am very sad that we have
to introduce a whole new exception (and one with special status as
well) in order to fix such a niggly corner case of the context manager

Since IIUC the original context manager design was intended to have
exactly one yield in the body of the context manager -- can't we just
declare fewer (or more) yields an error and rase an appropriate
TypeError or something?


On Mon, Mar 16, 2009 at 4:43 AM, Nick Coghlan <ncoghlan at gmail.com> wrote:
> Michael Foord wrote:
>> Well, StopIteration is still an implementation detail that only
>> occasionally bleeds through to actual programming. It says nothing about
>> whether using exceptions for non-exceptional circumstances (control
>> flow) is good practise. Personally I think it makes the intent of code
>> less easy to understand - in effect the exceptions *are* being used as a
>> goto.
> Note that raising SkipStatement manually is likely to be even rarer than
> raising StopIteration. Catching it should almost never happen other than
> implicitly inside a with statement (that's the reason I made it a peer
> of SystemExit and GeneratorExit rather than a peer of StopIteration).
> It is primarily proposed as a way for contextlib.contextmanager to tell
> the interpreter that the underlying generator didn't yield, so the body
> of the with statement should be skipped completely. It just so happens
> that manually implemented context managers will also be free to use it
> if they need to for some reason.
> An alternative approach worth considering may be to use NotImplemented
> as a model instead of StopIteration. With that approach, instead of
> having SkipStatement be an exception, have it be a singleton that can be
> returned from __enter__ to indicate that the with statement body would
> be skipped.
> That has a big advantage over using an exception when it comes to
> execution speed. The statement semantics in that case would become:
>    mgr = (EXPR)
>    exit = mgr.__exit__  # Not calling it yet
>    value = mgr.__enter__()
>    if value is not SkipStatement:
>        exc = True
>        try:
>            try:
>                VAR = value  # Only if "as VAR" is present
>                BLOCK
>            except:
>                # The exceptional case is handled here
>                exc = False
>                if not exit(*sys.exc_info()):
>                    raise
>                # The exception is swallowed if exit() returns true
>        finally:
>            # The normal and non-local-goto cases are handled here
>            if exc:
>                exit(None, None, None)
> (keeping in mind that I already plan to change PEP 377 to drop the idea
> of assigning anything to VAR when the statement body is skipped)
> The major drawback of that approach is that it becomes a little trickier
> to write a context manager like nested() correctly - it would need to
> check all of the __enter__() return values and start unwinding the
> context manager stack if it encountered SkipStatement. The fix isn't
> particularly complicated*, but it does contrast with the fact that
> having SkipStatement as an exception means that the current
> implementation of nested() will "just work" with the new semantics.
> Cheers,
> Nick.
> * For reference, to support a "SkipStatement as return value" approach
> the main loop in nested() would have to change from this:
>    for mgr in managers:
>        exit = mgr.__exit__
>        enter = mgr.__enter__
>        vars.append(enter())
>        exits.append(exit)
>    yield vars
> To this:
>    for mgr in managers:
>        exit = mgr.__exit__
>        enter = mgr.__enter__
>        var = enter()
>        if var is SkipStatement:
>            break
>        vars.append(var)
>        exits.append(exit)
>    else:
>        yield vars
> As mentioned above, if SkipStatement is an exception then nested() works
> correctly without any changes.
> --
> Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
> ---------------------------------------------------------------
> _______________________________________________
> Python-Dev mailing list
> Python-Dev at python.org
> http://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: http://mail.python.org/mailman/options/python-dev/guido%40python.org

--Guido van Rossum (home page: http://www.python.org/~guido/)

More information about the Python-Dev mailing list