Skip to content

Commit bcc7227

Browse files
[3.13] gh-125038: Fix crash after genexpr.gi_frame.f_locals manipulations (GH-125178) (#125846)
(cherry picked from commit 079875e) Co-authored-by: Mikhail Efimov <efimov.mikhail@gmail.com>
1 parent 5bb0538 commit bcc7227

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
@@ -782,6 +782,7 @@ def foo(x):
782782
POP_TOP
783783
L1: RESUME 0
784784
LOAD_FAST 0 (.0)
785+
GET_ITER
785786
L2: FOR_ITER 10 (to L3)
786787
STORE_FAST 1 (z)
787788
LOAD_DEREF 2 (x)

Lib/test/test_generators.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,79 @@ def loop():
246246
#This should not raise
247247
loop()
248248

249+
250+
class ModifyUnderlyingIterableTest(unittest.TestCase):
251+
iterables = [
252+
range(0),
253+
range(20),
254+
[1, 2, 3],
255+
(2,),
256+
{13, 48, 211},
257+
frozenset((15, 8, 6)),
258+
{1: 2, 3: 4},
259+
]
260+
261+
non_iterables = [
262+
None,
263+
42,
264+
3.0,
265+
2j,
266+
]
267+
268+
def genexpr(self):
269+
return (x for x in range(10))
270+
271+
def genfunc(self):
272+
def gen(it):
273+
for x in it:
274+
yield x
275+
return gen(range(10))
276+
277+
def process_tests(self, get_generator):
278+
for obj in self.iterables:
279+
g_obj = get_generator(obj)
280+
with self.subTest(g_obj=g_obj, obj=obj):
281+
self.assertListEqual(list(g_obj), list(obj))
282+
283+
g_iter = get_generator(iter(obj))
284+
with self.subTest(g_iter=g_iter, obj=obj):
285+
self.assertListEqual(list(g_iter), list(obj))
286+
287+
err_regex = "'.*' object is not iterable"
288+
for obj in self.non_iterables:
289+
g_obj = get_generator(obj)
290+
with self.subTest(g_obj=g_obj):
291+
self.assertRaisesRegex(TypeError, err_regex, list, g_obj)
292+
293+
def test_modify_f_locals(self):
294+
def modify_f_locals(g, local, obj):
295+
g.gi_frame.f_locals[local] = obj
296+
return g
297+
298+
def get_generator_genexpr(obj):
299+
return modify_f_locals(self.genexpr(), '.0', obj)
300+
301+
def get_generator_genfunc(obj):
302+
return modify_f_locals(self.genfunc(), 'it', obj)
303+
304+
self.process_tests(get_generator_genexpr)
305+
self.process_tests(get_generator_genfunc)
306+
307+
def test_new_gen_from_gi_code(self):
308+
def new_gen_from_gi_code(g, obj):
309+
generator_func = types.FunctionType(g.gi_code, {})
310+
return generator_func(obj)
311+
312+
def get_generator_genexpr(obj):
313+
return new_gen_from_gi_code(self.genexpr(), obj)
314+
315+
def get_generator_genfunc(obj):
316+
return new_gen_from_gi_code(self.genfunc(), obj)
317+
318+
self.process_tests(get_generator_genexpr)
319+
self.process_tests(get_generator_genfunc)
320+
321+
249322
class ExceptionTest(unittest.TestCase):
250323
# Tests for the issue #23353: check that the currently handled exception
251324
# 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/compile.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5404,6 +5404,7 @@ compiler_sync_comprehension_generator(struct compiler *c, location loc,
54045404

54055405
if (IS_LABEL(start)) {
54065406
depth++;
5407+
ADDOP(c, LOC(gen->iter), GET_ITER);
54075408
USE_LABEL(c, start);
54085409
ADDOP_JUMP(c, LOC(gen->iter), FOR_ITER, anchor);
54095410
}

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