11import doctest
22import textwrap
3+ import types
34import unittest
45
56
9293
9394
9495class ListComprehensionTest (unittest .TestCase ):
95- def _check_in_scopes (self , code , outputs = None , ns = None , scopes = None , raises = ()):
96+ def _check_in_scopes (self , code , outputs = None , ns = None , scopes = None , raises = (),
97+ exec_func = exec ):
9698 code = textwrap .dedent (code )
9799 scopes = scopes or ["module" , "class" , "function" ]
98100 for scope in scopes :
@@ -119,7 +121,7 @@ def get_output(moddict, name):
119121 return moddict [name ]
120122 newns = ns .copy () if ns else {}
121123 try :
122- exec (newcode , newns )
124+ exec_func (newcode , newns )
123125 except raises as e :
124126 # We care about e.g. NameError vs UnboundLocalError
125127 self .assertIs (type (e ), raises )
@@ -613,6 +615,45 @@ def test_frame_locals(self):
613615 import sys
614616 self ._check_in_scopes (code , {"val" : 0 }, ns = {"sys" : sys })
615617
618+ def _recursive_replace (self , maybe_code ):
619+ if not isinstance (maybe_code , types .CodeType ):
620+ return maybe_code
621+ return maybe_code .replace (co_consts = tuple (
622+ self ._recursive_replace (c ) for c in maybe_code .co_consts
623+ ))
624+
625+ def _replacing_exec (self , code_string , ns ):
626+ co = compile (code_string , "<string>" , "exec" )
627+ co = self ._recursive_replace (co )
628+ exec (co , ns )
629+
630+ def test_code_replace (self ):
631+ code = """
632+ x = 3
633+ [x for x in (1, 2)]
634+ dir()
635+ y = [x]
636+ """
637+ self ._check_in_scopes (code , {"y" : [3 ], "x" : 3 })
638+ self ._check_in_scopes (code , {"y" : [3 ], "x" : 3 }, exec_func = self ._replacing_exec )
639+
640+ def test_code_replace_extended_arg (self ):
641+ num_names = 300
642+ assignments = "; " .join (f"x{ i } = { i } " for i in range (num_names ))
643+ name_list = ", " .join (f"x{ i } " for i in range (num_names ))
644+ expected = {
645+ "y" : list (range (num_names )),
646+ ** {f"x{ i } " : i for i in range (num_names )}
647+ }
648+ code = f"""
649+ { assignments }
650+ [({ name_list } ) for { name_list } in (range(300),)]
651+ dir()
652+ y = [{ name_list } ]
653+ """
654+ self ._check_in_scopes (code , expected )
655+ self ._check_in_scopes (code , expected , exec_func = self ._replacing_exec )
656+
616657
617658__test__ = {'doctests' : doctests }
618659
0 commit comments