[pypy-commit] pypy default: Add __pypy__.locals_to_fast(), from an idea by Fabio Zadrozny to allow a

arigo noreply at buildbot.pypy.org
Thu Feb 6 16:47:46 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r69087:e4876660324a
Date: 2014-02-06 16:46 +0100
http://bitbucket.org/pypy/pypy/changeset/e4876660324a/

Log:	Add __pypy__.locals_to_fast(), from an idea by Fabio Zadrozny to
	allow a Python debugger to modify local variables more freely. The
	original is http://bugs.python.org/issue1654367 .

diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -81,6 +81,7 @@
         'newdict'                   : 'interp_dict.newdict',
         'dictstrategy'              : 'interp_dict.dictstrategy',
         'set_debug'                 : 'interp_magic.set_debug',
+        'locals_to_fast'            : 'interp_magic.locals_to_fast',
     }
     if sys.platform == 'win32':
         interpleveldefs['get_console_cp'] = 'interp_magic.get_console_cp'
diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py
--- a/pypy/module/__pypy__/interp_magic.py
+++ b/pypy/module/__pypy__/interp_magic.py
@@ -1,5 +1,6 @@
 from pypy.interpreter.error import OperationError, wrap_oserror
 from pypy.interpreter.gateway import unwrap_spec
+from pypy.interpreter.pyframe import PyFrame
 from rpython.rlib.objectmodel import we_are_translated
 from pypy.objspace.std.listobject import W_ListObject
 from pypy.objspace.std.typeobject import MethodCache
@@ -111,3 +112,8 @@
 @unwrap_spec(estimate=int)
 def add_memory_pressure(estimate):
     rgc.add_memory_pressure(estimate)
+
+ at unwrap_spec(w_frame=PyFrame)
+def locals_to_fast(space, w_frame):
+    assert isinstance(w_frame, PyFrame)
+    w_frame.locals2fast()
diff --git a/pypy/module/__pypy__/test/test_locals2fast.py b/pypy/module/__pypy__/test/test_locals2fast.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/__pypy__/test/test_locals2fast.py
@@ -0,0 +1,81 @@
+# Tests from Fabio Zadrozny
+
+
+class AppTestLocals2Fast:
+    """
+    Test setting locals in one function from another function
+    using several approaches.
+    """
+
+    def setup_class(cls):
+        cls.w_save_locals = cls.space.appexec([], """():
+            import sys
+            if '__pypy__' in sys.builtin_module_names:
+                import __pypy__
+                save_locals = __pypy__.locals_to_fast
+            else:
+                # CPython version
+                import ctypes
+                @staticmethod
+                def save_locals(frame):
+                    ctypes.pythonapi.PyFrame_LocalsToFast(
+                        ctypes.py_object(frame), ctypes.c_int(0))
+            return save_locals
+        """)
+
+    def test_set_locals_using_save_locals(self):
+        import sys
+        def use_save_locals(name, value):
+            frame = sys._getframe().f_back
+            locals_dict = frame.f_locals
+            locals_dict[name] = value
+            self.save_locals(frame)
+        def test_method(fn):
+            x = 1
+            # The method 'fn' should attempt to set x = 2 in the current frame.
+            fn('x', 2)
+            return x
+        x = test_method(use_save_locals)
+        assert x == 2
+
+    def test_frame_simple_change(self):
+        import sys
+        frame = sys._getframe()
+        a = 20
+        frame.f_locals['a'] = 50
+        self.save_locals(frame)
+        assert a == 50
+
+    def test_frame_co_freevars(self):
+        import sys
+        outer_var = 20
+        def func():
+            frame = sys._getframe()
+            frame.f_locals['outer_var'] = 50
+            self.save_locals(frame)
+            assert outer_var == 50
+        func()
+
+    def test_frame_co_cellvars(self):
+        import sys
+        def check_co_vars(a):
+            frame = sys._getframe()
+            def function2():
+                print a
+            assert 'a' in frame.f_code.co_cellvars
+            frame = sys._getframe()
+            frame.f_locals['a'] = 50
+            self.save_locals(frame)
+            assert a == 50
+        check_co_vars(1)
+
+    def test_frame_change_in_inner_frame(self):
+        import sys
+        def change(f):
+            assert f is not sys._getframe()
+            f.f_locals['a'] = 50
+            self.save_locals(f)
+        frame = sys._getframe()
+        a = 20
+        change(frame)
+        assert a == 50


More information about the pypy-commit mailing list