[Python-ideas] Possible PEP 380 tweak

Jacob Holm jh at improva.dk
Fri Oct 29 12:13:16 CEST 2010

On 2010-10-29 09:18, Greg Ewing wrote:
> I've been pondering the whole close()-returning-a-value
> thing I've convinced myself once again that it's a bad
> idea.

And I still believe we could have made it work.  However, I have been
doing my own thinking about the whole of PEP 380, PEP 3152, for-loop
co-iteration and so on.  And I think I have an idea that improves the
whole story.

The main thing to note is that the expression form of yield-from is
mostly intended to make it easier to have cofunctions that return a
value, and that there is a problem with reusing StopIteration for that
purpose.  Now that we have an actual PEP 3152, we could choose to move
the necessary support over there.  Here is one way to do that:

1)  Drop the current PEP 380 support for using "return <value>" inside a
generator.  That means no extended StopIteration and no expression form
of "yield from".  And since there are no return values, there is no
problem with how "close" should treat them.

2)  In PEP 3152, define "return <value>" in a cofunction to raise a new
IterationResult exception with the value.  (And treat falling off the
edge of the function or returning without a value as "return None")

3)  In PEP 3152, change the "cocall" expansion so that:

    <val> = cocall f(*args, **kwargs)

Expands to:

        yield from f.__cocall__(*args, **kwargs)
    except IterationResult as e:
        <val> = e.value
        raise StopIteration

(The same expansion would be used if cocalls are implicit of course).
This ensures that a cofunction can raise StopIteration just as a regular
function, which means we can extend the iterator protocol to support
cofunctions with only minor changes.

An interesting variation might be to keep the expression form of
yield-from, but change its semantics so that it returns the
StopIteration instance that was caught, instead of trying to extract a
value.   Then adding an IterationResult inheriting from StopIteration
and using it for "return <value>" in a generator.

That would make all current yield-from examples work with the minor
change that the old:

  <var> = yield from <expr>

would need to be written as

  <var> = (yield from <expr>).value

And would have the benefit that the PEP 3152 expansion could reraise the
actual StopIteration as in:

  e = yield from f.__cocall__(*args, **kwargs)
  if isinstance(e, IterationResult):
      <var> = e.value
      raise e

The idea of returning the exception takes some getting used to, but it
solves the problem with StopIteration and cofunctions, and I'm sure I
can find some interesting uses for it by itself.

Anyway....  Thoughts?

- Jacob

More information about the Python-ideas mailing list