]> granicus.if.org Git - python/commitdiff
Issue #21897: Fix a crash with the f_locals attribute with closure variables when...
authorAntoine Pitrou <solipsis@pitrou.net>
Sat, 5 Jul 2014 00:24:13 +0000 (20:24 -0400)
committerAntoine Pitrou <solipsis@pitrou.net>
Sat, 5 Jul 2014 00:24:13 +0000 (20:24 -0400)
Lib/test/test_frame.py
Misc/NEWS
Objects/frameobject.c

index 2dd5780c8890dd540b2f2c8d1ed938385aa0bfe1..c402ec394e1df553983a031c17b215a523e0251a 100644 (file)
@@ -1,5 +1,6 @@
 import gc
 import sys
+import types
 import unittest
 import weakref
 
@@ -109,6 +110,57 @@ class ClearTest(unittest.TestCase):
             self.assertIs(None, wr())
 
 
+class FrameLocalsTest(unittest.TestCase):
+    """
+    Tests for the .f_locals attribute.
+    """
+
+    def make_frames(self):
+        def outer():
+            x = 5
+            y = 6
+            def inner():
+                z = x + 2
+                1/0
+                t = 9
+            return inner()
+        try:
+            outer()
+        except ZeroDivisionError as e:
+            tb = e.__traceback__
+            frames = []
+            while tb:
+                frames.append(tb.tb_frame)
+                tb = tb.tb_next
+        return frames
+
+    def test_locals(self):
+        f, outer, inner = self.make_frames()
+        outer_locals = outer.f_locals
+        self.assertIsInstance(outer_locals.pop('inner'), types.FunctionType)
+        self.assertEqual(outer_locals, {'x': 5, 'y': 6})
+        inner_locals = inner.f_locals
+        self.assertEqual(inner_locals, {'x': 5, 'z': 7})
+
+    def test_clear_locals(self):
+        # Test f_locals after clear() (issue #21897)
+        f, outer, inner = self.make_frames()
+        outer.clear()
+        inner.clear()
+        self.assertEqual(outer.f_locals, {})
+        self.assertEqual(inner.f_locals, {})
+
+    def test_locals_clear_locals(self):
+        # Test f_locals before and after clear() (to exercise caching)
+        f, outer, inner = self.make_frames()
+        outer.f_locals
+        inner.f_locals
+        outer.clear()
+        inner.clear()
+        self.assertEqual(outer.f_locals, {})
+        self.assertEqual(inner.f_locals, {})
+
+
 def test_main():
     support.run_unittest(__name__)
 
index a413f48630a04d2324f04a0bbf0b60e2a6eb0bfb..4e083233a5462d9cc87e292e9155a2f4c5933600 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -27,6 +27,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #21897: Fix a crash with the f_locals attribute with closure
+  variables when frame.clear() has been called.
+
 - Issue #21151: Fixed a segfault in the winreg module when ``None`` is passed
   as a ``REG_BINARY`` value to SetValueEx.  Patch by John Ehresman.
 
index 0d6229360e619f5406d5b66d79a5568861086004..55ee5630f67e68f13643fa9f60f1d52c63900895 100644 (file)
@@ -786,7 +786,7 @@ map_to_dict(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values,
         PyObject *key = PyTuple_GET_ITEM(map, j);
         PyObject *value = values[j];
         assert(PyUnicode_Check(key));
-        if (deref) {
+        if (deref && value != NULL) {
             assert(PyCell_Check(value));
             value = PyCell_GET(value);
         }