| 
									
										
										
										
											2010-03-17 00:41:56 +00:00
										 |  |  | # Test the most dynamic corner cases of Python's runtime semantics. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import builtins | 
					
						
							| 
									
										
										
										
											2022-07-26 22:14:35 +02:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2010-03-17 00:41:56 +00:00
										 |  |  | import unittest | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 15:48:14 -08:00
										 |  |  | from test.support import is_wasi, Py_DEBUG, swap_item, swap_attr | 
					
						
							| 
									
										
										
										
											2010-03-17 00:41:56 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RebindBuiltinsTests(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Test all the ways that we can change/shadow globals/builtins.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def configure_func(self, func, *args): | 
					
						
							|  |  |  |         """Perform TestCase-specific configuration on a function before testing.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         By default, this does nothing. Example usage: spinning a function so | 
					
						
							|  |  |  |         that a JIT will optimize it. Subclasses should override this as needed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							|  |  |  |             func: function to configure. | 
					
						
							|  |  |  |             *args: any arguments that should be passed to func, if calling it. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             Nothing. Work will be performed on func in-place. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_globals_shadow_builtins(self): | 
					
						
							|  |  |  |         # Modify globals() to shadow an entry in builtins. | 
					
						
							|  |  |  |         def foo(): | 
					
						
							|  |  |  |             return len([1, 2, 3]) | 
					
						
							|  |  |  |         self.configure_func(foo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(foo(), 3) | 
					
						
							|  |  |  |         with swap_item(globals(), "len", lambda x: 7): | 
					
						
							|  |  |  |             self.assertEqual(foo(), 7) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_modify_builtins(self): | 
					
						
							|  |  |  |         # Modify the builtins module directly. | 
					
						
							|  |  |  |         def foo(): | 
					
						
							|  |  |  |             return len([1, 2, 3]) | 
					
						
							|  |  |  |         self.configure_func(foo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(foo(), 3) | 
					
						
							|  |  |  |         with swap_attr(builtins, "len", lambda x: 7): | 
					
						
							|  |  |  |             self.assertEqual(foo(), 7) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_modify_builtins_while_generator_active(self): | 
					
						
							|  |  |  |         # Modify the builtins out from under a live generator. | 
					
						
							|  |  |  |         def foo(): | 
					
						
							|  |  |  |             x = range(3) | 
					
						
							|  |  |  |             yield len(x) | 
					
						
							|  |  |  |             yield len(x) | 
					
						
							|  |  |  |         self.configure_func(foo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         g = foo() | 
					
						
							|  |  |  |         self.assertEqual(next(g), 3) | 
					
						
							|  |  |  |         with swap_attr(builtins, "len", lambda x: 7): | 
					
						
							|  |  |  |             self.assertEqual(next(g), 7) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_modify_builtins_from_leaf_function(self): | 
					
						
							|  |  |  |         # Verify that modifications made by leaf functions percolate up the | 
					
						
							|  |  |  |         # callstack. | 
					
						
							|  |  |  |         with swap_attr(builtins, "len", len): | 
					
						
							|  |  |  |             def bar(): | 
					
						
							|  |  |  |                 builtins.len = lambda x: 4 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def foo(modifier): | 
					
						
							|  |  |  |                 l = [] | 
					
						
							|  |  |  |                 l.append(len(range(7))) | 
					
						
							|  |  |  |                 modifier() | 
					
						
							|  |  |  |                 l.append(len(range(7))) | 
					
						
							|  |  |  |                 return l | 
					
						
							|  |  |  |             self.configure_func(foo, lambda: None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.assertEqual(foo(bar), [7, 4]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_cannot_change_globals_or_builtins_with_eval(self): | 
					
						
							|  |  |  |         def foo(): | 
					
						
							|  |  |  |             return len([1, 2, 3]) | 
					
						
							|  |  |  |         self.configure_func(foo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Note that this *doesn't* change the definition of len() seen by foo(). | 
					
						
							|  |  |  |         builtins_dict = {"len": lambda x: 7} | 
					
						
							|  |  |  |         globals_dict = {"foo": foo, "__builtins__": builtins_dict, | 
					
						
							|  |  |  |                         "len": lambda x: 8} | 
					
						
							|  |  |  |         self.assertEqual(eval("foo()", globals_dict), 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(eval("foo()", {"foo": foo}), 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_cannot_change_globals_or_builtins_with_exec(self): | 
					
						
							|  |  |  |         def foo(): | 
					
						
							|  |  |  |             return len([1, 2, 3]) | 
					
						
							|  |  |  |         self.configure_func(foo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         globals_dict = {"foo": foo} | 
					
						
							|  |  |  |         exec("x = foo()", globals_dict) | 
					
						
							|  |  |  |         self.assertEqual(globals_dict["x"], 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Note that this *doesn't* change the definition of len() seen by foo(). | 
					
						
							|  |  |  |         builtins_dict = {"len": lambda x: 7} | 
					
						
							|  |  |  |         globals_dict = {"foo": foo, "__builtins__": builtins_dict, | 
					
						
							|  |  |  |                         "len": lambda x: 8} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         exec("x = foo()", globals_dict) | 
					
						
							|  |  |  |         self.assertEqual(globals_dict["x"], 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_cannot_replace_builtins_dict_while_active(self): | 
					
						
							|  |  |  |         def foo(): | 
					
						
							|  |  |  |             x = range(3) | 
					
						
							|  |  |  |             yield len(x) | 
					
						
							|  |  |  |             yield len(x) | 
					
						
							|  |  |  |         self.configure_func(foo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         g = foo() | 
					
						
							|  |  |  |         self.assertEqual(next(g), 3) | 
					
						
							|  |  |  |         with swap_item(globals(), "__builtins__", {"len": lambda x: 7}): | 
					
						
							|  |  |  |             self.assertEqual(next(g), 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_cannot_replace_builtins_dict_between_calls(self): | 
					
						
							|  |  |  |         def foo(): | 
					
						
							|  |  |  |             return len([1, 2, 3]) | 
					
						
							|  |  |  |         self.configure_func(foo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(foo(), 3) | 
					
						
							|  |  |  |         with swap_item(globals(), "__builtins__", {"len": lambda x: 7}): | 
					
						
							|  |  |  |             self.assertEqual(foo(), 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_eval_gives_lambda_custom_globals(self): | 
					
						
							|  |  |  |         globals_dict = {"len": lambda x: 7} | 
					
						
							|  |  |  |         foo = eval("lambda: len([])", globals_dict) | 
					
						
							|  |  |  |         self.configure_func(foo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(foo(), 7) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 15:48:14 -08:00
										 |  |  |     @unittest.skipIf(is_wasi and Py_DEBUG, "stack depth too shallow in pydebug WASI") | 
					
						
							| 
									
										
										
										
											2022-04-17 14:04:29 -04:00
										 |  |  |     def test_load_global_specialization_failure_keeps_oparg(self): | 
					
						
							|  |  |  |         # https://github.com/python/cpython/issues/91625 | 
					
						
							|  |  |  |         class MyGlobals(dict): | 
					
						
							|  |  |  |             def __missing__(self, key): | 
					
						
							|  |  |  |                 return int(key.removeprefix("_number_")) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-05 01:34:03 +01:00
										 |  |  |         # Need more than 256 variables to use EXTENDED_ARGS | 
					
						
							|  |  |  |         variables = 400 | 
					
						
							|  |  |  |         code = "lambda: " + "+".join(f"_number_{i}" for i in range(variables)) | 
					
						
							| 
									
										
										
										
											2022-07-26 22:14:35 +02:00
										 |  |  |         sum_func = eval(code, MyGlobals()) | 
					
						
							| 
									
										
										
										
											2022-10-05 01:34:03 +01:00
										 |  |  |         expected = sum(range(variables)) | 
					
						
							| 
									
										
										
										
											2023-09-20 18:58:23 +02:00
										 |  |  |         # Warm up the function for quickening (PEP 659) | 
					
						
							| 
									
										
										
										
											2022-04-17 14:04:29 -04:00
										 |  |  |         for _ in range(30): | 
					
						
							| 
									
										
										
										
											2022-07-26 22:14:35 +02:00
										 |  |  |             self.assertEqual(sum_func(), expected) | 
					
						
							| 
									
										
										
										
											2010-03-17 00:41:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 17:16:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class TestTracing(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         self.addCleanup(sys.settrace, sys.gettrace()) | 
					
						
							|  |  |  |         sys.settrace(None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_after_specialization(self): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def trace(frame, event, arg): | 
					
						
							|  |  |  |             return trace | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         turn_on_trace = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class C: | 
					
						
							|  |  |  |             def __init__(self, x): | 
					
						
							|  |  |  |                 self.x = x | 
					
						
							|  |  |  |             def __del__(self): | 
					
						
							|  |  |  |                 if turn_on_trace: | 
					
						
							|  |  |  |                     sys.settrace(trace) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             # LOAD_GLOBAL[_BUILTIN] immediately follows the call to C.__del__ | 
					
						
							|  |  |  |             C(0).x, len | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def g(): | 
					
						
							|  |  |  |             # BINARY_SUSCR[_LIST_INT] immediately follows the call to C.__del__ | 
					
						
							|  |  |  |             [0][C(0).x] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def h(): | 
					
						
							|  |  |  |             # BINARY_OP[_ADD_INT] immediately follows the call to C.__del__ | 
					
						
							|  |  |  |             0 + C(0).x | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for func in (f, g, h): | 
					
						
							|  |  |  |             with self.subTest(func.__name__): | 
					
						
							|  |  |  |                 for _ in range(58): | 
					
						
							|  |  |  |                     func() | 
					
						
							|  |  |  |                 turn_on_trace = True | 
					
						
							|  |  |  |                 func() | 
					
						
							|  |  |  |                 sys.settrace(None) | 
					
						
							|  |  |  |                 turn_on_trace = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-17 00:41:56 +00:00
										 |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2015-04-13 15:00:43 -05:00
										 |  |  |     unittest.main() |