[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
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/)
More information about the Python-Dev
mailing list