[Python-Dev] PEP 377 - allow __enter__() methods to skip the statement body
Guido van Rossum
guido at python.org
Mon Mar 16 19:06:01 CET 2009
Moreover, since the main use case seems to be fixing a corner case of
the nested() context manager, perhaps the effort towards changing the
language would be better directed towards supporting "with a, b:" as a
shorthand for "with a: with b:" .
On Mon, Mar 16, 2009 at 10:01 AM, Guido van Rossum <guido at python.org> wrote:
> 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
> protocol.
>
> 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?
>
> --Guido
>
> 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/)
>
--
--Guido van Rossum (home page: http://www.python.org/~guido/)
More information about the Python-Dev
mailing list