1# Test the most dynamic corner cases of Python's runtime semantics. 2 3import builtins 4import sys 5import unittest 6 7from test.support import swap_item, swap_attr 8 9 10class RebindBuiltinsTests(unittest.TestCase): 11 12 """Test all the ways that we can change/shadow globals/builtins.""" 13 14 def configure_func(self, func, *args): 15 """Perform TestCase-specific configuration on a function before testing. 16 17 By default, this does nothing. Example usage: spinning a function so 18 that a JIT will optimize it. Subclasses should override this as needed. 19 20 Args: 21 func: function to configure. 22 *args: any arguments that should be passed to func, if calling it. 23 24 Returns: 25 Nothing. Work will be performed on func in-place. 26 """ 27 pass 28 29 def test_globals_shadow_builtins(self): 30 # Modify globals() to shadow an entry in builtins. 31 def foo(): 32 return len([1, 2, 3]) 33 self.configure_func(foo) 34 35 self.assertEqual(foo(), 3) 36 with swap_item(globals(), "len", lambda x: 7): 37 self.assertEqual(foo(), 7) 38 39 def test_modify_builtins(self): 40 # Modify the builtins module directly. 41 def foo(): 42 return len([1, 2, 3]) 43 self.configure_func(foo) 44 45 self.assertEqual(foo(), 3) 46 with swap_attr(builtins, "len", lambda x: 7): 47 self.assertEqual(foo(), 7) 48 49 def test_modify_builtins_while_generator_active(self): 50 # Modify the builtins out from under a live generator. 51 def foo(): 52 x = range(3) 53 yield len(x) 54 yield len(x) 55 self.configure_func(foo) 56 57 g = foo() 58 self.assertEqual(next(g), 3) 59 with swap_attr(builtins, "len", lambda x: 7): 60 self.assertEqual(next(g), 7) 61 62 def test_modify_builtins_from_leaf_function(self): 63 # Verify that modifications made by leaf functions percolate up the 64 # callstack. 65 with swap_attr(builtins, "len", len): 66 def bar(): 67 builtins.len = lambda x: 4 68 69 def foo(modifier): 70 l = [] 71 l.append(len(range(7))) 72 modifier() 73 l.append(len(range(7))) 74 return l 75 self.configure_func(foo, lambda: None) 76 77 self.assertEqual(foo(bar), [7, 4]) 78 79 def test_cannot_change_globals_or_builtins_with_eval(self): 80 def foo(): 81 return len([1, 2, 3]) 82 self.configure_func(foo) 83 84 # Note that this *doesn't* change the definition of len() seen by foo(). 85 builtins_dict = {"len": lambda x: 7} 86 globals_dict = {"foo": foo, "__builtins__": builtins_dict, 87 "len": lambda x: 8} 88 self.assertEqual(eval("foo()", globals_dict), 3) 89 90 self.assertEqual(eval("foo()", {"foo": foo}), 3) 91 92 def test_cannot_change_globals_or_builtins_with_exec(self): 93 def foo(): 94 return len([1, 2, 3]) 95 self.configure_func(foo) 96 97 globals_dict = {"foo": foo} 98 exec("x = foo()", globals_dict) 99 self.assertEqual(globals_dict["x"], 3) 100 101 # Note that this *doesn't* change the definition of len() seen by foo(). 102 builtins_dict = {"len": lambda x: 7} 103 globals_dict = {"foo": foo, "__builtins__": builtins_dict, 104 "len": lambda x: 8} 105 106 exec("x = foo()", globals_dict) 107 self.assertEqual(globals_dict["x"], 3) 108 109 def test_cannot_replace_builtins_dict_while_active(self): 110 def foo(): 111 x = range(3) 112 yield len(x) 113 yield len(x) 114 self.configure_func(foo) 115 116 g = foo() 117 self.assertEqual(next(g), 3) 118 with swap_item(globals(), "__builtins__", {"len": lambda x: 7}): 119 self.assertEqual(next(g), 3) 120 121 def test_cannot_replace_builtins_dict_between_calls(self): 122 def foo(): 123 return len([1, 2, 3]) 124 self.configure_func(foo) 125 126 self.assertEqual(foo(), 3) 127 with swap_item(globals(), "__builtins__", {"len": lambda x: 7}): 128 self.assertEqual(foo(), 3) 129 130 def test_eval_gives_lambda_custom_globals(self): 131 globals_dict = {"len": lambda x: 7} 132 foo = eval("lambda: len([])", globals_dict) 133 self.configure_func(foo) 134 135 self.assertEqual(foo(), 7) 136 137 def test_load_global_specialization_failure_keeps_oparg(self): 138 # https://github.com/python/cpython/issues/91625 139 class MyGlobals(dict): 140 def __missing__(self, key): 141 return int(key.removeprefix("_number_")) 142 143 code = "lambda: " + "+".join(f"_number_{i}" for i in range(1000)) 144 sum_1000 = eval(code, MyGlobals()) 145 expected = sum(range(1000)) 146 # Warm up the the function for quickening (PEP 659) 147 for _ in range(30): 148 self.assertEqual(sum_1000(), expected) 149 150 151class TestTracing(unittest.TestCase): 152 153 def setUp(self): 154 self.addCleanup(sys.settrace, sys.gettrace()) 155 sys.settrace(None) 156 157 def test_after_specialization(self): 158 159 def trace(frame, event, arg): 160 return trace 161 162 turn_on_trace = False 163 164 class C: 165 def __init__(self, x): 166 self.x = x 167 def __del__(self): 168 if turn_on_trace: 169 sys.settrace(trace) 170 171 def f(): 172 # LOAD_GLOBAL[_BUILTIN] immediately follows the call to C.__del__ 173 C(0).x, len 174 175 def g(): 176 # BINARY_SUSCR[_LIST_INT] immediately follows the call to C.__del__ 177 [0][C(0).x] 178 179 def h(): 180 # BINARY_OP[_ADD_INT] immediately follows the call to C.__del__ 181 0 + C(0).x 182 183 for func in (f, g, h): 184 with self.subTest(func.__name__): 185 for _ in range(58): 186 func() 187 turn_on_trace = True 188 func() 189 sys.settrace(None) 190 turn_on_trace = False 191 192 193if __name__ == "__main__": 194 unittest.main() 195