[Python-ideas] Revised**12 PEP on Yield-From

Erik Groeneveld erik at cq2.org
Thu Apr 23 11:31:03 CEST 2009


Greg, Jacob, Jim,

> No, g never raises a ValueError here, because the raise
> statement is not reached.
>
> You need to wrap the yield in a try-except to catch the
> GeneratorExit.

Thanks for signaling this.  The example was wrong.  I was confused by
my code implicitly turning a GeneratorExit into StopIteration. In my
unittests I have:

        def f2():
            try:
                yield
            except GeneratorExit:
                pass
            # implicit raise StopIteration here

and I added a duplicate to test the problem more explicitly:

        def f3():
            try:
                yield
            except GeneratorExit:
                pass
            yield  # does not raise an exception but yields None

Thanks a lot, I was now able to complete all unittests and code
according to the new PEP.

There is one problem left however.  The code dealing with
GeneratorExit has to undo the work of close() a bit.  To account for:

      "If this call results in an exception, it is propagated to the delegating
      generator. Otherwise, GeneratorExit is raised in the delegating
generator."

and knowing that close() raises a RuntimeError when a generator
ignores GeneratorExit, I have:

                    try:
                        generator.close()
                    except RuntimeError:
                        pass
                    raise GeneratorExit

But this code cannot tell if the generator intended to raise a
RuntimeError.  Indeed, I can't make this test work with RuntimeError
(see commented lines):

        msg = []
        def f8():
            try:
                yield f9()
            #except RuntimeError, e:
            except ValueError, e:
                msg.append(str(e))
                raise StopIteration()
        def f9():
            try:
                yield
            except GeneratorExit:
                msg.append('GeneratorExit turned into ValueError')
                #raise RuntimeError('stop here')
                raise ValueError('stop here')
            yield

        g8 = compose(f8())
        g8.next()
        try:
            g8.throw(GeneratorExit())
            self.fail('must raise StopIteration')
        except StopIteration:
            pass
        self.assertEquals(['GeneratorExit turned into ValueError',
'stop here'], msg)

I wonder what you think about this and how to get this right.

Erik



More information about the Python-ideas mailing list