Skip to content

Commit 079875e

Browse files
gh-125038: Fix crash after genexpr.gi_frame.f_locals manipulations (#125178)
1 parent 91ddde4 commit 079875e

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed

Lib/test/test_dis.py

+1
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,7 @@ def foo(x):
810810
POP_TOP
811811
L1: RESUME 0
812812
LOAD_FAST 0 (.0)
813+
GET_ITER
813814
L2: FOR_ITER 10 (to L3)
814815
STORE_FAST 1 (z)
815816
LOAD_DEREF 2 (x)

Lib/test/test_generators.py

+73
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,79 @@ def loop():
268268
#This should not raise
269269
loop()
270270

271+
272+
class ModifyUnderlyingIterableTest(unittest.TestCase):
273+
iterables = [
274+
range(0),
275+
range(20),
276+
[1, 2, 3],
277+
(2,),
278+
{13, 48, 211},
279+
frozenset((15, 8, 6)),
280+
{1: 2, 3: 4},
281+
]
282+
283+
non_iterables = [
284+
None,
285+
42,
286+
3.0,
287+
2j,
288+
]
289+
290+
def genexpr(self):
291+
return (x for x in range(10))
292+
293+
def genfunc(self):
294+
def gen(it):
295+
for x in it:
296+
yield x
297+
return gen(range(10))
298+
299+
def process_tests(self, get_generator):
300+
for obj in self.iterables:
301+
g_obj = get_generator(obj)
302+
with self.subTest(g_obj=g_obj, obj=obj):
303+
self.assertListEqual(list(g_obj), list(obj))
304+
305+
g_iter = get_generator(iter(obj))
306+
with self.subTest(g_iter=g_iter, obj=obj):
307+
self.assertListEqual(list(g_iter), list(obj))
308+
309+
err_regex = "'.*' object is not iterable"
310+
for obj in self.non_iterables:
311+
g_obj = get_generator(obj)
312+
with self.subTest(g_obj=g_obj):
313+
self.assertRaisesRegex(TypeError, err_regex, list, g_obj)
314+
315+
def test_modify_f_locals(self):
316+
def modify_f_locals(g, local, obj):
317+
g.gi_frame.f_locals[local] = obj
318+
return g
319+
320+
def get_generator_genexpr(obj):
321+
return modify_f_locals(self.genexpr(), '.0', obj)
322+
323+
def get_generator_genfunc(obj):
324+
return modify_f_locals(self.genfunc(), 'it', obj)
325+
326+
self.process_tests(get_generator_genexpr)
327+
self.process_tests(get_generator_genfunc)
328+
329+
def test_new_gen_from_gi_code(self):
330+
def new_gen_from_gi_code(g, obj):
331+
generator_func = types.FunctionType(g.gi_code, {})
332+
return generator_func(obj)
333+
334+
def get_generator_genexpr(obj):
335+
return new_gen_from_gi_code(self.genexpr(), obj)
336+
337+
def get_generator_genfunc(obj):
338+
return new_gen_from_gi_code(self.genfunc(), obj)
339+
340+
self.process_tests(get_generator_genexpr)
341+
self.process_tests(get_generator_genfunc)
342+
343+
271344
class ExceptionTest(unittest.TestCase):
272345
# Tests for the issue #23353: check that the currently handled exception
273346
# is correctly saved/restored in PyEval_EvalFrameEx().
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash when iterating over a generator expression after direct changes on ``gi_frame.f_locals``.
2+
Patch by Mikhail Efimov.

Python/codegen.c

+1
Original file line numberDiff line numberDiff line change
@@ -4164,6 +4164,7 @@ codegen_sync_comprehension_generator(compiler *c, location loc,
41644164

41654165
if (IS_JUMP_TARGET_LABEL(start)) {
41664166
depth++;
4167+
ADDOP(c, LOC(gen->iter), GET_ITER);
41674168
USE_LABEL(c, start);
41684169
ADDOP_JUMP(c, LOC(gen->iter), FOR_ITER, anchor);
41694170
}

0 commit comments

Comments
 (0)