blob: c29556354ef49d1b73b6e1793cee1281f3839191 [file] [log] [blame]
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00001"""Test cases for traceback module"""
2
Christian Heimes81ee3ef2008-05-04 22:42:01 +00003from io import StringIO
4import sys
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00005import unittest
Benjamin Petersone6528212008-07-15 15:32:09 +00006import re
Benjamin Peterson26d64ae2010-09-20 21:47:37 +00007from test.support import run_unittest, Error, captured_output
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +02008from test.support import TESTFN, unlink, cpython_only
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00009
10import traceback
11
Christian Heimes81ee3ef2008-05-04 22:42:01 +000012
Benjamin Petersone6528212008-07-15 15:32:09 +000013class SyntaxTracebackCases(unittest.TestCase):
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000014 # For now, a very minimal set of tests. I want to be sure that
15 # formatting of SyntaxErrors works based on changes for 2.1.
16
17 def get_exception_format(self, func, exc):
18 try:
19 func()
Guido van Rossumb940e112007-01-10 16:19:56 +000020 except exc as value:
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000021 return traceback.format_exception_only(exc, value)
22 else:
Collin Winter3add4d72007-08-29 23:37:32 +000023 raise ValueError("call did not raise exception")
Tim Peters7e01e282001-04-08 07:44:07 +000024
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000025 def syntax_error_with_caret(self):
26 compile("def fact(x):\n\treturn x!\n", "?", "exec")
27
Georg Brandl751899a2009-06-04 19:41:00 +000028 def syntax_error_with_caret_2(self):
29 compile("1 +\n", "?", "exec")
30
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000031 def syntax_error_bad_indentation(self):
Georg Brandl88fc6642007-02-09 21:28:07 +000032 compile("def spam():\n print(1)\n print(2)", "?", "exec")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000033
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020034 def syntax_error_with_caret_non_ascii(self):
35 compile('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', "?", "exec")
36
Florent Xicluna758fa5e2014-01-22 01:11:43 +010037 def syntax_error_bad_indentation2(self):
38 compile(" print(2)", "?", "exec")
39
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000040 def test_caret(self):
41 err = self.get_exception_format(self.syntax_error_with_caret,
42 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000043 self.assertEqual(len(err), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000044 self.assertTrue(err[1].strip() == "return x!")
Benjamin Peterson577473f2010-01-19 00:09:57 +000045 self.assertIn("^", err[2]) # third line has caret
Guido van Rossume61fd5b2007-07-11 12:20:59 +000046 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place
Tim Peters7e01e282001-04-08 07:44:07 +000047
Georg Brandl751899a2009-06-04 19:41:00 +000048 err = self.get_exception_format(self.syntax_error_with_caret_2,
49 SyntaxError)
Benjamin Peterson577473f2010-01-19 00:09:57 +000050 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010051 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
52 self.assertEqual(err[1].find("+"), err[2].find("^")) # in the right place
Georg Brandl751899a2009-06-04 19:41:00 +000053
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020054 err = self.get_exception_format(self.syntax_error_with_caret_non_ascii,
55 SyntaxError)
56 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010057 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
58 self.assertEqual(err[1].find("+"), err[2].find("^")) # in the right place
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020059
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000060 def test_nocaret(self):
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000061 exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
62 err = traceback.format_exception_only(SyntaxError, exc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000063 self.assertEqual(len(err), 3)
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000064 self.assertEqual(err[1].strip(), "bad syntax")
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000065
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000066 def test_bad_indentation(self):
67 err = self.get_exception_format(self.syntax_error_bad_indentation,
68 IndentationError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000069 self.assertEqual(len(err), 4)
70 self.assertEqual(err[1].strip(), "print(2)")
Benjamin Peterson577473f2010-01-19 00:09:57 +000071 self.assertIn("^", err[2])
Guido van Rossume61fd5b2007-07-11 12:20:59 +000072 self.assertEqual(err[1].find(")"), err[2].find("^"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000073
Florent Xicluna758fa5e2014-01-22 01:11:43 +010074 err = self.get_exception_format(self.syntax_error_bad_indentation2,
75 IndentationError)
76 self.assertEqual(len(err), 4)
77 self.assertEqual(err[1].strip(), "print(2)")
78 self.assertIn("^", err[2])
79 self.assertEqual(err[1].find("p"), err[2].find("^"))
80
Thomas Wouters477c8d52006-05-27 19:21:47 +000081 def test_base_exception(self):
82 # Test that exceptions derived from BaseException are formatted right
83 e = KeyboardInterrupt()
84 lst = traceback.format_exception_only(e.__class__, e)
85 self.assertEqual(lst, ['KeyboardInterrupt\n'])
86
Thomas Wouters0e3f5912006-08-11 14:57:12 +000087 def test_format_exception_only_bad__str__(self):
88 class X(Exception):
89 def __str__(self):
90 1/0
91 err = traceback.format_exception_only(X, X())
92 self.assertEqual(len(err), 1)
93 str_value = '<unprintable %s object>' % X.__name__
Georg Brandl1a3284e2007-12-02 09:40:06 +000094 if X.__module__ in ('__main__', 'builtins'):
Brett Cannon44c52612007-02-27 00:12:43 +000095 str_name = X.__name__
96 else:
97 str_name = '.'.join([X.__module__, X.__name__])
98 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
Thomas Wouters0e3f5912006-08-11 14:57:12 +000099
Thomas Wouters89f507f2006-12-13 04:49:30 +0000100 def test_without_exception(self):
101 err = traceback.format_exception_only(None, None)
102 self.assertEqual(err, ['None\n'])
103
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000104 def test_encoded_file(self):
105 # Test that tracebacks are correctly printed for encoded source files:
106 # - correct line number (Issue2384)
107 # - respect file encoding (Issue3975)
108 import tempfile, sys, subprocess, os
109
110 # The spawned subprocess has its stdout redirected to a PIPE, and its
111 # encoding may be different from the current interpreter, on Windows
112 # at least.
113 process = subprocess.Popen([sys.executable, "-c",
114 "import sys; print(sys.stdout.encoding)"],
115 stdout=subprocess.PIPE,
116 stderr=subprocess.STDOUT)
117 stdout, stderr = process.communicate()
118 output_encoding = str(stdout, 'ascii').splitlines()[0]
119
120 def do_test(firstlines, message, charset, lineno):
121 # Raise the message in a subprocess, and catch the output
122 try:
123 output = open(TESTFN, "w", encoding=charset)
124 output.write("""{0}if 1:
125 import traceback;
126 raise RuntimeError('{1}')
127 """.format(firstlines, message))
128 output.close()
129 process = subprocess.Popen([sys.executable, TESTFN],
130 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
131 stdout, stderr = process.communicate()
132 stdout = stdout.decode(output_encoding).splitlines()
133 finally:
134 unlink(TESTFN)
135
136 # The source lines are encoded with the 'backslashreplace' handler
137 encoded_message = message.encode(output_encoding,
138 'backslashreplace')
139 # and we just decoded them with the output_encoding.
140 message_ascii = encoded_message.decode(output_encoding)
141
142 err_line = "raise RuntimeError('{0}')".format(message_ascii)
143 err_msg = "RuntimeError: {0}".format(message_ascii)
144
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000145 self.assertIn(("line %s" % lineno), stdout[1],
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000146 "Invalid line number: {0!r} instead of {1}".format(
147 stdout[1], lineno))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000148 self.assertTrue(stdout[2].endswith(err_line),
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000149 "Invalid traceback line: {0!r} instead of {1!r}".format(
150 stdout[2], err_line))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000151 self.assertTrue(stdout[3] == err_msg,
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000152 "Invalid error message: {0!r} instead of {1!r}".format(
153 stdout[3], err_msg))
154
155 do_test("", "foo", "ascii", 3)
156 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
157 if charset == "ascii":
158 text = "foo"
159 elif charset == "GBK":
160 text = "\u4E02\u5100"
161 else:
162 text = "h\xe9 ho"
163 do_test("# coding: {0}\n".format(charset),
164 text, charset, 4)
165 do_test("#!shebang\n# coding: {0}\n".format(charset),
166 text, charset, 5)
Serhiy Storchaka1064a132014-01-09 20:12:49 +0200167 do_test(" \t\f\n# coding: {0}\n".format(charset),
168 text, charset, 5)
169 # Issue #18960: coding spec should has no effect
170 do_test("0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000171
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000172
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000173class TracebackFormatTests(unittest.TestCase):
174
Antoine Pitrou58720d62013-08-05 23:26:40 +0200175 def some_exception(self):
176 raise KeyError('blah')
177
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200178 @cpython_only
Antoine Pitrou58720d62013-08-05 23:26:40 +0200179 def check_traceback_format(self, cleanup_func=None):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200180 from _testcapi import traceback_print
Georg Brandl236f7972009-04-05 14:28:42 +0000181 try:
Antoine Pitrou58720d62013-08-05 23:26:40 +0200182 self.some_exception()
Georg Brandl236f7972009-04-05 14:28:42 +0000183 except KeyError:
184 type_, value, tb = sys.exc_info()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200185 if cleanup_func is not None:
186 # Clear the inner frames, not this one
187 cleanup_func(tb.tb_next)
Georg Brandl236f7972009-04-05 14:28:42 +0000188 traceback_fmt = 'Traceback (most recent call last):\n' + \
189 ''.join(traceback.format_tb(tb))
190 file_ = StringIO()
191 traceback_print(tb, file_)
192 python_fmt = file_.getvalue()
Benjamin Petersond9fec152013-04-29 16:09:39 -0400193 # Call all _tb and _exc functions
194 with captured_output("stderr") as tbstderr:
195 traceback.print_tb(tb)
196 tbfile = StringIO()
197 traceback.print_tb(tb, file=tbfile)
198 with captured_output("stderr") as excstderr:
199 traceback.print_exc()
200 excfmt = traceback.format_exc()
201 excfile = StringIO()
202 traceback.print_exc(file=excfile)
Georg Brandl236f7972009-04-05 14:28:42 +0000203 else:
204 raise Error("unable to create test traceback string")
205
206 # Make sure that Python and the traceback module format the same thing
Ezio Melottib3aedd42010-11-20 19:04:17 +0000207 self.assertEqual(traceback_fmt, python_fmt)
Benjamin Petersond9fec152013-04-29 16:09:39 -0400208 # Now verify the _tb func output
209 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
210 # Now verify the _exc func output
211 self.assertEqual(excstderr.getvalue(), excfile.getvalue())
212 self.assertEqual(excfmt, excfile.getvalue())
Georg Brandl236f7972009-04-05 14:28:42 +0000213
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000214 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000215 tb_lines = python_fmt.splitlines()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200216 self.assertEqual(len(tb_lines), 5)
217 banner = tb_lines[0]
218 location, source_line = tb_lines[-2:]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000219 self.assertTrue(banner.startswith('Traceback'))
220 self.assertTrue(location.startswith(' File'))
221 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000222
Antoine Pitrou58720d62013-08-05 23:26:40 +0200223 def test_traceback_format(self):
224 self.check_traceback_format()
225
226 def test_traceback_format_with_cleared_frames(self):
227 # Check that traceback formatting also works with a clear()ed frame
228 def cleanup_tb(tb):
229 tb.tb_frame.clear()
230 self.check_traceback_format(cleanup_tb)
231
Benjamin Petersond9fec152013-04-29 16:09:39 -0400232 def test_stack_format(self):
233 # Verify _stack functions. Note we have to use _getframe(1) to
234 # compare them without this frame appearing in the output
235 with captured_output("stderr") as ststderr:
236 traceback.print_stack(sys._getframe(1))
237 stfile = StringIO()
238 traceback.print_stack(sys._getframe(1), file=stfile)
239 self.assertEqual(ststderr.getvalue(), stfile.getvalue())
240
241 stfmt = traceback.format_stack(sys._getframe(1))
242
243 self.assertEqual(ststderr.getvalue(), "".join(stfmt))
244
Benjamin Petersone6528212008-07-15 15:32:09 +0000245
246cause_message = (
247 "\nThe above exception was the direct cause "
248 "of the following exception:\n\n")
249
250context_message = (
251 "\nDuring handling of the above exception, "
252 "another exception occurred:\n\n")
253
254boundaries = re.compile(
255 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
256
257
258class BaseExceptionReportingTests:
259
260 def get_exception(self, exception_or_callable):
261 if isinstance(exception_or_callable, Exception):
262 return exception_or_callable
263 try:
264 exception_or_callable()
265 except Exception as e:
266 return e
267
268 def zero_div(self):
269 1/0 # In zero_div
270
271 def check_zero_div(self, msg):
272 lines = msg.splitlines()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000273 self.assertTrue(lines[-3].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000274 self.assertIn('1/0 # In zero_div', lines[-2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000275 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000276
277 def test_simple(self):
278 try:
279 1/0 # Marker
280 except ZeroDivisionError as _:
281 e = _
282 lines = self.get_report(e).splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000283 self.assertEqual(len(lines), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000284 self.assertTrue(lines[0].startswith('Traceback'))
285 self.assertTrue(lines[1].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000286 self.assertIn('1/0 # Marker', lines[2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000287 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000288
289 def test_cause(self):
290 def inner_raise():
291 try:
292 self.zero_div()
293 except ZeroDivisionError as e:
294 raise KeyError from e
295 def outer_raise():
296 inner_raise() # Marker
297 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000298 self.assertEqual(len(blocks), 3)
299 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000300 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000301 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000302
303 def test_context(self):
304 def inner_raise():
305 try:
306 self.zero_div()
307 except ZeroDivisionError:
308 raise KeyError
309 def outer_raise():
310 inner_raise() # Marker
311 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000312 self.assertEqual(len(blocks), 3)
313 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000314 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000315 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000316
Nick Coghlanab7bf212012-02-26 17:49:52 +1000317 def test_context_suppression(self):
318 try:
319 try:
320 raise Exception
321 except:
322 raise ZeroDivisionError from None
323 except ZeroDivisionError as _:
324 e = _
325 lines = self.get_report(e).splitlines()
326 self.assertEqual(len(lines), 4)
327 self.assertTrue(lines[0].startswith('Traceback'))
328 self.assertTrue(lines[1].startswith(' File'))
329 self.assertIn('ZeroDivisionError from None', lines[2])
330 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
331
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000332 def test_cause_and_context(self):
333 # When both a cause and a context are set, only the cause should be
334 # displayed and the context should be muted.
335 def inner_raise():
336 try:
337 self.zero_div()
338 except ZeroDivisionError as _e:
339 e = _e
340 try:
341 xyzzy
342 except NameError:
343 raise KeyError from e
344 def outer_raise():
345 inner_raise() # Marker
346 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000347 self.assertEqual(len(blocks), 3)
348 self.assertEqual(blocks[1], cause_message)
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000349 self.check_zero_div(blocks[0])
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000350 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000351
Benjamin Petersone6528212008-07-15 15:32:09 +0000352 def test_cause_recursive(self):
353 def inner_raise():
354 try:
355 try:
356 self.zero_div()
357 except ZeroDivisionError as e:
358 z = e
359 raise KeyError from e
360 except KeyError as e:
361 raise z from e
362 def outer_raise():
363 inner_raise() # Marker
364 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000365 self.assertEqual(len(blocks), 3)
366 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000367 # The first block is the KeyError raised from the ZeroDivisionError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000368 self.assertIn('raise KeyError from e', blocks[0])
369 self.assertNotIn('1/0', blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000370 # The second block (apart from the boundary) is the ZeroDivisionError
371 # re-raised from the KeyError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000372 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000373 self.check_zero_div(blocks[2])
374
Benjamin Peterson503d6c52010-10-24 02:52:05 +0000375 def test_syntax_error_offset_at_eol(self):
376 # See #10186.
377 def e():
378 raise SyntaxError('', ('', 0, 5, 'hello'))
379 msg = self.get_report(e).splitlines()
380 self.assertEqual(msg[-2], " ^")
Benjamin Petersona95e9772010-10-29 03:28:14 +0000381 def e():
382 exec("x = 5 | 4 |")
383 msg = self.get_report(e).splitlines()
384 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000385
386
387class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
388 #
389 # This checks reporting through the 'traceback' module, with both
390 # format_exception() and print_exception().
391 #
392
393 def get_report(self, e):
394 e = self.get_exception(e)
395 s = ''.join(
396 traceback.format_exception(type(e), e, e.__traceback__))
397 with captured_output("stderr") as sio:
398 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000399 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000400 return s
401
402
403class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
404 #
405 # This checks built-in reporting by the interpreter.
406 #
407
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200408 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000409 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200410 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000411 e = self.get_exception(e)
412 with captured_output("stderr") as s:
413 exception_print(e)
414 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000415
416
Andrew Kuchling173a1572013-09-15 18:15:56 -0400417class MiscTracebackCases(unittest.TestCase):
418 #
419 # Check non-printing functions in traceback module
420 #
421
422 def test_clear(self):
423 def outer():
424 middle()
425 def middle():
426 inner()
427 def inner():
428 i = 1
429 1/0
430
431 try:
432 outer()
433 except:
434 type_, value, tb = sys.exc_info()
435
436 # Initial assertion: there's one local in the inner frame.
437 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
438 self.assertEqual(len(inner_frame.f_locals), 1)
439
440 # Clear traceback frames
441 traceback.clear_frames(tb)
442
443 # Local variable dict should now be empty.
444 self.assertEqual(len(inner_frame.f_locals), 0)
445
446
Fred Drake2e2be372001-09-20 21:33:42 +0000447def test_main():
Benjamin Petersone6528212008-07-15 15:32:09 +0000448 run_unittest(__name__)
Fred Drake2e2be372001-09-20 21:33:42 +0000449
450if __name__ == "__main__":
451 test_main()