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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 73 additions & 0 deletions
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().
Lines changed: 2 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy