blob: 61d86a1166e713b6b60a811c7f811680a7d9302c [file] [log] [blame]
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00001"""Test cases for traceback module"""
2
Robert Collins6bc2c1e2015-03-05 12:07:57 +13003from collections import namedtuple
Christian Heimes81ee3ef2008-05-04 22:42:01 +00004from io import StringIO
Robert Collins6bc2c1e2015-03-05 12:07:57 +13005import linecache
Christian Heimes81ee3ef2008-05-04 22:42:01 +00006import sys
Miss Islington (bot)eb0a6802021-06-17 09:41:46 -07007import inspect
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00008import unittest
Benjamin Petersone6528212008-07-15 15:32:09 +00009import re
Serhiy Storchaka24559e42015-05-03 13:19:46 +030010from test import support
Hai Shideb01622020-07-06 20:29:49 +080011from test.support import Error, captured_output, cpython_only, ALWAYS_EQ
12from test.support.os_helper import TESTFN, unlink
Berker Peksagce643912015-05-06 06:33:17 +030013from test.support.script_helper import assert_python_ok
Victor Stinner9d279b82014-12-05 10:18:30 +010014import textwrap
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000015
16import traceback
17
Christian Heimes81ee3ef2008-05-04 22:42:01 +000018
Robert Collins6bc2c1e2015-03-05 12:07:57 +130019test_code = namedtuple('code', ['co_filename', 'co_name'])
Robert Collinsd7c7e0e2015-03-05 20:28:52 +130020test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals'])
Robert Collins6bc2c1e2015-03-05 12:07:57 +130021test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next'])
22
23
Martin Panterbb8b1cb2016-09-22 09:37:39 +000024class TracebackCases(unittest.TestCase):
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000025 # For now, a very minimal set of tests. I want to be sure that
26 # formatting of SyntaxErrors works based on changes for 2.1.
27
28 def get_exception_format(self, func, exc):
29 try:
30 func()
Guido van Rossumb940e112007-01-10 16:19:56 +000031 except exc as value:
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000032 return traceback.format_exception_only(exc, value)
33 else:
Collin Winter3add4d72007-08-29 23:37:32 +000034 raise ValueError("call did not raise exception")
Tim Peters7e01e282001-04-08 07:44:07 +000035
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000036 def syntax_error_with_caret(self):
37 compile("def fact(x):\n\treturn x!\n", "?", "exec")
38
Georg Brandl751899a2009-06-04 19:41:00 +000039 def syntax_error_with_caret_2(self):
40 compile("1 +\n", "?", "exec")
41
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000042 def syntax_error_bad_indentation(self):
Georg Brandl88fc6642007-02-09 21:28:07 +000043 compile("def spam():\n print(1)\n print(2)", "?", "exec")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000044
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020045 def syntax_error_with_caret_non_ascii(self):
46 compile('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', "?", "exec")
47
Florent Xicluna758fa5e2014-01-22 01:11:43 +010048 def syntax_error_bad_indentation2(self):
49 compile(" print(2)", "?", "exec")
50
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000051 def test_caret(self):
52 err = self.get_exception_format(self.syntax_error_with_caret,
53 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000054 self.assertEqual(len(err), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000055 self.assertTrue(err[1].strip() == "return x!")
Benjamin Peterson577473f2010-01-19 00:09:57 +000056 self.assertIn("^", err[2]) # third line has caret
Guido van Rossume61fd5b2007-07-11 12:20:59 +000057 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place
Tim Peters7e01e282001-04-08 07:44:07 +000058
Georg Brandl751899a2009-06-04 19:41:00 +000059 err = self.get_exception_format(self.syntax_error_with_caret_2,
60 SyntaxError)
Benjamin Peterson577473f2010-01-19 00:09:57 +000061 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010062 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070063 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Georg Brandl751899a2009-06-04 19:41:00 +000064
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020065 err = self.get_exception_format(self.syntax_error_with_caret_non_ascii,
66 SyntaxError)
67 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010068 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070069 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020070
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000071 def test_nocaret(self):
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000072 exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
73 err = traceback.format_exception_only(SyntaxError, exc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000074 self.assertEqual(len(err), 3)
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000075 self.assertEqual(err[1].strip(), "bad syntax")
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000076
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000077 def test_bad_indentation(self):
78 err = self.get_exception_format(self.syntax_error_bad_indentation,
79 IndentationError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000080 self.assertEqual(len(err), 4)
81 self.assertEqual(err[1].strip(), "print(2)")
Benjamin Peterson577473f2010-01-19 00:09:57 +000082 self.assertIn("^", err[2])
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070083 self.assertEqual(err[1].find(")") + 1, err[2].find("^"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000084
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070085 # No caret for "unexpected indent"
Florent Xicluna758fa5e2014-01-22 01:11:43 +010086 err = self.get_exception_format(self.syntax_error_bad_indentation2,
87 IndentationError)
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070088 self.assertEqual(len(err), 3)
Florent Xicluna758fa5e2014-01-22 01:11:43 +010089 self.assertEqual(err[1].strip(), "print(2)")
Florent Xicluna758fa5e2014-01-22 01:11:43 +010090
Thomas Wouters477c8d52006-05-27 19:21:47 +000091 def test_base_exception(self):
92 # Test that exceptions derived from BaseException are formatted right
93 e = KeyboardInterrupt()
94 lst = traceback.format_exception_only(e.__class__, e)
95 self.assertEqual(lst, ['KeyboardInterrupt\n'])
96
Thomas Wouters0e3f5912006-08-11 14:57:12 +000097 def test_format_exception_only_bad__str__(self):
98 class X(Exception):
99 def __str__(self):
100 1/0
101 err = traceback.format_exception_only(X, X())
102 self.assertEqual(len(err), 1)
103 str_value = '<unprintable %s object>' % X.__name__
Georg Brandl1a3284e2007-12-02 09:40:06 +0000104 if X.__module__ in ('__main__', 'builtins'):
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300105 str_name = X.__qualname__
Brett Cannon44c52612007-02-27 00:12:43 +0000106 else:
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300107 str_name = '.'.join([X.__module__, X.__qualname__])
Brett Cannon44c52612007-02-27 00:12:43 +0000108 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000109
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000110 def test_encoded_file(self):
111 # Test that tracebacks are correctly printed for encoded source files:
112 # - correct line number (Issue2384)
113 # - respect file encoding (Issue3975)
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)90d0cfb2018-11-16 21:02:58 +0530114 import sys, subprocess
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000115
116 # The spawned subprocess has its stdout redirected to a PIPE, and its
117 # encoding may be different from the current interpreter, on Windows
118 # at least.
119 process = subprocess.Popen([sys.executable, "-c",
120 "import sys; print(sys.stdout.encoding)"],
121 stdout=subprocess.PIPE,
122 stderr=subprocess.STDOUT)
123 stdout, stderr = process.communicate()
124 output_encoding = str(stdout, 'ascii').splitlines()[0]
125
126 def do_test(firstlines, message, charset, lineno):
127 # Raise the message in a subprocess, and catch the output
128 try:
Victor Stinner51d8c522016-02-08 17:57:02 +0100129 with open(TESTFN, "w", encoding=charset) as output:
130 output.write("""{0}if 1:
131 import traceback;
132 raise RuntimeError('{1}')
133 """.format(firstlines, message))
134
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000135 process = subprocess.Popen([sys.executable, TESTFN],
136 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
137 stdout, stderr = process.communicate()
138 stdout = stdout.decode(output_encoding).splitlines()
139 finally:
140 unlink(TESTFN)
141
142 # The source lines are encoded with the 'backslashreplace' handler
143 encoded_message = message.encode(output_encoding,
144 'backslashreplace')
145 # and we just decoded them with the output_encoding.
146 message_ascii = encoded_message.decode(output_encoding)
147
148 err_line = "raise RuntimeError('{0}')".format(message_ascii)
149 err_msg = "RuntimeError: {0}".format(message_ascii)
150
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000151 self.assertIn(("line %s" % lineno), stdout[1],
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000152 "Invalid line number: {0!r} instead of {1}".format(
153 stdout[1], lineno))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000154 self.assertTrue(stdout[2].endswith(err_line),
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000155 "Invalid traceback line: {0!r} instead of {1!r}".format(
156 stdout[2], err_line))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000157 self.assertTrue(stdout[3] == err_msg,
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000158 "Invalid error message: {0!r} instead of {1!r}".format(
159 stdout[3], err_msg))
160
161 do_test("", "foo", "ascii", 3)
162 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
163 if charset == "ascii":
164 text = "foo"
165 elif charset == "GBK":
166 text = "\u4E02\u5100"
167 else:
168 text = "h\xe9 ho"
169 do_test("# coding: {0}\n".format(charset),
170 text, charset, 4)
171 do_test("#!shebang\n# coding: {0}\n".format(charset),
172 text, charset, 5)
Serhiy Storchaka1064a132014-01-09 20:12:49 +0200173 do_test(" \t\f\n# coding: {0}\n".format(charset),
174 text, charset, 5)
Martin Panter614827c2016-04-19 04:05:59 +0000175 # Issue #18960: coding spec should have no effect
Victor Stinner51d8c522016-02-08 17:57:02 +0100176 do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000177
Victor Stinner9d279b82014-12-05 10:18:30 +0100178 def test_print_traceback_at_exit(self):
179 # Issue #22599: Ensure that it is possible to use the traceback module
180 # to display an exception at Python exit
181 code = textwrap.dedent("""
182 import sys
183 import traceback
184
185 class PrintExceptionAtExit(object):
186 def __init__(self):
187 try:
188 x = 1 / 0
189 except Exception:
190 self.exc_info = sys.exc_info()
191 # self.exc_info[1] (traceback) contains frames:
192 # explicitly clear the reference to self in the current
193 # frame to break a reference cycle
194 self = None
195
196 def __del__(self):
197 traceback.print_exception(*self.exc_info)
198
199 # Keep a reference in the module namespace to call the destructor
200 # when the module is unloaded
201 obj = PrintExceptionAtExit()
202 """)
203 rc, stdout, stderr = assert_python_ok('-c', code)
204 expected = [b'Traceback (most recent call last):',
205 b' File "<string>", line 8, in __init__',
206 b'ZeroDivisionError: division by zero']
207 self.assertEqual(stderr.splitlines(), expected)
208
Berker Peksagc3f417d2015-07-24 17:36:21 +0300209 def test_print_exception(self):
210 output = StringIO()
211 traceback.print_exception(
212 Exception, Exception("projector"), None, file=output
213 )
214 self.assertEqual(output.getvalue(), "Exception: projector\n")
215
Zackery Spytz91e93792020-11-05 15:18:44 -0700216 def test_print_exception_exc(self):
217 output = StringIO()
218 traceback.print_exception(Exception("projector"), file=output)
219 self.assertEqual(output.getvalue(), "Exception: projector\n")
220
221 def test_format_exception_exc(self):
222 e = Exception("projector")
223 output = traceback.format_exception(e)
224 self.assertEqual(output, ["Exception: projector\n"])
225 with self.assertRaisesRegex(ValueError, 'Both or neither'):
226 traceback.format_exception(e.__class__, e)
227 with self.assertRaisesRegex(ValueError, 'Both or neither'):
228 traceback.format_exception(e.__class__, tb=e.__traceback__)
229 with self.assertRaisesRegex(TypeError, 'positional-only'):
230 traceback.format_exception(exc=e)
231
232 def test_format_exception_only_exc(self):
233 output = traceback.format_exception_only(Exception("projector"))
234 self.assertEqual(output, ["Exception: projector\n"])
235
Irit Katriel26f18b82021-02-23 14:58:47 +0000236 def test_exception_is_None(self):
237 NONE_EXC_STRING = 'NoneType: None\n'
238 excfile = StringIO()
Irit Katrielb798ab02021-02-23 17:43:04 +0000239 traceback.print_exception(None, file=excfile)
240 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
241
242 excfile = StringIO()
Irit Katriel26f18b82021-02-23 14:58:47 +0000243 traceback.print_exception(None, None, None, file=excfile)
244 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
245
246 excfile = StringIO()
247 traceback.print_exc(None, file=excfile)
248 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
249
250 self.assertEqual(traceback.format_exc(None), NONE_EXC_STRING)
Irit Katrielb798ab02021-02-23 17:43:04 +0000251 self.assertEqual(traceback.format_exception(None), [NONE_EXC_STRING])
Irit Katriel26f18b82021-02-23 14:58:47 +0000252 self.assertEqual(
253 traceback.format_exception(None, None, None), [NONE_EXC_STRING])
254 self.assertEqual(
255 traceback.format_exception_only(None), [NONE_EXC_STRING])
256 self.assertEqual(
257 traceback.format_exception_only(None, None), [NONE_EXC_STRING])
258
Miss Islington (bot)eb0a6802021-06-17 09:41:46 -0700259 def test_signatures(self):
260 self.assertEqual(
261 str(inspect.signature(traceback.print_exception)),
262 ('(exc, /, value=<implicit>, tb=<implicit>, '
263 'limit=None, file=None, chain=True)'))
264
265 self.assertEqual(
266 str(inspect.signature(traceback.format_exception)),
267 ('(exc, /, value=<implicit>, tb=<implicit>, limit=None, '
268 'chain=True)'))
269
270 self.assertEqual(
271 str(inspect.signature(traceback.format_exception_only)),
272 '(exc, /, value=<implicit>)')
273
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000274
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000275class TracebackFormatTests(unittest.TestCase):
276
Antoine Pitrou58720d62013-08-05 23:26:40 +0200277 def some_exception(self):
278 raise KeyError('blah')
279
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200280 @cpython_only
Antoine Pitrou58720d62013-08-05 23:26:40 +0200281 def check_traceback_format(self, cleanup_func=None):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200282 from _testcapi import traceback_print
Georg Brandl236f7972009-04-05 14:28:42 +0000283 try:
Antoine Pitrou58720d62013-08-05 23:26:40 +0200284 self.some_exception()
Georg Brandl236f7972009-04-05 14:28:42 +0000285 except KeyError:
286 type_, value, tb = sys.exc_info()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200287 if cleanup_func is not None:
288 # Clear the inner frames, not this one
289 cleanup_func(tb.tb_next)
Georg Brandl236f7972009-04-05 14:28:42 +0000290 traceback_fmt = 'Traceback (most recent call last):\n' + \
291 ''.join(traceback.format_tb(tb))
292 file_ = StringIO()
293 traceback_print(tb, file_)
294 python_fmt = file_.getvalue()
Benjamin Petersond9fec152013-04-29 16:09:39 -0400295 # Call all _tb and _exc functions
296 with captured_output("stderr") as tbstderr:
297 traceback.print_tb(tb)
298 tbfile = StringIO()
299 traceback.print_tb(tb, file=tbfile)
300 with captured_output("stderr") as excstderr:
301 traceback.print_exc()
302 excfmt = traceback.format_exc()
303 excfile = StringIO()
304 traceback.print_exc(file=excfile)
Georg Brandl236f7972009-04-05 14:28:42 +0000305 else:
306 raise Error("unable to create test traceback string")
307
308 # Make sure that Python and the traceback module format the same thing
Ezio Melottib3aedd42010-11-20 19:04:17 +0000309 self.assertEqual(traceback_fmt, python_fmt)
Benjamin Petersond9fec152013-04-29 16:09:39 -0400310 # Now verify the _tb func output
311 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
312 # Now verify the _exc func output
313 self.assertEqual(excstderr.getvalue(), excfile.getvalue())
314 self.assertEqual(excfmt, excfile.getvalue())
Georg Brandl236f7972009-04-05 14:28:42 +0000315
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000316 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000317 tb_lines = python_fmt.splitlines()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200318 self.assertEqual(len(tb_lines), 5)
319 banner = tb_lines[0]
320 location, source_line = tb_lines[-2:]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000321 self.assertTrue(banner.startswith('Traceback'))
322 self.assertTrue(location.startswith(' File'))
323 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000324
Antoine Pitrou58720d62013-08-05 23:26:40 +0200325 def test_traceback_format(self):
326 self.check_traceback_format()
327
328 def test_traceback_format_with_cleared_frames(self):
329 # Check that traceback formatting also works with a clear()ed frame
330 def cleanup_tb(tb):
331 tb.tb_frame.clear()
332 self.check_traceback_format(cleanup_tb)
333
Benjamin Petersond9fec152013-04-29 16:09:39 -0400334 def test_stack_format(self):
335 # Verify _stack functions. Note we have to use _getframe(1) to
336 # compare them without this frame appearing in the output
337 with captured_output("stderr") as ststderr:
338 traceback.print_stack(sys._getframe(1))
339 stfile = StringIO()
340 traceback.print_stack(sys._getframe(1), file=stfile)
341 self.assertEqual(ststderr.getvalue(), stfile.getvalue())
342
343 stfmt = traceback.format_stack(sys._getframe(1))
344
345 self.assertEqual(ststderr.getvalue(), "".join(stfmt))
346
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300347 def test_print_stack(self):
348 def prn():
349 traceback.print_stack()
350 with captured_output("stderr") as stderr:
351 prn()
352 lineno = prn.__code__.co_firstlineno
353 self.assertEqual(stderr.getvalue().splitlines()[-4:], [
354 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3),
355 ' prn()',
356 ' File "%s", line %d, in prn' % (__file__, lineno+1),
357 ' traceback.print_stack()',
358 ])
359
Nick Coghland0034232016-08-15 13:11:34 +1000360 # issue 26823 - Shrink recursive tracebacks
361 def _check_recursive_traceback_display(self, render_exc):
362 # Always show full diffs when this test fails
363 # Note that rearranging things may require adjusting
364 # the relative line numbers in the expected tracebacks
365 self.maxDiff = None
366
367 # Check hitting the recursion limit
368 def f():
369 f()
370
371 with captured_output("stderr") as stderr_f:
372 try:
373 f()
Pablo Galindo293dd232019-11-19 21:34:03 +0000374 except RecursionError:
Nick Coghland0034232016-08-15 13:11:34 +1000375 render_exc()
376 else:
377 self.fail("no recursion occurred")
378
379 lineno_f = f.__code__.co_firstlineno
380 result_f = (
381 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400382 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000383 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400384 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000385 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400386 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000387 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400388 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000389 ' f()\n'
390 # XXX: The following line changes depending on whether the tests
391 # are run through the interactive interpreter or with -m
392 # It also varies depending on the platform (stack size)
393 # Fortunately, we don't care about exactness here, so we use regex
394 r' \[Previous line repeated (\d+) more times\]' '\n'
395 'RecursionError: maximum recursion depth exceeded\n'
396 )
397
398 expected = result_f.splitlines()
399 actual = stderr_f.getvalue().splitlines()
400
401 # Check the output text matches expectations
402 # 2nd last line contains the repetition count
403 self.assertEqual(actual[:-2], expected[:-2])
404 self.assertRegex(actual[-2], expected[-2])
codedragon3480ef92017-05-24 14:23:46 -0700405 # last line can have additional text appended
406 self.assertIn(expected[-1], actual[-1])
Nick Coghland0034232016-08-15 13:11:34 +1000407
408 # Check the recursion count is roughly as expected
409 rec_limit = sys.getrecursionlimit()
Serhiy Storchakab7281052016-09-12 00:52:40 +0300410 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
Nick Coghland0034232016-08-15 13:11:34 +1000411
412 # Check a known (limited) number of recursive invocations
413 def g(count=10):
414 if count:
415 return g(count-1)
416 raise ValueError
417
418 with captured_output("stderr") as stderr_g:
419 try:
420 g()
Pablo Galindo293dd232019-11-19 21:34:03 +0000421 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000422 render_exc()
423 else:
424 self.fail("no value error was raised")
425
426 lineno_g = g.__code__.co_firstlineno
427 result_g = (
Eric V. Smith451d0e32016-09-09 21:56:20 -0400428 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000429 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400430 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000431 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400432 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000433 ' return g(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700434 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400435 f' File "{__file__}", line {lineno_g+3}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000436 ' raise ValueError\n'
437 'ValueError\n'
438 )
439 tb_line = (
440 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400441 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000442 ' g()\n'
443 )
444 expected = (tb_line + result_g).splitlines()
445 actual = stderr_g.getvalue().splitlines()
446 self.assertEqual(actual, expected)
447
448 # Check 2 different repetitive sections
449 def h(count=10):
450 if count:
451 return h(count-1)
452 g()
453
454 with captured_output("stderr") as stderr_h:
455 try:
456 h()
Pablo Galindo293dd232019-11-19 21:34:03 +0000457 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000458 render_exc()
459 else:
460 self.fail("no value error was raised")
461
462 lineno_h = h.__code__.co_firstlineno
463 result_h = (
464 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400465 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000466 ' h()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400467 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000468 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400469 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000470 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400471 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000472 ' return h(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700473 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400474 f' File "{__file__}", line {lineno_h+3}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000475 ' g()\n'
476 )
477 expected = (result_h + result_g).splitlines()
478 actual = stderr_h.getvalue().splitlines()
479 self.assertEqual(actual, expected)
480
Benjamin Petersond5458692018-09-10 08:43:10 -0700481 # Check the boundary conditions. First, test just below the cutoff.
482 with captured_output("stderr") as stderr_g:
483 try:
484 g(traceback._RECURSIVE_CUTOFF)
Pablo Galindo293dd232019-11-19 21:34:03 +0000485 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700486 render_exc()
487 else:
488 self.fail("no error raised")
489 result_g = (
490 f' File "{__file__}", line {lineno_g+2}, in g\n'
491 ' return g(count-1)\n'
492 f' File "{__file__}", line {lineno_g+2}, in g\n'
493 ' return g(count-1)\n'
494 f' File "{__file__}", line {lineno_g+2}, in g\n'
495 ' return g(count-1)\n'
496 f' File "{__file__}", line {lineno_g+3}, in g\n'
497 ' raise ValueError\n'
498 'ValueError\n'
499 )
500 tb_line = (
501 'Traceback (most recent call last):\n'
502 f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n'
503 ' g(traceback._RECURSIVE_CUTOFF)\n'
504 )
505 expected = (tb_line + result_g).splitlines()
506 actual = stderr_g.getvalue().splitlines()
507 self.assertEqual(actual, expected)
508
509 # Second, test just above the cutoff.
510 with captured_output("stderr") as stderr_g:
511 try:
512 g(traceback._RECURSIVE_CUTOFF + 1)
Pablo Galindo293dd232019-11-19 21:34:03 +0000513 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700514 render_exc()
515 else:
516 self.fail("no error raised")
517 result_g = (
518 f' File "{__file__}", line {lineno_g+2}, in g\n'
519 ' return g(count-1)\n'
520 f' File "{__file__}", line {lineno_g+2}, in g\n'
521 ' return g(count-1)\n'
522 f' File "{__file__}", line {lineno_g+2}, in g\n'
523 ' return g(count-1)\n'
524 ' [Previous line repeated 1 more time]\n'
525 f' File "{__file__}", line {lineno_g+3}, in g\n'
526 ' raise ValueError\n'
527 'ValueError\n'
528 )
529 tb_line = (
530 'Traceback (most recent call last):\n'
531 f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n'
532 ' g(traceback._RECURSIVE_CUTOFF + 1)\n'
533 )
534 expected = (tb_line + result_g).splitlines()
535 actual = stderr_g.getvalue().splitlines()
536 self.assertEqual(actual, expected)
537
Nick Coghland0034232016-08-15 13:11:34 +1000538 def test_recursive_traceback_python(self):
539 self._check_recursive_traceback_display(traceback.print_exc)
540
541 @cpython_only
542 def test_recursive_traceback_cpython_internal(self):
543 from _testcapi import exception_print
544 def render_exc():
545 exc_type, exc_value, exc_tb = sys.exc_info()
546 exception_print(exc_value)
547 self._check_recursive_traceback_display(render_exc)
548
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300549 def test_format_stack(self):
550 def fmt():
551 return traceback.format_stack()
552 result = fmt()
553 lineno = fmt.__code__.co_firstlineno
554 self.assertEqual(result[-2:], [
555 ' File "%s", line %d, in test_format_stack\n'
556 ' result = fmt()\n' % (__file__, lineno+2),
557 ' File "%s", line %d, in fmt\n'
558 ' return traceback.format_stack()\n' % (__file__, lineno+1),
559 ])
560
Zane Bitterde860732017-10-17 17:29:39 -0400561 @cpython_only
562 def test_unhashable(self):
563 from _testcapi import exception_print
564
565 class UnhashableException(Exception):
566 def __eq__(self, other):
567 return True
568
569 ex1 = UnhashableException('ex1')
570 ex2 = UnhashableException('ex2')
571 try:
572 raise ex2 from ex1
573 except UnhashableException:
574 try:
575 raise ex1
576 except UnhashableException:
577 exc_type, exc_val, exc_tb = sys.exc_info()
578
579 with captured_output("stderr") as stderr_f:
580 exception_print(exc_val)
581
582 tb = stderr_f.getvalue().strip().splitlines()
583 self.assertEqual(11, len(tb))
584 self.assertEqual(context_message.strip(), tb[5])
585 self.assertIn('UnhashableException: ex2', tb[3])
586 self.assertIn('UnhashableException: ex1', tb[10])
587
Benjamin Petersone6528212008-07-15 15:32:09 +0000588
589cause_message = (
590 "\nThe above exception was the direct cause "
591 "of the following exception:\n\n")
592
593context_message = (
594 "\nDuring handling of the above exception, "
595 "another exception occurred:\n\n")
596
597boundaries = re.compile(
598 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
599
600
601class BaseExceptionReportingTests:
602
603 def get_exception(self, exception_or_callable):
604 if isinstance(exception_or_callable, Exception):
605 return exception_or_callable
606 try:
607 exception_or_callable()
608 except Exception as e:
609 return e
610
611 def zero_div(self):
612 1/0 # In zero_div
613
614 def check_zero_div(self, msg):
615 lines = msg.splitlines()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000616 self.assertTrue(lines[-3].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000617 self.assertIn('1/0 # In zero_div', lines[-2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000618 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000619
620 def test_simple(self):
621 try:
622 1/0 # Marker
623 except ZeroDivisionError as _:
624 e = _
625 lines = self.get_report(e).splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000626 self.assertEqual(len(lines), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000627 self.assertTrue(lines[0].startswith('Traceback'))
628 self.assertTrue(lines[1].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000629 self.assertIn('1/0 # Marker', lines[2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000630 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000631
632 def test_cause(self):
633 def inner_raise():
634 try:
635 self.zero_div()
636 except ZeroDivisionError as e:
637 raise KeyError from e
638 def outer_raise():
639 inner_raise() # Marker
640 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000641 self.assertEqual(len(blocks), 3)
642 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000643 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000644 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000645
646 def test_context(self):
647 def inner_raise():
648 try:
649 self.zero_div()
650 except ZeroDivisionError:
651 raise KeyError
652 def outer_raise():
653 inner_raise() # Marker
654 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000655 self.assertEqual(len(blocks), 3)
656 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000657 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000658 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000659
Nick Coghlanab7bf212012-02-26 17:49:52 +1000660 def test_context_suppression(self):
661 try:
662 try:
663 raise Exception
664 except:
665 raise ZeroDivisionError from None
666 except ZeroDivisionError as _:
667 e = _
668 lines = self.get_report(e).splitlines()
669 self.assertEqual(len(lines), 4)
670 self.assertTrue(lines[0].startswith('Traceback'))
671 self.assertTrue(lines[1].startswith(' File'))
672 self.assertIn('ZeroDivisionError from None', lines[2])
673 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
674
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000675 def test_cause_and_context(self):
676 # When both a cause and a context are set, only the cause should be
677 # displayed and the context should be muted.
678 def inner_raise():
679 try:
680 self.zero_div()
681 except ZeroDivisionError as _e:
682 e = _e
683 try:
684 xyzzy
685 except NameError:
686 raise KeyError from e
687 def outer_raise():
688 inner_raise() # Marker
689 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000690 self.assertEqual(len(blocks), 3)
691 self.assertEqual(blocks[1], cause_message)
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000692 self.check_zero_div(blocks[0])
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000693 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000694
Benjamin Petersone6528212008-07-15 15:32:09 +0000695 def test_cause_recursive(self):
696 def inner_raise():
697 try:
698 try:
699 self.zero_div()
700 except ZeroDivisionError as e:
701 z = e
702 raise KeyError from e
703 except KeyError as e:
704 raise z from e
705 def outer_raise():
706 inner_raise() # Marker
707 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000708 self.assertEqual(len(blocks), 3)
709 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000710 # The first block is the KeyError raised from the ZeroDivisionError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000711 self.assertIn('raise KeyError from e', blocks[0])
712 self.assertNotIn('1/0', blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000713 # The second block (apart from the boundary) is the ZeroDivisionError
714 # re-raised from the KeyError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000715 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000716 self.check_zero_div(blocks[2])
717
Benjamin Peterson503d6c52010-10-24 02:52:05 +0000718 def test_syntax_error_offset_at_eol(self):
719 # See #10186.
720 def e():
721 raise SyntaxError('', ('', 0, 5, 'hello'))
722 msg = self.get_report(e).splitlines()
723 self.assertEqual(msg[-2], " ^")
Benjamin Petersona95e9772010-10-29 03:28:14 +0000724 def e():
725 exec("x = 5 | 4 |")
726 msg = self.get_report(e).splitlines()
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700727 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000728
Irit Katriel069560b2020-12-22 19:53:09 +0000729 def test_syntax_error_no_lineno(self):
730 # See #34463.
731
732 # Without filename
733 e = SyntaxError('bad syntax')
734 msg = self.get_report(e).splitlines()
735 self.assertEqual(msg,
736 ['SyntaxError: bad syntax'])
737 e.lineno = 100
738 msg = self.get_report(e).splitlines()
739 self.assertEqual(msg,
740 [' File "<string>", line 100', 'SyntaxError: bad syntax'])
741
742 # With filename
743 e = SyntaxError('bad syntax')
744 e.filename = 'myfile.py'
745
746 msg = self.get_report(e).splitlines()
747 self.assertEqual(msg,
748 ['SyntaxError: bad syntax (myfile.py)'])
749 e.lineno = 100
750 msg = self.get_report(e).splitlines()
751 self.assertEqual(msg,
752 [' File "myfile.py", line 100', 'SyntaxError: bad syntax'])
753
Martin Panterbb8b1cb2016-09-22 09:37:39 +0000754 def test_message_none(self):
755 # A message that looks like "None" should not be treated specially
756 err = self.get_report(Exception(None))
757 self.assertIn('Exception: None\n', err)
758 err = self.get_report(Exception('None'))
759 self.assertIn('Exception: None\n', err)
760 err = self.get_report(Exception())
761 self.assertIn('Exception\n', err)
762 err = self.get_report(Exception(''))
763 self.assertIn('Exception\n', err)
764
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700765 def test_syntax_error_various_offsets(self):
766 for offset in range(-5, 10):
767 for add in [0, 2]:
768 text = " "*add + "text%d" % offset
769 expected = [' File "file.py", line 1']
770 if offset < 1:
771 expected.append(" %s" % text.lstrip())
772 elif offset <= 6:
773 expected.append(" %s" % text.lstrip())
774 expected.append(" %s^" % (" "*(offset-1)))
775 else:
776 expected.append(" %s" % text.lstrip())
777 expected.append(" %s^" % (" "*5))
778 expected.append("SyntaxError: msg")
779 expected.append("")
780 err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text)))
781 exp = "\n".join(expected)
782 self.assertEqual(exp, err)
783
Benjamin Petersone6528212008-07-15 15:32:09 +0000784
785class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
786 #
787 # This checks reporting through the 'traceback' module, with both
788 # format_exception() and print_exception().
789 #
790
791 def get_report(self, e):
792 e = self.get_exception(e)
793 s = ''.join(
794 traceback.format_exception(type(e), e, e.__traceback__))
795 with captured_output("stderr") as sio:
796 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000797 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000798 return s
799
800
801class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
802 #
803 # This checks built-in reporting by the interpreter.
804 #
805
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200806 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000807 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200808 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000809 e = self.get_exception(e)
810 with captured_output("stderr") as s:
811 exception_print(e)
812 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000813
814
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300815class LimitTests(unittest.TestCase):
816
817 ''' Tests for limit argument.
818 It's enough to test extact_tb, extract_stack and format_exception '''
819
820 def last_raises1(self):
821 raise Exception('Last raised')
822
823 def last_raises2(self):
824 self.last_raises1()
825
826 def last_raises3(self):
827 self.last_raises2()
828
829 def last_raises4(self):
830 self.last_raises3()
831
832 def last_raises5(self):
833 self.last_raises4()
834
835 def last_returns_frame1(self):
836 return sys._getframe()
837
838 def last_returns_frame2(self):
839 return self.last_returns_frame1()
840
841 def last_returns_frame3(self):
842 return self.last_returns_frame2()
843
844 def last_returns_frame4(self):
845 return self.last_returns_frame3()
846
847 def last_returns_frame5(self):
848 return self.last_returns_frame4()
849
850 def test_extract_stack(self):
851 frame = self.last_returns_frame5()
852 def extract(**kwargs):
853 return traceback.extract_stack(frame, **kwargs)
854 def assertEqualExcept(actual, expected, ignore):
855 self.assertEqual(actual[:ignore], expected[:ignore])
856 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
857 self.assertEqual(len(actual), len(expected))
858
859 with support.swap_attr(sys, 'tracebacklimit', 1000):
860 nolim = extract()
861 self.assertGreater(len(nolim), 5)
862 self.assertEqual(extract(limit=2), nolim[-2:])
863 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
864 self.assertEqual(extract(limit=-2), nolim[:2])
865 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
866 self.assertEqual(extract(limit=0), [])
867 del sys.tracebacklimit
868 assertEqualExcept(extract(), nolim, -5-1)
869 sys.tracebacklimit = 2
870 self.assertEqual(extract(), nolim[-2:])
871 self.assertEqual(extract(limit=3), nolim[-3:])
872 self.assertEqual(extract(limit=-3), nolim[:3])
873 sys.tracebacklimit = 0
874 self.assertEqual(extract(), [])
875 sys.tracebacklimit = -1
876 self.assertEqual(extract(), [])
877
878 def test_extract_tb(self):
879 try:
880 self.last_raises5()
881 except Exception:
882 exc_type, exc_value, tb = sys.exc_info()
883 def extract(**kwargs):
884 return traceback.extract_tb(tb, **kwargs)
885
886 with support.swap_attr(sys, 'tracebacklimit', 1000):
887 nolim = extract()
888 self.assertEqual(len(nolim), 5+1)
889 self.assertEqual(extract(limit=2), nolim[:2])
890 self.assertEqual(extract(limit=10), nolim)
891 self.assertEqual(extract(limit=-2), nolim[-2:])
892 self.assertEqual(extract(limit=-10), nolim)
893 self.assertEqual(extract(limit=0), [])
894 del sys.tracebacklimit
895 self.assertEqual(extract(), nolim)
896 sys.tracebacklimit = 2
897 self.assertEqual(extract(), nolim[:2])
898 self.assertEqual(extract(limit=3), nolim[:3])
899 self.assertEqual(extract(limit=-3), nolim[-3:])
900 sys.tracebacklimit = 0
901 self.assertEqual(extract(), [])
902 sys.tracebacklimit = -1
903 self.assertEqual(extract(), [])
904
905 def test_format_exception(self):
906 try:
907 self.last_raises5()
908 except Exception:
909 exc_type, exc_value, tb = sys.exc_info()
910 # [1:-1] to exclude "Traceback (...)" header and
911 # exception type and value
912 def extract(**kwargs):
913 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
914
915 with support.swap_attr(sys, 'tracebacklimit', 1000):
916 nolim = extract()
917 self.assertEqual(len(nolim), 5+1)
918 self.assertEqual(extract(limit=2), nolim[:2])
919 self.assertEqual(extract(limit=10), nolim)
920 self.assertEqual(extract(limit=-2), nolim[-2:])
921 self.assertEqual(extract(limit=-10), nolim)
922 self.assertEqual(extract(limit=0), [])
923 del sys.tracebacklimit
924 self.assertEqual(extract(), nolim)
925 sys.tracebacklimit = 2
926 self.assertEqual(extract(), nolim[:2])
927 self.assertEqual(extract(limit=3), nolim[:3])
928 self.assertEqual(extract(limit=-3), nolim[-3:])
929 sys.tracebacklimit = 0
930 self.assertEqual(extract(), [])
931 sys.tracebacklimit = -1
932 self.assertEqual(extract(), [])
933
934
Andrew Kuchling173a1572013-09-15 18:15:56 -0400935class MiscTracebackCases(unittest.TestCase):
936 #
937 # Check non-printing functions in traceback module
938 #
939
940 def test_clear(self):
941 def outer():
942 middle()
943 def middle():
944 inner()
945 def inner():
946 i = 1
947 1/0
948
949 try:
950 outer()
951 except:
952 type_, value, tb = sys.exc_info()
953
954 # Initial assertion: there's one local in the inner frame.
955 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
956 self.assertEqual(len(inner_frame.f_locals), 1)
957
958 # Clear traceback frames
959 traceback.clear_frames(tb)
960
961 # Local variable dict should now be empty.
962 self.assertEqual(len(inner_frame.f_locals), 0)
963
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300964 def test_extract_stack(self):
965 def extract():
966 return traceback.extract_stack()
967 result = extract()
968 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300969 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300970 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
971 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
972 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300973 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300974
Andrew Kuchling173a1572013-09-15 18:15:56 -0400975
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300976class TestFrame(unittest.TestCase):
977
978 def test_basics(self):
979 linecache.clearcache()
980 linecache.lazycache("f", globals())
981 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300982 self.assertEqual(f,
983 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
984 self.assertEqual(tuple(f),
985 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
986 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
987 self.assertEqual(f, tuple(f))
988 # Since tuple.__eq__ doesn't support FrameSummary, the equality
989 # operator fallbacks to FrameSummary.__eq__.
990 self.assertEqual(tuple(f), f)
991 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +0300992 self.assertNotEqual(f, object())
993 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300994
995 def test_lazy_lines(self):
996 linecache.clearcache()
997 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
998 self.assertEqual(None, f._line)
999 linecache.lazycache("f", globals())
1000 self.assertEqual(
1001 '"""Test cases for traceback module"""',
1002 f.line)
1003
Pablo Galindo61eb9b52021-07-08 17:47:12 +01001004 def test_no_line(self):
1005 f = traceback.FrameSummary("f", None, "dummy")
1006 self.assertEqual(f.line, None)
1007
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001008 def test_explicit_line(self):
1009 f = traceback.FrameSummary("f", 1, "dummy", line="line")
1010 self.assertEqual("line", f.line)
1011
Berker Peksag9797b7a2018-09-10 20:02:33 +03001012 def test_len(self):
1013 f = traceback.FrameSummary("f", 1, "dummy", line="line")
1014 self.assertEqual(len(f), 4)
1015
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001016
1017class TestStack(unittest.TestCase):
1018
1019 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +03001020 def deeper():
1021 return list(traceback.walk_stack(None))
1022 s1 = list(traceback.walk_stack(None))
1023 s2 = deeper()
1024 self.assertEqual(len(s2) - len(s1), 1)
1025 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001026
1027 def test_walk_tb(self):
1028 try:
1029 1/0
1030 except Exception:
1031 _, _, tb = sys.exc_info()
1032 s = list(traceback.walk_tb(tb))
1033 self.assertEqual(len(s), 1)
1034
1035 def test_extract_stack(self):
1036 s = traceback.StackSummary.extract(traceback.walk_stack(None))
1037 self.assertIsInstance(s, traceback.StackSummary)
1038
1039 def test_extract_stack_limit(self):
1040 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
1041 self.assertEqual(len(s), 5)
1042
1043 def test_extract_stack_lookup_lines(self):
1044 linecache.clearcache()
1045 linecache.updatecache('/foo.py', globals())
1046 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001047 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001048 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
1049 linecache.clearcache()
1050 self.assertEqual(s[0].line, "import sys")
1051
1052 def test_extract_stackup_deferred_lookup_lines(self):
1053 linecache.clearcache()
1054 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001055 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001056 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
1057 self.assertEqual({}, linecache.cache)
1058 linecache.updatecache('/foo.py', globals())
1059 self.assertEqual(s[0].line, "import sys")
1060
1061 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001062 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001063 self.assertEqual(
1064 [' File "foo.py", line 1, in fred\n line\n'],
1065 s.format())
1066
Robert Collinsbbb8ade2015-03-16 15:27:16 +13001067 def test_from_list_edited_stack(self):
1068 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
1069 s[0] = ('foo.py', 2, 'fred', 'line')
1070 s2 = traceback.StackSummary.from_list(s)
1071 self.assertEqual(
1072 [' File "foo.py", line 2, in fred\n line\n'],
1073 s2.format())
1074
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001075 def test_format_smoke(self):
1076 # For detailed tests see the format_list tests, which consume the same
1077 # code.
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001078 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001079 self.assertEqual(
1080 [' File "foo.py", line 1, in fred\n line\n'],
1081 s.format())
1082
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001083 def test_locals(self):
1084 linecache.updatecache('/foo.py', globals())
1085 c = test_code('/foo.py', 'method')
1086 f = test_frame(c, globals(), {'something': 1})
1087 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
1088 self.assertEqual(s[0].locals, {'something': '1'})
1089
1090 def test_no_locals(self):
1091 linecache.updatecache('/foo.py', globals())
1092 c = test_code('/foo.py', 'method')
1093 f = test_frame(c, globals(), {'something': 1})
1094 s = traceback.StackSummary.extract(iter([(f, 6)]))
1095 self.assertEqual(s[0].locals, None)
1096
1097 def test_format_locals(self):
1098 def some_inner(k, v):
1099 a = 1
1100 b = 2
1101 return traceback.StackSummary.extract(
1102 traceback.walk_stack(None), capture_locals=True, limit=1)
1103 s = some_inner(3, 4)
1104 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +03001105 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001106 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001107 ' a = 1\n'
1108 ' b = 2\n'
1109 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001110 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001111 ], s.format())
1112
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001113class TestTracebackException(unittest.TestCase):
1114
1115 def test_smoke(self):
1116 try:
1117 1/0
1118 except Exception:
1119 exc_info = sys.exc_info()
1120 exc = traceback.TracebackException(*exc_info)
1121 expected_stack = traceback.StackSummary.extract(
1122 traceback.walk_tb(exc_info[2]))
1123 self.assertEqual(None, exc.__cause__)
1124 self.assertEqual(None, exc.__context__)
1125 self.assertEqual(False, exc.__suppress_context__)
1126 self.assertEqual(expected_stack, exc.stack)
1127 self.assertEqual(exc_info[0], exc.exc_type)
1128 self.assertEqual(str(exc_info[1]), str(exc))
1129
1130 def test_from_exception(self):
1131 # Check all the parameters are accepted.
1132 def foo():
1133 1/0
1134 try:
1135 foo()
1136 except Exception as e:
1137 exc_info = sys.exc_info()
1138 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001139 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1140 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001141 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001142 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001143 expected_stack = self.expected_stack
1144 exc = self.exc
1145 self.assertEqual(None, exc.__cause__)
1146 self.assertEqual(None, exc.__context__)
1147 self.assertEqual(False, exc.__suppress_context__)
1148 self.assertEqual(expected_stack, exc.stack)
1149 self.assertEqual(exc_info[0], exc.exc_type)
1150 self.assertEqual(str(exc_info[1]), str(exc))
1151
1152 def test_cause(self):
1153 try:
1154 try:
1155 1/0
1156 finally:
1157 exc_info_context = sys.exc_info()
1158 exc_context = traceback.TracebackException(*exc_info_context)
1159 cause = Exception("cause")
1160 raise Exception("uh oh") from cause
1161 except Exception:
1162 exc_info = sys.exc_info()
1163 exc = traceback.TracebackException(*exc_info)
1164 expected_stack = traceback.StackSummary.extract(
1165 traceback.walk_tb(exc_info[2]))
1166 exc_cause = traceback.TracebackException(Exception, cause, None)
1167 self.assertEqual(exc_cause, exc.__cause__)
1168 self.assertEqual(exc_context, exc.__context__)
1169 self.assertEqual(True, exc.__suppress_context__)
1170 self.assertEqual(expected_stack, exc.stack)
1171 self.assertEqual(exc_info[0], exc.exc_type)
1172 self.assertEqual(str(exc_info[1]), str(exc))
1173
1174 def test_context(self):
1175 try:
1176 try:
1177 1/0
1178 finally:
1179 exc_info_context = sys.exc_info()
1180 exc_context = traceback.TracebackException(*exc_info_context)
1181 raise Exception("uh oh")
1182 except Exception:
1183 exc_info = sys.exc_info()
1184 exc = traceback.TracebackException(*exc_info)
1185 expected_stack = traceback.StackSummary.extract(
1186 traceback.walk_tb(exc_info[2]))
1187 self.assertEqual(None, exc.__cause__)
1188 self.assertEqual(exc_context, exc.__context__)
1189 self.assertEqual(False, exc.__suppress_context__)
1190 self.assertEqual(expected_stack, exc.stack)
1191 self.assertEqual(exc_info[0], exc.exc_type)
1192 self.assertEqual(str(exc_info[1]), str(exc))
1193
Irit Katriel6dfd1732021-01-12 22:14:27 +00001194 def test_long_context_chain(self):
1195 def f():
1196 try:
1197 1/0
1198 except:
1199 f()
1200
1201 try:
1202 f()
1203 except RecursionError:
1204 exc_info = sys.exc_info()
1205 else:
1206 self.fail("Exception not raised")
1207
1208 te = traceback.TracebackException(*exc_info)
1209 res = list(te.format())
1210
1211 # many ZeroDiv errors followed by the RecursionError
1212 self.assertGreater(len(res), sys.getrecursionlimit())
1213 self.assertGreater(
1214 len([l for l in res if 'ZeroDivisionError:' in l]),
1215 sys.getrecursionlimit() * 0.5)
1216 self.assertIn(
1217 "RecursionError: maximum recursion depth exceeded", res[-1])
1218
Irit Katriel4c94d742021-01-15 02:45:02 +00001219 def test_compact_with_cause(self):
1220 try:
1221 try:
1222 1/0
1223 finally:
1224 cause = Exception("cause")
1225 raise Exception("uh oh") from cause
1226 except Exception:
1227 exc_info = sys.exc_info()
1228 exc = traceback.TracebackException(*exc_info, compact=True)
1229 expected_stack = traceback.StackSummary.extract(
1230 traceback.walk_tb(exc_info[2]))
1231 exc_cause = traceback.TracebackException(Exception, cause, None)
1232 self.assertEqual(exc_cause, exc.__cause__)
1233 self.assertEqual(None, exc.__context__)
1234 self.assertEqual(True, exc.__suppress_context__)
1235 self.assertEqual(expected_stack, exc.stack)
1236 self.assertEqual(exc_info[0], exc.exc_type)
1237 self.assertEqual(str(exc_info[1]), str(exc))
1238
1239 def test_compact_no_cause(self):
1240 try:
1241 try:
1242 1/0
1243 finally:
1244 exc_info_context = sys.exc_info()
1245 exc_context = traceback.TracebackException(*exc_info_context)
1246 raise Exception("uh oh")
1247 except Exception:
1248 exc_info = sys.exc_info()
1249 exc = traceback.TracebackException(*exc_info, compact=True)
1250 expected_stack = traceback.StackSummary.extract(
1251 traceback.walk_tb(exc_info[2]))
1252 self.assertEqual(None, exc.__cause__)
1253 self.assertEqual(exc_context, exc.__context__)
1254 self.assertEqual(False, exc.__suppress_context__)
1255 self.assertEqual(expected_stack, exc.stack)
1256 self.assertEqual(exc_info[0], exc.exc_type)
1257 self.assertEqual(str(exc_info[1]), str(exc))
1258
Irit Katriel427613f2020-12-01 01:35:25 +00001259 def test_no_refs_to_exception_and_traceback_objects(self):
1260 try:
1261 1/0
1262 except Exception:
1263 exc_info = sys.exc_info()
1264
1265 refcnt1 = sys.getrefcount(exc_info[1])
1266 refcnt2 = sys.getrefcount(exc_info[2])
1267 exc = traceback.TracebackException(*exc_info)
1268 self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
1269 self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
1270
Irit Katriel44ca05a2020-11-27 16:38:54 +00001271 def test_comparison_basic(self):
Serhiy Storchaka662db122019-08-08 08:42:54 +03001272 try:
1273 1/0
1274 except Exception:
1275 exc_info = sys.exc_info()
1276 exc = traceback.TracebackException(*exc_info)
1277 exc2 = traceback.TracebackException(*exc_info)
1278 self.assertIsNot(exc, exc2)
1279 self.assertEqual(exc, exc2)
1280 self.assertNotEqual(exc, object())
1281 self.assertEqual(exc, ALWAYS_EQ)
1282
Irit Katriel44ca05a2020-11-27 16:38:54 +00001283 def test_comparison_params_variations(self):
1284 def raise_exc():
1285 try:
1286 raise ValueError('bad value')
1287 except:
1288 raise
1289
1290 def raise_with_locals():
1291 x, y = 1, 2
1292 raise_exc()
1293
1294 try:
1295 raise_with_locals()
1296 except Exception:
1297 exc_info = sys.exc_info()
1298
1299 exc = traceback.TracebackException(*exc_info)
1300 exc1 = traceback.TracebackException(*exc_info, limit=10)
1301 exc2 = traceback.TracebackException(*exc_info, limit=2)
1302
1303 self.assertEqual(exc, exc1) # limit=10 gets all frames
1304 self.assertNotEqual(exc, exc2) # limit=2 truncates the output
1305
1306 # locals change the output
1307 exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
1308 self.assertNotEqual(exc, exc3)
1309
1310 # there are no locals in the innermost frame
1311 exc4 = traceback.TracebackException(*exc_info, limit=-1)
1312 exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
1313 self.assertEqual(exc4, exc5)
1314
1315 # there are locals in the next-to-innermost frame
1316 exc6 = traceback.TracebackException(*exc_info, limit=-2)
1317 exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
1318 self.assertNotEqual(exc6, exc7)
1319
Irit Katriel427613f2020-12-01 01:35:25 +00001320 def test_comparison_equivalent_exceptions_are_equal(self):
1321 excs = []
1322 for _ in range(2):
1323 try:
1324 1/0
1325 except:
1326 excs.append(traceback.TracebackException(*sys.exc_info()))
1327 self.assertEqual(excs[0], excs[1])
1328 self.assertEqual(list(excs[0].format()), list(excs[1].format()))
1329
Zane Bitterde860732017-10-17 17:29:39 -04001330 def test_unhashable(self):
1331 class UnhashableException(Exception):
1332 def __eq__(self, other):
1333 return True
1334
1335 ex1 = UnhashableException('ex1')
1336 ex2 = UnhashableException('ex2')
1337 try:
1338 raise ex2 from ex1
1339 except UnhashableException:
1340 try:
1341 raise ex1
1342 except UnhashableException:
1343 exc_info = sys.exc_info()
1344 exc = traceback.TracebackException(*exc_info)
1345 formatted = list(exc.format())
1346 self.assertIn('UnhashableException: ex2\n', formatted[2])
1347 self.assertIn('UnhashableException: ex1\n', formatted[6])
1348
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001349 def test_limit(self):
1350 def recurse(n):
1351 if n:
1352 recurse(n-1)
1353 else:
1354 1/0
1355 try:
1356 recurse(10)
1357 except Exception:
1358 exc_info = sys.exc_info()
1359 exc = traceback.TracebackException(*exc_info, limit=5)
1360 expected_stack = traceback.StackSummary.extract(
1361 traceback.walk_tb(exc_info[2]), limit=5)
1362 self.assertEqual(expected_stack, exc.stack)
1363
1364 def test_lookup_lines(self):
1365 linecache.clearcache()
1366 e = Exception("uh oh")
1367 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001368 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001369 tb = test_tb(f, 6, None)
1370 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
Irit Katriel44ca05a2020-11-27 16:38:54 +00001371 self.assertEqual(linecache.cache, {})
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001372 linecache.updatecache('/foo.py', globals())
1373 self.assertEqual(exc.stack[0].line, "import sys")
1374
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001375 def test_locals(self):
1376 linecache.updatecache('/foo.py', globals())
1377 e = Exception("uh oh")
1378 c = test_code('/foo.py', 'method')
1379 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1380 tb = test_tb(f, 6, None)
1381 exc = traceback.TracebackException(
1382 Exception, e, tb, capture_locals=True)
1383 self.assertEqual(
1384 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1385
1386 def test_no_locals(self):
1387 linecache.updatecache('/foo.py', globals())
1388 e = Exception("uh oh")
1389 c = test_code('/foo.py', 'method')
1390 f = test_frame(c, globals(), {'something': 1})
1391 tb = test_tb(f, 6, None)
1392 exc = traceback.TracebackException(Exception, e, tb)
1393 self.assertEqual(exc.stack[0].locals, None)
1394
Berker Peksagc3f417d2015-07-24 17:36:21 +03001395 def test_traceback_header(self):
1396 # do not print a traceback header if exc_traceback is None
1397 # see issue #24695
1398 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1399 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1400
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001401
Berker Peksag716b3d32015-04-08 09:47:14 +03001402class MiscTest(unittest.TestCase):
1403
1404 def test_all(self):
1405 expected = set()
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001406 denylist = {'print_list'}
Berker Peksag716b3d32015-04-08 09:47:14 +03001407 for name in dir(traceback):
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001408 if name.startswith('_') or name in denylist:
Berker Peksag716b3d32015-04-08 09:47:14 +03001409 continue
1410 module_object = getattr(traceback, name)
1411 if getattr(module_object, '__module__', None) == 'traceback':
1412 expected.add(name)
1413 self.assertCountEqual(traceback.__all__, expected)
1414
Fred Drake2e2be372001-09-20 21:33:42 +00001415
1416if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001417 unittest.main()