test the most dynamic corner cases of python s runtime semantics test all the ways that we can changeshadow globalsbuiltins def configurefuncself func args pass def testglobalsshadowbuiltinsself modify globals to shadow an entry in builtins def foo return len1 2 3 self configurefuncfoo self assertequalfoo 3 with swapitemglobals len lambda x 7 self assertequalfoo 7 def testmodifybuiltinsself modify the builtins module directly def foo return len1 2 3 self configurefuncfoo self assertequalfoo 3 with swapattrbuiltins len lambda x 7 self assertequalfoo 7 def testmodifybuiltinswhilegeneratoractiveself modify the builtins out from under a live generator def foo x range3 yield lenx yield lenx self configurefuncfoo g foo self assertequalnextg 3 with swapattrbuiltins len lambda x 7 self assertequalnextg 7 def testmodifybuiltinsfromleaffunctionself verify that modifications made by leaf functions percolate up the callstack with swapattrbuiltins len len def bar builtins len lambda x 4 def foomodifier l l appendlenrange7 modifier l appendlenrange7 return l self configurefuncfoo lambda none self assertequalfoobar 7 4 def testcannotchangeglobalsorbuiltinswithevalself def foo return len1 2 3 self configurefuncfoo note that this doesn t change the definition of len seen by foo builtinsdict len lambda x 7 globalsdict foo foo builtins builtinsdict len lambda x 8 self assertequalevalfoo globalsdict 3 self assertequalevalfoo foo foo 3 def testcannotchangeglobalsorbuiltinswithexecself def foo return len1 2 3 self configurefuncfoo globalsdict foo foo execx foo globalsdict self assertequalglobalsdictx 3 note that this doesn t change the definition of len seen by foo builtinsdict len lambda x 7 globalsdict foo foo builtins builtinsdict len lambda x 8 execx foo globalsdict self assertequalglobalsdictx 3 def testcannotreplacebuiltinsdictwhileactiveself def foo x range3 yield lenx yield lenx self configurefuncfoo g foo self assertequalnextg 3 with swapitemglobals builtins len lambda x 7 self assertequalnextg 3 def testcannotreplacebuiltinsdictbetweencallsself def foo return len1 2 3 self configurefuncfoo self assertequalfoo 3 with swapitemglobals builtins len lambda x 7 self assertequalfoo 3 def testevalgiveslambdacustomglobalsself globalsdict len lambda x 7 foo evallambda len globalsdict self configurefuncfoo self assertequalfoo 7 def testloadglobalspecializationfailurekeepsopargself https github compythoncpythonissues91625 class myglobalsdict def missingself key return intkey removeprefixnumber need more than 256 variables to use extendedargs variables 400 code lambda joinfnumberi for i in rangevariables sumfunc evalcode myglobals expected sumrangevariables warm up the function for quickening pep 659 for in range30 self assertequalsumfunc expected class testtracingunittest testcase def setupself self addcleanupsys settrace sys gettrace sys settracenone def testafterspecializationself def traceframe event arg return trace turnontrace false class c def initself x self x x def delself if turnontrace sys settracetrace def f loadglobalbuiltin immediately follows the call to c del c0 x len def g binarysuscrlistint immediately follows the call to c del 0c0 x def h binaryopaddint immediately follows the call to c del 0 c0 x for func in f g h with self subtestfunc name for in range58 func turnontrace true func sys settracenone turnontrace false if name main unittest main test the most dynamic corner cases of python s runtime semantics test all the ways that we can change shadow globals builtins 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 modify globals to shadow an entry in builtins modify the builtins module directly modify the builtins out from under a live generator verify that modifications made by leaf functions percolate up the callstack note that this doesn t change the definition of len seen by foo note that this doesn t change the definition of len seen by foo https github com python cpython issues 91625 need more than 256 variables to use extended_args warm up the function for quickening pep 659 load_global _builtin immediately follows the call to c __del__ binary_suscr _list_int immediately follows the call to c __del__ binary_op _add_int immediately follows the call to c __del__
import builtins
import sys
import unittest

from test.support import swap_item, swap_attr


class RebindBuiltinsTests(unittest.TestCase):

    

    def configure_func(self, func, *args):
        
        pass

    def test_globals_shadow_builtins(self):
        
        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):
        
        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):
        
        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):
        
        
        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)

        
        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)

        
        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)

    def test_load_global_specialization_failure_keeps_oparg(self):
        
        class MyGlobals(dict):
            def __missing__(self, key):
                return int(key.removeprefix("_number_"))

        
        variables = 400
        code = "lambda: " + "+".join(f"_number_{i}" for i in range(variables))
        sum_func = eval(code, MyGlobals())
        expected = sum(range(variables))
        
        for _ in range(30):
            self.assertEqual(sum_func(), expected)


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():
            
            C(0).x, len

        def g():
            
            [0][C(0).x]

        def h():
            
            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


if __name__ == "__main__":
    unittest.main()