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