summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCollin Winter <collinw@gmail.com>2010-03-17 00:41:56 (GMT)
committerCollin Winter <collinw@gmail.com>2010-03-17 00:41:56 (GMT)
commitf2bf2b3ec4a15e479933c136f2ccbde0a45a3f8e (patch)
treee1e5224e34c11ae7b3603ec4c03df601aca46048
parent0077b65a9e8ce12dd0ebb4793416cf54ddfe5374 (diff)
downloadcpython-f2bf2b3ec4a15e479933c136f2ccbde0a45a3f8e.zip
cpython-f2bf2b3ec4a15e479933c136f2ccbde0a45a3f8e.tar.gz
cpython-f2bf2b3ec4a15e479933c136f2ccbde0a45a3f8e.tar.bz2
Add some tests for ways users can change or shadow globals and builtins.
-rw-r--r--Lib/test/support.py57
-rw-r--r--Lib/test/test_dynamic.py143
2 files changed, 199 insertions, 1 deletions
diff --git a/Lib/test/support.py b/Lib/test/support.py
index a05f420..28f7e27 100644
--- a/Lib/test/support.py
+++ b/Lib/test/support.py
@@ -30,7 +30,8 @@ __all__ = ["Error", "TestFailed", "ResourceDenied", "import_module",
"run_with_locale",
"set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner",
"run_unittest", "run_doctest", "threading_setup", "threading_cleanup",
- "reap_children", "cpython_only", "check_impl_detail", "get_attribute"]
+ "reap_children", "cpython_only", "check_impl_detail", "get_attribute",
+ "swap_item", "swap_attr"]
class Error(Exception):
"""Base class for regression test exceptions."""
@@ -1074,3 +1075,57 @@ def reap_children():
break
except:
break
+
+@contextlib.contextmanager
+def swap_attr(obj, attr, new_val):
+ """Temporary swap out an attribute with a new object.
+
+ Usage:
+ with swap_attr(obj, "attr", 5):
+ ...
+
+ This will set obj.attr to 5 for the duration of the with: block,
+ restoring the old value at the end of the block. If `attr` doesn't
+ exist on `obj`, it will be created and then deleted at the end of the
+ block.
+ """
+ if hasattr(obj, attr):
+ real_val = getattr(obj, attr)
+ setattr(obj, attr, new_val)
+ try:
+ yield
+ finally:
+ setattr(obj, attr, real_val)
+ else:
+ setattr(obj, attr, new_val)
+ try:
+ yield
+ finally:
+ delattr(obj, attr)
+
+@contextlib.contextmanager
+def swap_item(obj, item, new_val):
+ """Temporary swap out an item with a new object.
+
+ Usage:
+ with swap_item(obj, "item", 5):
+ ...
+
+ This will set obj["item"] to 5 for the duration of the with: block,
+ restoring the old value at the end of the block. If `item` doesn't
+ exist on `obj`, it will be created and then deleted at the end of the
+ block.
+ """
+ if item in obj:
+ real_val = obj[item]
+ obj[item] = new_val
+ try:
+ yield
+ finally:
+ obj[item] = real_val
+ else:
+ obj[item] = new_val
+ try:
+ yield
+ finally:
+ del obj[item]
diff --git a/Lib/test/test_dynamic.py b/Lib/test/test_dynamic.py
new file mode 100644
index 0000000..beb7b1c
--- /dev/null
+++ b/Lib/test/test_dynamic.py
@@ -0,0 +1,143 @@
+# Test the most dynamic corner cases of Python's runtime semantics.
+
+import builtins
+import contextlib
+import unittest
+
+from test.support import run_unittest, swap_item, swap_attr
+
+
+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)
+
+
+def test_main():
+ run_unittest(RebindBuiltinsTests)
+
+
+if __name__ == "__main__":
+ test_main()