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

Jacob Holm jh at improva.dk
Fri Mar 20 02:04:42 CET 2009


Greg Ewing wrote:
> Jacob Holm wrote:
>
>>   2. In the comment for "gen_undelegate" you mention "certain recursive
>>      situations" where a generator may lose its frame before we get a
>>      chance to clear f_yieldfrom. Can you elaborate?
>
> I can't remember the details, but I definitely ran into one
> during development, which is why I added that function. Have
> you tried running all of my tests?
Yup.  All tests pass, except for your test19 where my traceback is 
different.

> --- expected/test19.py.out 2009-02-22 09:51:26.000000000 +0100 +++ 
> actual/test19.py.out 2009-03-20 01:50:28.000000000 +0100 @@ -7,8 +7,8 
> @@ Traceback (most recent call last): File "test19.py", line 20, in 
> <module> for y in gi: - File "test19.py", line 16, in g2 - yield from 
> gi File "test19.py", line 9, in g1 yield from g2() + File "test19.py", 
> line 16, in g2 + yield from gi ValueError: generator already executing 

I am not quite sure why that is, but I actually think mine is better.

>>   3. It looks like you are not calling "close" properly from "next",
>>      "send" and "throw".
>
> I'm not sure what you mean by that. Can you provide an
> example that doesn't behave as expected?
Sure, see below.

>>   4. It looks like your "gen_close" does not try to throw a
>>      GeneratorExit before calling close when delegating to a
>>      non-generator.
>
> I'm not sure what you mean here either. Regardless of the
> type of sub-iterator, it should end up getting to the
> part which does
>
>     if (!PyErr_Occurred())
>         PyErr_SetNone(PyExc_GeneratorExit);
>
> Again, and example that doesn't behave properly would
> help.
>
Of course.   Here is a demonstration/test...

class iterator(object):
    """Simple iterator that counts to n while writing what is done to it"""

    def __init__(self, n):
        self.ctr = iter(xrange(n))

    def __iter__(self):
        return self

    def close(self):
        print "Close"

    def next(self):
        print "Next"
        return self.ctr.next()

    def send(self, val):
        print "Send", val
        return self.ctr.next()

    def throw(self, *args):
        print "Throw:", args
        return self.ctr.next()


def generator(n):
    yield from iterator(n)


g = generator(1)
g.next()
try:
    g.next()
except Exception, e:
    print type(e)
else:
    print 'No exception'
del g
print '--'

g = generator(1)
g.next()
try:
    g.send(1)
except Exception, e:
    print type(e)
else:
    print 'No exception'
del g
print '--'

g = generator(1)
g.next()
try:
    g.throw(ValueError)
except Exception, e:
    print type(e)
else:
    print 'No exception'
del g
print '--'

g = generator(2)
g.next()
try:
    g.next()
except Exception, e:
    print type(e)
else:
    print 'No exception'
del g
print '--'

g = generator(2)
g.next()
try:
    g.send(1)
except Exception, e:
    print type(e)
else:
    print 'No exception'
del g
print '--'

g = generator(2)
g.next()
try:
    g.throw(ValueError)
except Exception, e:
    print type(e)
else:
    print 'No exception'
del g
print '--'



And here is the output I would expect based on the relevant PEPs.

Next
Next
Close
<type 'exceptions.StopIteration'>
--
Next
Send 1
Close
<type 'exceptions.StopIteration'>
--
Next
Throw: (<type 'exceptions.ValueError'>,)
Close
<type 'exceptions.StopIteration'>
--
Next
Next
No exception
Throw: (<type 'exceptions.GeneratorExit'>,)
Close
--
Next
Send 1
No exception
Throw: (<type 'exceptions.GeneratorExit'>,)
Close
--
Next
Throw: (<type 'exceptions.ValueError'>,)
No exception
Throw: (<type 'exceptions.GeneratorExit'>,)
Close
--


However, when I run this using your patch, the first 3 "Close" messages, 
and the 3 "GeneratorExit" messages are missing.

Did that help?

- Jacob




More information about the Python-ideas mailing list