1# Test the most dynamic corner cases of Python's runtime semantics.
2
3import builtins
4import unittest
5
6from test.support import swap_item, swap_attr
7
8
9class RebindBuiltinsTests(unittest.TestCase):
10
11    """Test all the ways that we can change/shadow globals/builtins."""
12
13    def configure_func(self, func, *args):
14        """Perform TestCase-specific configuration on a function before testing.
15
16        By default, this does nothing. Example usage: spinning a function so
17        that a JIT will optimize it. Subclasses should override this as needed.
18
19        Args:
20            func: function to configure.
21            *args: any arguments that should be passed to func, if calling it.
22
23        Returns:
24            Nothing. Work will be performed on func in-place.
25        """
26        pass
27
28    def test_globals_shadow_builtins(self):
29        # Modify globals() to shadow an entry in builtins.
30        def foo():
31            return len([1, 2, 3])
32        self.configure_func(foo)
33
34        self.assertEqual(foo(), 3)
35        with swap_item(globals(), "len", lambda x: 7):
36            self.assertEqual(foo(), 7)
37
38    def test_modify_builtins(self):
39        # Modify the builtins module directly.
40        def foo():
41            return len([1, 2, 3])
42        self.configure_func(foo)
43
44        self.assertEqual(foo(), 3)
45        with swap_attr(builtins, "len", lambda x: 7):
46            self.assertEqual(foo(), 7)
47
48    def test_modify_builtins_while_generator_active(self):
49        # Modify the builtins out from under a live generator.
50        def foo():
51            x = range(3)
52            yield len(x)
53            yield len(x)
54        self.configure_func(foo)
55
56        g = foo()
57        self.assertEqual(next(g), 3)
58        with swap_attr(builtins, "len", lambda x: 7):
59            self.assertEqual(next(g), 7)
60
61    def test_modify_builtins_from_leaf_function(self):
62        # Verify that modifications made by leaf functions percolate up the
63        # callstack.
64        with swap_attr(builtins, "len", len):
65            def bar():
66                builtins.len = lambda x: 4
67
68            def foo(modifier):
69                l = []
70                l.append(len(range(7)))
71                modifier()
72                l.append(len(range(7)))
73                return l
74            self.configure_func(foo, lambda: None)
75
76            self.assertEqual(foo(bar), [7, 4])
77
78    def test_cannot_change_globals_or_builtins_with_eval(self):
79        def foo():
80            return len([1, 2, 3])
81        self.configure_func(foo)
82
83        # Note that this *doesn't* change the definition of len() seen by foo().
84        builtins_dict = {"len": lambda x: 7}
85        globals_dict = {"foo": foo, "__builtins__": builtins_dict,
86                        "len": lambda x: 8}
87        self.assertEqual(eval("foo()", globals_dict), 3)
88
89        self.assertEqual(eval("foo()", {"foo": foo}), 3)
90
91    def test_cannot_change_globals_or_builtins_with_exec(self):
92        def foo():
93            return len([1, 2, 3])
94        self.configure_func(foo)
95
96        globals_dict = {"foo": foo}
97        exec("x = foo()", globals_dict)
98        self.assertEqual(globals_dict["x"], 3)
99
100        # Note that this *doesn't* change the definition of len() seen by foo().
101        builtins_dict = {"len": lambda x: 7}
102        globals_dict = {"foo": foo, "__builtins__": builtins_dict,
103                        "len": lambda x: 8}
104
105        exec("x = foo()", globals_dict)
106        self.assertEqual(globals_dict["x"], 3)
107
108    def test_cannot_replace_builtins_dict_while_active(self):
109        def foo():
110            x = range(3)
111            yield len(x)
112            yield len(x)
113        self.configure_func(foo)
114
115        g = foo()
116        self.assertEqual(next(g), 3)
117        with swap_item(globals(), "__builtins__", {"len": lambda x: 7}):
118            self.assertEqual(next(g), 3)
119
120    def test_cannot_replace_builtins_dict_between_calls(self):
121        def foo():
122            return len([1, 2, 3])
123        self.configure_func(foo)
124
125        self.assertEqual(foo(), 3)
126        with swap_item(globals(), "__builtins__", {"len": lambda x: 7}):
127            self.assertEqual(foo(), 3)
128
129    def test_eval_gives_lambda_custom_globals(self):
130        globals_dict = {"len": lambda x: 7}
131        foo = eval("lambda: len([])", globals_dict)
132        self.configure_func(foo)
133
134        self.assertEqual(foo(), 7)
135
136
137if __name__ == "__main__":
138    unittest.main()
139