blob: d6aa2834cbc28b749b32f7daa412be296d5f68ca [file] [log] [blame]
Antoine Pitrou14709142017-12-31 22:35:22 +01001import re
Antoine Pitrouacc8cf22014-07-04 20:24:13 -04002import types
Antoine Pitrou9e3d27b2013-08-05 23:35:43 +02003import unittest
4import weakref
5
6from test import support
7
8
9class ClearTest(unittest.TestCase):
10 """
11 Tests for frame.clear().
12 """
13
14 def inner(self, x=5, **kwargs):
15 1/0
16
17 def outer(self, **kwargs):
18 try:
19 self.inner(**kwargs)
20 except ZeroDivisionError as e:
21 exc = e
22 return exc
23
24 def clear_traceback_frames(self, tb):
25 """
26 Clear all frames in a traceback.
27 """
28 while tb is not None:
29 tb.tb_frame.clear()
30 tb = tb.tb_next
31
32 def test_clear_locals(self):
33 class C:
34 pass
35 c = C()
36 wr = weakref.ref(c)
37 exc = self.outer(c=c)
38 del c
39 support.gc_collect()
40 # A reference to c is held through the frames
41 self.assertIsNot(None, wr())
42 self.clear_traceback_frames(exc.__traceback__)
43 support.gc_collect()
44 # The reference was released by .clear()
45 self.assertIs(None, wr())
46
47 def test_clear_generator(self):
48 endly = False
49 def g():
50 nonlocal endly
51 try:
52 yield
53 inner()
54 finally:
55 endly = True
56 gen = g()
57 next(gen)
58 self.assertFalse(endly)
59 # Clearing the frame closes the generator
60 gen.gi_frame.clear()
61 self.assertTrue(endly)
62
63 def test_clear_executing(self):
64 # Attempting to clear an executing frame is forbidden.
65 try:
66 1/0
67 except ZeroDivisionError as e:
68 f = e.__traceback__.tb_frame
69 with self.assertRaises(RuntimeError):
70 f.clear()
71 with self.assertRaises(RuntimeError):
72 f.f_back.clear()
73
74 def test_clear_executing_generator(self):
75 # Attempting to clear an executing generator frame is forbidden.
76 endly = False
77 def g():
78 nonlocal endly
79 try:
80 1/0
81 except ZeroDivisionError as e:
82 f = e.__traceback__.tb_frame
83 with self.assertRaises(RuntimeError):
84 f.clear()
85 with self.assertRaises(RuntimeError):
86 f.f_back.clear()
87 yield f
88 finally:
89 endly = True
90 gen = g()
91 f = next(gen)
92 self.assertFalse(endly)
Antoine Pitroudbfc1292013-08-06 23:05:23 +020093 # Clearing the frame closes the generator
94 f.clear()
95 self.assertTrue(endly)
Antoine Pitrou9e3d27b2013-08-05 23:35:43 +020096
97 @support.cpython_only
98 def test_clear_refcycles(self):
Antoine Pitrou236a5472013-08-06 23:06:59 +020099 # .clear() doesn't leave any refcycle behind
Antoine Pitrou9e3d27b2013-08-05 23:35:43 +0200100 with support.disable_gc():
101 class C:
102 pass
103 c = C()
104 wr = weakref.ref(c)
105 exc = self.outer(c=c)
106 del c
107 self.assertIsNot(None, wr())
108 self.clear_traceback_frames(exc.__traceback__)
109 self.assertIs(None, wr())
110
111
Zackery Spytz842acaa2018-12-17 07:52:45 -0700112class FrameAttrsTest(unittest.TestCase):
Antoine Pitrouacc8cf22014-07-04 20:24:13 -0400113
114 def make_frames(self):
115 def outer():
116 x = 5
117 y = 6
118 def inner():
119 z = x + 2
120 1/0
121 t = 9
122 return inner()
123 try:
124 outer()
125 except ZeroDivisionError as e:
126 tb = e.__traceback__
127 frames = []
128 while tb:
129 frames.append(tb.tb_frame)
130 tb = tb.tb_next
131 return frames
132
133 def test_locals(self):
134 f, outer, inner = self.make_frames()
135 outer_locals = outer.f_locals
136 self.assertIsInstance(outer_locals.pop('inner'), types.FunctionType)
137 self.assertEqual(outer_locals, {'x': 5, 'y': 6})
138 inner_locals = inner.f_locals
139 self.assertEqual(inner_locals, {'x': 5, 'z': 7})
140
141 def test_clear_locals(self):
142 # Test f_locals after clear() (issue #21897)
143 f, outer, inner = self.make_frames()
144 outer.clear()
145 inner.clear()
146 self.assertEqual(outer.f_locals, {})
147 self.assertEqual(inner.f_locals, {})
148
149 def test_locals_clear_locals(self):
150 # Test f_locals before and after clear() (to exercise caching)
151 f, outer, inner = self.make_frames()
152 outer.f_locals
153 inner.f_locals
154 outer.clear()
155 inner.clear()
156 self.assertEqual(outer.f_locals, {})
157 self.assertEqual(inner.f_locals, {})
158
Zackery Spytz842acaa2018-12-17 07:52:45 -0700159 def test_f_lineno_del_segfault(self):
160 f, _, _ = self.make_frames()
161 with self.assertRaises(AttributeError):
162 del f.f_lineno
163
Antoine Pitrouacc8cf22014-07-04 20:24:13 -0400164
Antoine Pitrou14709142017-12-31 22:35:22 +0100165class ReprTest(unittest.TestCase):
166 """
167 Tests for repr(frame).
168 """
169
170 def test_repr(self):
171 def outer():
172 x = 5
173 y = 6
174 def inner():
175 z = x + 2
176 1/0
177 t = 9
178 return inner()
179
180 offset = outer.__code__.co_firstlineno
181 try:
182 outer()
183 except ZeroDivisionError as e:
184 tb = e.__traceback__
185 frames = []
186 while tb:
187 frames.append(tb.tb_frame)
188 tb = tb.tb_next
189 else:
190 self.fail("should have raised")
191
192 f_this, f_outer, f_inner = frames
193 file_repr = re.escape(repr(__file__))
194 self.assertRegex(repr(f_this),
195 r"^<frame at 0x[0-9a-fA-F]+, file %s, line %d, code test_repr>$"
196 % (file_repr, offset + 23))
197 self.assertRegex(repr(f_outer),
198 r"^<frame at 0x[0-9a-fA-F]+, file %s, line %d, code outer>$"
199 % (file_repr, offset + 7))
200 self.assertRegex(repr(f_inner),
201 r"^<frame at 0x[0-9a-fA-F]+, file %s, line %d, code inner>$"
202 % (file_repr, offset + 5))
203
204
Antoine Pitrou9e3d27b2013-08-05 23:35:43 +0200205if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -0500206 unittest.main()