]> granicus.if.org Git - python/commitdiff
Followup to 862ab99ab570: I forgot to add the magnificent test_frame.py.
authorAntoine Pitrou <solipsis@pitrou.net>
Mon, 5 Aug 2013 21:35:43 +0000 (23:35 +0200)
committerAntoine Pitrou <solipsis@pitrou.net>
Mon, 5 Aug 2013 21:35:43 +0000 (23:35 +0200)
Lib/test/test_frame.py [new file with mode: 0644]

diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py
new file mode 100644 (file)
index 0000000..daabba8
--- /dev/null
@@ -0,0 +1,113 @@
+import gc
+import sys
+import unittest
+import weakref
+
+from test import support
+
+
+class ClearTest(unittest.TestCase):
+    """
+    Tests for frame.clear().
+    """
+
+    def inner(self, x=5, **kwargs):
+        1/0
+
+    def outer(self, **kwargs):
+        try:
+            self.inner(**kwargs)
+        except ZeroDivisionError as e:
+            exc = e
+        return exc
+
+    def clear_traceback_frames(self, tb):
+        """
+        Clear all frames in a traceback.
+        """
+        while tb is not None:
+            tb.tb_frame.clear()
+            tb = tb.tb_next
+
+    def test_clear_locals(self):
+        class C:
+            pass
+        c = C()
+        wr = weakref.ref(c)
+        exc = self.outer(c=c)
+        del c
+        support.gc_collect()
+        # A reference to c is held through the frames
+        self.assertIsNot(None, wr())
+        self.clear_traceback_frames(exc.__traceback__)
+        support.gc_collect()
+        # The reference was released by .clear()
+        self.assertIs(None, wr())
+
+    def test_clear_generator(self):
+        endly = False
+        def g():
+            nonlocal endly
+            try:
+                yield
+                inner()
+            finally:
+                endly = True
+        gen = g()
+        next(gen)
+        self.assertFalse(endly)
+        # Clearing the frame closes the generator
+        gen.gi_frame.clear()
+        self.assertTrue(endly)
+
+    def test_clear_executing(self):
+        # Attempting to clear an executing frame is forbidden.
+        try:
+            1/0
+        except ZeroDivisionError as e:
+            f = e.__traceback__.tb_frame
+        with self.assertRaises(RuntimeError):
+            f.clear()
+        with self.assertRaises(RuntimeError):
+            f.f_back.clear()
+
+    def test_clear_executing_generator(self):
+        # Attempting to clear an executing generator frame is forbidden.
+        endly = False
+        def g():
+            nonlocal endly
+            try:
+                1/0
+            except ZeroDivisionError as e:
+                f = e.__traceback__.tb_frame
+                with self.assertRaises(RuntimeError):
+                    f.clear()
+                with self.assertRaises(RuntimeError):
+                    f.f_back.clear()
+                yield f
+            finally:
+                endly = True
+        gen = g()
+        f = next(gen)
+        self.assertFalse(endly)
+
+    @support.cpython_only
+    def test_clear_refcycles(self):
+        # .clear() doesn't leave any refcycle behin
+        with support.disable_gc():
+            class C:
+                pass
+            c = C()
+            wr = weakref.ref(c)
+            exc = self.outer(c=c)
+            del c
+            self.assertIsNot(None, wr())
+            self.clear_traceback_frames(exc.__traceback__)
+            self.assertIs(None, wr())
+
+
+def test_main():
+    support.run_unittest(__name__)
+
+if __name__ == "__main__":
+    test_main()