1 # Test the most dynamic corner cases of Python's runtime semantics.
2
3 import builtins
4 import sys
5 import unittest
6
7 from test.support import swap_item, swap_attr
8
9
10 class ESC[4;38;5;81mRebindBuiltinsTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
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
151 class 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
193 if __name__ == "__main__":
194 unittest.main()