1
1
import doctest
2
2
import textwrap
3
+ import types
3
4
import unittest
4
5
5
6
92
93
93
94
94
95
class 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 ):
96
98
code = textwrap .dedent (code )
97
99
scopes = scopes or ["module" , "class" , "function" ]
98
100
for scope in scopes :
@@ -119,7 +121,7 @@ def get_output(moddict, name):
119
121
return moddict [name ]
120
122
newns = ns .copy () if ns else {}
121
123
try :
122
- exec (newcode , newns )
124
+ exec_func (newcode , newns )
123
125
except raises as e :
124
126
# We care about e.g. NameError vs UnboundLocalError
125
127
self .assertIs (type (e ), raises )
@@ -613,6 +615,45 @@ def test_frame_locals(self):
613
615
import sys
614
616
self ._check_in_scopes (code , {"val" : 0 }, ns = {"sys" : sys })
615
617
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
+
616
657
617
658
__test__ = {'doctests' : doctests }
618
659
0 commit comments