New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"yield from" kills generator on re-entry #58428
Comments
Based on the existing "test_attempted_yield_from_loop" in Lib/test/test_pep380.py, I wrote this test and I wonder why it does not work: """
def test_attempted_reentry():
"""
>>> for line in test_attempted_reentry(): print(line)
g1: starting
Yielded: y1
g1: about to yield from g2
g2: starting
Yielded: y2
g2: about to yield from g1
g2: caught ValueError
Yielded: y3
g1: after delegating to g2
Yielded: y4
"""
trace = []
def g1():
trace.append("g1: starting")
yield "y1"
trace.append("g1: about to yield from g2")
yield from g2()
trace.append("g1: after delegating to g2")
yield "y4" def g2():
trace.append("g2: starting")
yield "y2"
trace.append("g2: about to yield from g1")
try:
yield from gi
except ValueError:
trace.append("g2: caught ValueError")
else:
trace.append("g1 did not raise ValueError on reentry")
yield "y3"
gi = g1()
for y in gi:
trace.append("Yielded: %s" % (y,))
return trace
""" In current CPython, I get this: """ Even though I catch the ValueError (raised on generator reentry) at the position where I run the "yield from", the outer generator (g1) does not continue to run after the termination of g2. It shouldn't normally have an impact on the running g1 that someone attempts to jump back into it, but it clearly does here. I noticed this while trying to adapt the implementation for Cython, because the original test was one of the few failing cases and it made the code jump through the generator support code quite wildly. |
Here is an analysis of this (less verbose) code: def g1():
yield "y1"
yield from g2()
yield "y4"
def g2():
yield "y2"
try:
yield from gi
except ValueError:
pass # catch "already running" error
yield "y3"
gi = g1()
for y in gi:
print("Yielded: %s" % (y,)) This is what it currently does:
Ok so far. Now:
Effect: "y4" is not yielded anymore. The problem is in steps 5) and 6), which are handled by g1 at the wrong call level. They shouldn't lead to undelegation and termination in g1, just to an exception being raised in g2. |
Added Mark Shannon to the nosy list - he's been tinkering with this area of the interpreter lately. This definitely needs to be fixed though (even if that does mean major surgery on the implementation, up to and including the introduction of an __iter_from__ method) |
Don't worry! I'll be fixing it in a moment... |
New changeset 3357eac1ba62 by Benjamin Peterson in branch 'default': |
Ah, yes, that should work in both implementations. I'll give it a try. Thanks! |
I've just added bpo-14230 which overlaps with this issue somewhat. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: