[Python-Dev] PEP 479

Chris Angelico rosuav at gmail.com
Sun Nov 30 03:31:33 CET 2014


On Sun, Nov 30, 2014 at 1:04 PM, Jim J. Jewett <jimjjewett at gmail.com> wrote:
> I have a strong suspicion that I'm missing something; I have been
> persuaded both directions too often to believe I have a grip on the
> real issue.
>
> So I'm putting out some assumptions; please tell me if I'm wrong, and
> maybe make them more explicit in the PEP.
>
> (1)  The change will only affect situations where StopIteration is
> currently raised as an Exception -- i.e., it leaks past the bounds of
> a loop.

Where a StopIteration would come up out of the generator. Inside the
generator function, it's exactly the same as it is in any other
function; you can raise it, you can catch it, everything's normal.

> (2)  This can happen because of an explicit raise StopIteration.  This
> is currently a supported idiom, and that is changing with PEP 479.

Correct. There is nothing that explicitly-raised StopIteration can do
in 3.0-3.4 that a return statement can't do in 3.0-3.7. There is the
downside that "raise StopIteration(value)" works on 2.7 where "return
value" is a syntax error; the PEP currently has no solution for this.

> (2a)  Generators in the unwind path will now need to catch and reraise.

More likely, catch and return; if your code was allowing "next(iter)"
to have the effect of potentially terminating the function, then you
now have to spell that "try: next(iter); except StopIteration:
return", which makes it clear that there's control flow here.

> (3)  It can also happen because of an explicit next statement (as
> opposed the the implicit next of a loop).
> This is currently supported; after PEP 479, the next statement should
> be wrapped in a try statement, so that the intent will be explicit.

Correct, as per previous point. As you say, the intent will be
explicit: take a value, and if there aren't any more, stop processing.

> (4)  It can happen because of "yield from" yielding from an iterator,
> rather than a generator?

No; as I understand it (though maybe I'm wrong too), "yield from" will
yield every value the other iterator yields, and will then quietly
emit a value if the iterator raises StopIteration, or will allow any
other exception to propagate. The StopIteration coming from the
iterator is absorbed by the "yield from" construct. To completely
propagate it out, "return (yield from iter)" should cover all three
results (yielded value, returned value, raised exception).

> (5)  There is no other case where this can happen?  (So the generator
> comprehension case won't matter unless it also includes one of the
> earlier cases.)

Correct. In a generator expression (I assume that's what you mean?),
the most likely way to leak a StopIteration is the "or stop()" hack,
which has always been at least slightly dubious, and is now going to
be actively rejected. Control flow in a generator expression is now
the same as in a comprehension, with no early-abort option; if you
want that, the best way is to break the expression into an out-of-line
generator function. This is now very similar to the restrictions on
lambda; you can't (eg) raise exceptions in a lambda function, and if
anyone comes to python-list asking how to do so, the best response is
"use def instead of lambda".

ChrisA


More information about the Python-Dev mailing list