@@ -530,6 +530,34 @@ def g(n):
530530 self .assertEqual (g .__code__ .co_cellvars , ("x" ,))
531531 self .assertEqual ([fn () for fn in g (2 )[1 ]], [1 , 1 ])
532532
533+ def test_nested_inlined_async_comp_iter_var_is_fast_local (self ):
534+ import asyncio
535+
536+ async def agen (n ):
537+ for i in range (n ):
538+ yield i
539+
540+ async def f (n ):
541+ return [[x async for _ in agen (2 )] async for x in agen (n )]
542+
543+ self .assertEqual (f .__code__ .co_cellvars , ())
544+ self .assertEqual (asyncio .run (f (2 )), [[0 , 0 ], [1 , 1 ]])
545+
546+ async def g (n ):
547+ return [[(lambda : x ) async for _ in agen (2 )] async for x in agen (n )]
548+
549+ self .assertEqual (g .__code__ .co_cellvars , ("x" ,))
550+ out = asyncio .run (g (2 ))
551+ self .assertEqual ([fn () for fn in out [1 ]], [1 , 1 ])
552+
553+ def test_inlined_comprehension_name_mangling_in_method_scope (self ):
554+ class C :
555+ def f (self ):
556+ __x = 42
557+ return [__x for _ in (0 ,)]
558+
559+ self .assertEqual (C ().f (), [42 ])
560+
533561 def test_nested_references___class__ (self ):
534562 code = """
535563 res = [[__class__ for _ in (0,)] for _ in (1,)]
@@ -914,6 +942,46 @@ def inner():
914942 {"snaps" : [1 , 2 ], "vals" : [2 , 2 ], "consistent" : [True , True ]},
915943 ns = {"sys" : sys }, scopes = ["module" , "function" ])
916944
945+ def test_frame_locals_nested_comp_cell_and_enclosing_free (self ):
946+ # Stress a nested inlined shape where a comp cell and enclosing free
947+ # share a name; all f_locals views must stay consistent.
948+ code = """
949+ def outer(x):
950+ def inner():
951+ return [(
952+ lambda: x,
953+ [[x for _ in (0,)] for _ in (0,)][0][0],
954+ dict(**sys._getframe().f_locals),
955+ len(sys._getframe().f_locals),
956+ list(sys._getframe().f_locals.keys()),
957+ list(sys._getframe().f_locals.values()),
958+ list(sys._getframe().f_locals.items()),
959+ dict(sys._getframe().f_locals.items()))
960+ for x in x]
961+ return inner()
962+ result = outer([1, 2])
963+ snaps = [d['x'] for _, _, d, *_ in result]
964+ vals = [fn() for fn, *_ in result]
965+ nested_vals = [nested for _, nested, *_ in result]
966+ consistent = []
967+ for _, _, d, n, ks, vs, it, d_items in result:
968+ consistent.append(
969+ n == len(ks) == len(vs) == len(it)
970+ and ks.count('x') == 1
971+ and d == d_items == dict(zip(ks, vs))
972+ )
973+ """
974+ import sys
975+ self ._check_in_scopes (
976+ code ,
977+ {
978+ "snaps" : [1 , 2 ],
979+ "vals" : [2 , 2 ],
980+ "nested_vals" : [1 , 2 ],
981+ "consistent" : [True , True ],
982+ },
983+ ns = {"sys" : sys }, scopes = ["module" , "function" ])
984+
917985 def _recursive_replace (self , maybe_code ):
918986 if not isinstance (maybe_code , types .CodeType ):
919987 return maybe_code
0 commit comments