Skip to content

Commit 8fb36ff

Browse files
DanielNoordhauntsaninja
authored andcommitted
[mypyc] Fix crash on double yielding Iterators (#21826)
Closes mypyc/mypyc#1210 This fixes another issue I ran into while trying to use `mypyc` for `isort`. I am aware the contributing guidelines say new contributors are not encouraged to use LLMs but I hope this can get the same handling as #21785. I have tried to ensure this patch works as much as I can by: Testing locally with `pytest mypyc/test/test_run.py `. Tested that the test fails on `master` without the changes. Also tried to run CI on my local fork (DanielNoord#2), which succeeded. As with the previous PR, feel free to push changes to the branch or cherry pick this into another branch. I just want to unblock `isort` :)
1 parent 1522c88 commit 8fb36ff

2 files changed

Lines changed: 22 additions & 1 deletion

File tree

mypyc/irbuild/generator.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ def setup_generator_class(builder: IRBuilder) -> ClassIR:
166166
builder.fn_info.env_class = generator_class_ir
167167
else:
168168
generator_class_ir.attributes[ENV_ATTR_NAME] = RInstance(builder.fn_info.env_class)
169+
if not builder.fn_info.fitem.is_coroutine:
170+
# After completion generators still need generator.__mypyc_env__ for subsequent
171+
# __next__() calls to observe the terminal next-label and raise StopIteration.
172+
# Coroutines can't be resumed after completion, so keeping the environment alive
173+
# there would just extend local lifetimes unnecessarily.
174+
generator_class_ir.attrs_to_keep_alive_on_completion.add(ENV_ATTR_NAME)
169175

170176
builder.classes.append(generator_class_ir)
171177
return generator_class_ir

mypyc/test-data/run-generators.test

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Test cases for generators and yield (compile and run)
22

33
[case testYield]
4-
from typing import Generator, Iterable, Union, Tuple, Dict
4+
from typing import Callable, Dict, Generator, Iterable, Iterator, Tuple, Union
55

66
def yield_three_times() -> Iterable[int]:
77
yield 1
@@ -87,6 +87,19 @@ def return_tuple() -> Generator[int, None, Tuple[int, int]]:
8787
yield 0
8888
return 1, 2
8989

90+
def call_lambda(fn: Callable[[], None]) -> None:
91+
fn()
92+
93+
def get_iterator() -> Iterator[str]:
94+
call_lambda(lambda: None)
95+
yield ""
96+
97+
def yield_twice_via_generator_reuse() -> Iterator[str]:
98+
identified_imports = get_iterator()
99+
yield from identified_imports
100+
for identified_import in identified_imports:
101+
yield identified_import
102+
90103
[file driver.py]
91104
from native import (
92105
yield_three_times,
@@ -99,6 +112,7 @@ from native import (
99112
A,
100113
return_tuple,
101114
yield_dict_methods,
115+
yield_twice_via_generator_reuse,
102116
)
103117
from testutil import run_generator
104118
from collections import defaultdict
@@ -114,6 +128,7 @@ assert run_generator(A(0).generator()) == ((0,), None)
114128
assert run_generator(return_tuple()) == ((0,), (1, 2))
115129
assert run_generator(yield_dict_methods({}, {}, {})) == ((), None)
116130
assert run_generator(yield_dict_methods({1: 2}, {3: 4}, {5: 6})) == ((1, 3, 4, 6), None)
131+
assert run_generator(yield_twice_via_generator_reuse()) == (("",), None)
117132
dd = defaultdict(int, {0: 1})
118133
assert run_generator(yield_dict_methods(dd, dd, dd)) == ((0, 0, 1, 1), None)
119134

0 commit comments

Comments
 (0)