test_dynamic.py 5.9 KB

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