blob: ee18383478c4bc43e7a2ea637c5d5996906cac75 [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
Miss Islington (bot)6b996d62021-09-08 09:32:19 -0700784 def test_format_exception_only_qualname(self):
785 class A:
786 class B:
787 class X(Exception):
788 def __str__(self):
789 return "I am X"
790 pass
791 err = self.get_report(A.B.X())
792 str_value = 'I am X'
793 str_name = '.'.join([A.B.X.__module__, A.B.X.__qualname__])
794 exp = "%s: %s\n" % (str_name, str_value)
795 self.assertEqual(exp, err)
796
Benjamin Petersone6528212008-07-15 15:32:09 +0000797
798class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
799 #
800 # This checks reporting through the 'traceback' module, with both
801 # format_exception() and print_exception().
802 #
803
804 def get_report(self, e):
805 e = self.get_exception(e)
806 s = ''.join(
807 traceback.format_exception(type(e), e, e.__traceback__))
808 with captured_output("stderr") as sio:
809 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000810 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000811 return s
812
813
814class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
815 #
816 # This checks built-in reporting by the interpreter.
817 #
818
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200819 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000820 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200821 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000822 e = self.get_exception(e)
823 with captured_output("stderr") as s:
824 exception_print(e)
825 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000826
827
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300828class LimitTests(unittest.TestCase):
829
830 ''' Tests for limit argument.
831 It's enough to test extact_tb, extract_stack and format_exception '''
832
833 def last_raises1(self):
834 raise Exception('Last raised')
835
836 def last_raises2(self):
837 self.last_raises1()
838
839 def last_raises3(self):
840 self.last_raises2()
841
842 def last_raises4(self):
843 self.last_raises3()
844
845 def last_raises5(self):
846 self.last_raises4()
847
848 def last_returns_frame1(self):
849 return sys._getframe()
850
851 def last_returns_frame2(self):
852 return self.last_returns_frame1()
853
854 def last_returns_frame3(self):
855 return self.last_returns_frame2()
856
857 def last_returns_frame4(self):
858 return self.last_returns_frame3()
859
860 def last_returns_frame5(self):
861 return self.last_returns_frame4()
862
863 def test_extract_stack(self):
864 frame = self.last_returns_frame5()
865 def extract(**kwargs):
866 return traceback.extract_stack(frame, **kwargs)
867 def assertEqualExcept(actual, expected, ignore):
868 self.assertEqual(actual[:ignore], expected[:ignore])
869 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
870 self.assertEqual(len(actual), len(expected))
871
872 with support.swap_attr(sys, 'tracebacklimit', 1000):
873 nolim = extract()
874 self.assertGreater(len(nolim), 5)
875 self.assertEqual(extract(limit=2), nolim[-2:])
876 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
877 self.assertEqual(extract(limit=-2), nolim[:2])
878 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
879 self.assertEqual(extract(limit=0), [])
880 del sys.tracebacklimit
881 assertEqualExcept(extract(), nolim, -5-1)
882 sys.tracebacklimit = 2
883 self.assertEqual(extract(), nolim[-2:])
884 self.assertEqual(extract(limit=3), nolim[-3:])
885 self.assertEqual(extract(limit=-3), nolim[:3])
886 sys.tracebacklimit = 0
887 self.assertEqual(extract(), [])
888 sys.tracebacklimit = -1
889 self.assertEqual(extract(), [])
890
891 def test_extract_tb(self):
892 try:
893 self.last_raises5()
894 except Exception:
895 exc_type, exc_value, tb = sys.exc_info()
896 def extract(**kwargs):
897 return traceback.extract_tb(tb, **kwargs)
898
899 with support.swap_attr(sys, 'tracebacklimit', 1000):
900 nolim = extract()
901 self.assertEqual(len(nolim), 5+1)
902 self.assertEqual(extract(limit=2), nolim[:2])
903 self.assertEqual(extract(limit=10), nolim)
904 self.assertEqual(extract(limit=-2), nolim[-2:])
905 self.assertEqual(extract(limit=-10), nolim)
906 self.assertEqual(extract(limit=0), [])
907 del sys.tracebacklimit
908 self.assertEqual(extract(), nolim)
909 sys.tracebacklimit = 2
910 self.assertEqual(extract(), nolim[:2])
911 self.assertEqual(extract(limit=3), nolim[:3])
912 self.assertEqual(extract(limit=-3), nolim[-3:])
913 sys.tracebacklimit = 0
914 self.assertEqual(extract(), [])
915 sys.tracebacklimit = -1
916 self.assertEqual(extract(), [])
917
918 def test_format_exception(self):
919 try:
920 self.last_raises5()
921 except Exception:
922 exc_type, exc_value, tb = sys.exc_info()
923 # [1:-1] to exclude "Traceback (...)" header and
924 # exception type and value
925 def extract(**kwargs):
926 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
927
928 with support.swap_attr(sys, 'tracebacklimit', 1000):
929 nolim = extract()
930 self.assertEqual(len(nolim), 5+1)
931 self.assertEqual(extract(limit=2), nolim[:2])
932 self.assertEqual(extract(limit=10), nolim)
933 self.assertEqual(extract(limit=-2), nolim[-2:])
934 self.assertEqual(extract(limit=-10), nolim)
935 self.assertEqual(extract(limit=0), [])
936 del sys.tracebacklimit
937 self.assertEqual(extract(), nolim)
938 sys.tracebacklimit = 2
939 self.assertEqual(extract(), nolim[:2])
940 self.assertEqual(extract(limit=3), nolim[:3])
941 self.assertEqual(extract(limit=-3), nolim[-3:])
942 sys.tracebacklimit = 0
943 self.assertEqual(extract(), [])
944 sys.tracebacklimit = -1
945 self.assertEqual(extract(), [])
946
947
Andrew Kuchling173a1572013-09-15 18:15:56 -0400948class MiscTracebackCases(unittest.TestCase):
949 #
950 # Check non-printing functions in traceback module
951 #
952
953 def test_clear(self):
954 def outer():
955 middle()
956 def middle():
957 inner()
958 def inner():
959 i = 1
960 1/0
961
962 try:
963 outer()
964 except:
965 type_, value, tb = sys.exc_info()
966
967 # Initial assertion: there's one local in the inner frame.
968 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
969 self.assertEqual(len(inner_frame.f_locals), 1)
970
971 # Clear traceback frames
972 traceback.clear_frames(tb)
973
974 # Local variable dict should now be empty.
975 self.assertEqual(len(inner_frame.f_locals), 0)
976
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300977 def test_extract_stack(self):
978 def extract():
979 return traceback.extract_stack()
980 result = extract()
981 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300982 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300983 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
984 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
985 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300986 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300987
Andrew Kuchling173a1572013-09-15 18:15:56 -0400988
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300989class TestFrame(unittest.TestCase):
990
991 def test_basics(self):
992 linecache.clearcache()
993 linecache.lazycache("f", globals())
994 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300995 self.assertEqual(f,
996 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
997 self.assertEqual(tuple(f),
998 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
999 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
1000 self.assertEqual(f, tuple(f))
1001 # Since tuple.__eq__ doesn't support FrameSummary, the equality
1002 # operator fallbacks to FrameSummary.__eq__.
1003 self.assertEqual(tuple(f), f)
1004 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +03001005 self.assertNotEqual(f, object())
1006 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001007
1008 def test_lazy_lines(self):
1009 linecache.clearcache()
1010 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
1011 self.assertEqual(None, f._line)
1012 linecache.lazycache("f", globals())
1013 self.assertEqual(
1014 '"""Test cases for traceback module"""',
1015 f.line)
1016
Pablo Galindo61eb9b52021-07-08 17:47:12 +01001017 def test_no_line(self):
1018 f = traceback.FrameSummary("f", None, "dummy")
1019 self.assertEqual(f.line, None)
1020
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001021 def test_explicit_line(self):
1022 f = traceback.FrameSummary("f", 1, "dummy", line="line")
1023 self.assertEqual("line", f.line)
1024
Berker Peksag9797b7a2018-09-10 20:02:33 +03001025 def test_len(self):
1026 f = traceback.FrameSummary("f", 1, "dummy", line="line")
1027 self.assertEqual(len(f), 4)
1028
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001029
1030class TestStack(unittest.TestCase):
1031
1032 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +03001033 def deeper():
1034 return list(traceback.walk_stack(None))
1035 s1 = list(traceback.walk_stack(None))
1036 s2 = deeper()
1037 self.assertEqual(len(s2) - len(s1), 1)
1038 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001039
1040 def test_walk_tb(self):
1041 try:
1042 1/0
1043 except Exception:
1044 _, _, tb = sys.exc_info()
1045 s = list(traceback.walk_tb(tb))
1046 self.assertEqual(len(s), 1)
1047
1048 def test_extract_stack(self):
1049 s = traceback.StackSummary.extract(traceback.walk_stack(None))
1050 self.assertIsInstance(s, traceback.StackSummary)
1051
1052 def test_extract_stack_limit(self):
1053 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
1054 self.assertEqual(len(s), 5)
1055
1056 def test_extract_stack_lookup_lines(self):
1057 linecache.clearcache()
1058 linecache.updatecache('/foo.py', globals())
1059 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001060 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001061 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
1062 linecache.clearcache()
1063 self.assertEqual(s[0].line, "import sys")
1064
1065 def test_extract_stackup_deferred_lookup_lines(self):
1066 linecache.clearcache()
1067 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001068 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001069 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
1070 self.assertEqual({}, linecache.cache)
1071 linecache.updatecache('/foo.py', globals())
1072 self.assertEqual(s[0].line, "import sys")
1073
1074 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001075 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001076 self.assertEqual(
1077 [' File "foo.py", line 1, in fred\n line\n'],
1078 s.format())
1079
Robert Collinsbbb8ade2015-03-16 15:27:16 +13001080 def test_from_list_edited_stack(self):
1081 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
1082 s[0] = ('foo.py', 2, 'fred', 'line')
1083 s2 = traceback.StackSummary.from_list(s)
1084 self.assertEqual(
1085 [' File "foo.py", line 2, in fred\n line\n'],
1086 s2.format())
1087
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001088 def test_format_smoke(self):
1089 # For detailed tests see the format_list tests, which consume the same
1090 # code.
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001091 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001092 self.assertEqual(
1093 [' File "foo.py", line 1, in fred\n line\n'],
1094 s.format())
1095
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001096 def test_locals(self):
1097 linecache.updatecache('/foo.py', globals())
1098 c = test_code('/foo.py', 'method')
1099 f = test_frame(c, globals(), {'something': 1})
1100 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
1101 self.assertEqual(s[0].locals, {'something': '1'})
1102
1103 def test_no_locals(self):
1104 linecache.updatecache('/foo.py', globals())
1105 c = test_code('/foo.py', 'method')
1106 f = test_frame(c, globals(), {'something': 1})
1107 s = traceback.StackSummary.extract(iter([(f, 6)]))
1108 self.assertEqual(s[0].locals, None)
1109
1110 def test_format_locals(self):
1111 def some_inner(k, v):
1112 a = 1
1113 b = 2
1114 return traceback.StackSummary.extract(
1115 traceback.walk_stack(None), capture_locals=True, limit=1)
1116 s = some_inner(3, 4)
1117 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +03001118 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001119 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001120 ' a = 1\n'
1121 ' b = 2\n'
1122 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001123 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001124 ], s.format())
1125
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001126class TestTracebackException(unittest.TestCase):
1127
1128 def test_smoke(self):
1129 try:
1130 1/0
1131 except Exception:
1132 exc_info = sys.exc_info()
1133 exc = traceback.TracebackException(*exc_info)
1134 expected_stack = traceback.StackSummary.extract(
1135 traceback.walk_tb(exc_info[2]))
1136 self.assertEqual(None, exc.__cause__)
1137 self.assertEqual(None, exc.__context__)
1138 self.assertEqual(False, exc.__suppress_context__)
1139 self.assertEqual(expected_stack, exc.stack)
1140 self.assertEqual(exc_info[0], exc.exc_type)
1141 self.assertEqual(str(exc_info[1]), str(exc))
1142
1143 def test_from_exception(self):
1144 # Check all the parameters are accepted.
1145 def foo():
1146 1/0
1147 try:
1148 foo()
1149 except Exception as e:
1150 exc_info = sys.exc_info()
1151 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001152 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1153 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001154 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001155 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001156 expected_stack = self.expected_stack
1157 exc = self.exc
1158 self.assertEqual(None, exc.__cause__)
1159 self.assertEqual(None, exc.__context__)
1160 self.assertEqual(False, exc.__suppress_context__)
1161 self.assertEqual(expected_stack, exc.stack)
1162 self.assertEqual(exc_info[0], exc.exc_type)
1163 self.assertEqual(str(exc_info[1]), str(exc))
1164
1165 def test_cause(self):
1166 try:
1167 try:
1168 1/0
1169 finally:
1170 exc_info_context = sys.exc_info()
1171 exc_context = traceback.TracebackException(*exc_info_context)
1172 cause = Exception("cause")
1173 raise Exception("uh oh") from cause
1174 except Exception:
1175 exc_info = sys.exc_info()
1176 exc = traceback.TracebackException(*exc_info)
1177 expected_stack = traceback.StackSummary.extract(
1178 traceback.walk_tb(exc_info[2]))
1179 exc_cause = traceback.TracebackException(Exception, cause, None)
1180 self.assertEqual(exc_cause, exc.__cause__)
1181 self.assertEqual(exc_context, exc.__context__)
1182 self.assertEqual(True, exc.__suppress_context__)
1183 self.assertEqual(expected_stack, exc.stack)
1184 self.assertEqual(exc_info[0], exc.exc_type)
1185 self.assertEqual(str(exc_info[1]), str(exc))
1186
1187 def test_context(self):
1188 try:
1189 try:
1190 1/0
1191 finally:
1192 exc_info_context = sys.exc_info()
1193 exc_context = traceback.TracebackException(*exc_info_context)
1194 raise Exception("uh oh")
1195 except Exception:
1196 exc_info = sys.exc_info()
1197 exc = traceback.TracebackException(*exc_info)
1198 expected_stack = traceback.StackSummary.extract(
1199 traceback.walk_tb(exc_info[2]))
1200 self.assertEqual(None, exc.__cause__)
1201 self.assertEqual(exc_context, exc.__context__)
1202 self.assertEqual(False, exc.__suppress_context__)
1203 self.assertEqual(expected_stack, exc.stack)
1204 self.assertEqual(exc_info[0], exc.exc_type)
1205 self.assertEqual(str(exc_info[1]), str(exc))
1206
Irit Katriel6dfd1732021-01-12 22:14:27 +00001207 def test_long_context_chain(self):
1208 def f():
1209 try:
1210 1/0
1211 except:
1212 f()
1213
1214 try:
1215 f()
1216 except RecursionError:
1217 exc_info = sys.exc_info()
1218 else:
1219 self.fail("Exception not raised")
1220
1221 te = traceback.TracebackException(*exc_info)
1222 res = list(te.format())
1223
1224 # many ZeroDiv errors followed by the RecursionError
1225 self.assertGreater(len(res), sys.getrecursionlimit())
1226 self.assertGreater(
1227 len([l for l in res if 'ZeroDivisionError:' in l]),
1228 sys.getrecursionlimit() * 0.5)
1229 self.assertIn(
1230 "RecursionError: maximum recursion depth exceeded", res[-1])
1231
Irit Katriel4c94d742021-01-15 02:45:02 +00001232 def test_compact_with_cause(self):
1233 try:
1234 try:
1235 1/0
1236 finally:
1237 cause = Exception("cause")
1238 raise Exception("uh oh") from cause
1239 except Exception:
1240 exc_info = sys.exc_info()
1241 exc = traceback.TracebackException(*exc_info, compact=True)
1242 expected_stack = traceback.StackSummary.extract(
1243 traceback.walk_tb(exc_info[2]))
1244 exc_cause = traceback.TracebackException(Exception, cause, None)
1245 self.assertEqual(exc_cause, exc.__cause__)
1246 self.assertEqual(None, exc.__context__)
1247 self.assertEqual(True, exc.__suppress_context__)
1248 self.assertEqual(expected_stack, exc.stack)
1249 self.assertEqual(exc_info[0], exc.exc_type)
1250 self.assertEqual(str(exc_info[1]), str(exc))
1251
1252 def test_compact_no_cause(self):
1253 try:
1254 try:
1255 1/0
1256 finally:
1257 exc_info_context = sys.exc_info()
1258 exc_context = traceback.TracebackException(*exc_info_context)
1259 raise Exception("uh oh")
1260 except Exception:
1261 exc_info = sys.exc_info()
1262 exc = traceback.TracebackException(*exc_info, compact=True)
1263 expected_stack = traceback.StackSummary.extract(
1264 traceback.walk_tb(exc_info[2]))
1265 self.assertEqual(None, exc.__cause__)
1266 self.assertEqual(exc_context, exc.__context__)
1267 self.assertEqual(False, exc.__suppress_context__)
1268 self.assertEqual(expected_stack, exc.stack)
1269 self.assertEqual(exc_info[0], exc.exc_type)
1270 self.assertEqual(str(exc_info[1]), str(exc))
1271
Irit Katriel427613f2020-12-01 01:35:25 +00001272 def test_no_refs_to_exception_and_traceback_objects(self):
1273 try:
1274 1/0
1275 except Exception:
1276 exc_info = sys.exc_info()
1277
1278 refcnt1 = sys.getrefcount(exc_info[1])
1279 refcnt2 = sys.getrefcount(exc_info[2])
1280 exc = traceback.TracebackException(*exc_info)
1281 self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
1282 self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
1283
Irit Katriel44ca05a2020-11-27 16:38:54 +00001284 def test_comparison_basic(self):
Serhiy Storchaka662db122019-08-08 08:42:54 +03001285 try:
1286 1/0
1287 except Exception:
1288 exc_info = sys.exc_info()
1289 exc = traceback.TracebackException(*exc_info)
1290 exc2 = traceback.TracebackException(*exc_info)
1291 self.assertIsNot(exc, exc2)
1292 self.assertEqual(exc, exc2)
1293 self.assertNotEqual(exc, object())
1294 self.assertEqual(exc, ALWAYS_EQ)
1295
Irit Katriel44ca05a2020-11-27 16:38:54 +00001296 def test_comparison_params_variations(self):
1297 def raise_exc():
1298 try:
1299 raise ValueError('bad value')
1300 except:
1301 raise
1302
1303 def raise_with_locals():
1304 x, y = 1, 2
1305 raise_exc()
1306
1307 try:
1308 raise_with_locals()
1309 except Exception:
1310 exc_info = sys.exc_info()
1311
1312 exc = traceback.TracebackException(*exc_info)
1313 exc1 = traceback.TracebackException(*exc_info, limit=10)
1314 exc2 = traceback.TracebackException(*exc_info, limit=2)
1315
1316 self.assertEqual(exc, exc1) # limit=10 gets all frames
1317 self.assertNotEqual(exc, exc2) # limit=2 truncates the output
1318
1319 # locals change the output
1320 exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
1321 self.assertNotEqual(exc, exc3)
1322
1323 # there are no locals in the innermost frame
1324 exc4 = traceback.TracebackException(*exc_info, limit=-1)
1325 exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
1326 self.assertEqual(exc4, exc5)
1327
1328 # there are locals in the next-to-innermost frame
1329 exc6 = traceback.TracebackException(*exc_info, limit=-2)
1330 exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
1331 self.assertNotEqual(exc6, exc7)
1332
Irit Katriel427613f2020-12-01 01:35:25 +00001333 def test_comparison_equivalent_exceptions_are_equal(self):
1334 excs = []
1335 for _ in range(2):
1336 try:
1337 1/0
1338 except:
1339 excs.append(traceback.TracebackException(*sys.exc_info()))
1340 self.assertEqual(excs[0], excs[1])
1341 self.assertEqual(list(excs[0].format()), list(excs[1].format()))
1342
Zane Bitterde860732017-10-17 17:29:39 -04001343 def test_unhashable(self):
1344 class UnhashableException(Exception):
1345 def __eq__(self, other):
1346 return True
1347
1348 ex1 = UnhashableException('ex1')
1349 ex2 = UnhashableException('ex2')
1350 try:
1351 raise ex2 from ex1
1352 except UnhashableException:
1353 try:
1354 raise ex1
1355 except UnhashableException:
1356 exc_info = sys.exc_info()
1357 exc = traceback.TracebackException(*exc_info)
1358 formatted = list(exc.format())
1359 self.assertIn('UnhashableException: ex2\n', formatted[2])
1360 self.assertIn('UnhashableException: ex1\n', formatted[6])
1361
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001362 def test_limit(self):
1363 def recurse(n):
1364 if n:
1365 recurse(n-1)
1366 else:
1367 1/0
1368 try:
1369 recurse(10)
1370 except Exception:
1371 exc_info = sys.exc_info()
1372 exc = traceback.TracebackException(*exc_info, limit=5)
1373 expected_stack = traceback.StackSummary.extract(
1374 traceback.walk_tb(exc_info[2]), limit=5)
1375 self.assertEqual(expected_stack, exc.stack)
1376
1377 def test_lookup_lines(self):
1378 linecache.clearcache()
1379 e = Exception("uh oh")
1380 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001381 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001382 tb = test_tb(f, 6, None)
1383 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
Irit Katriel44ca05a2020-11-27 16:38:54 +00001384 self.assertEqual(linecache.cache, {})
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001385 linecache.updatecache('/foo.py', globals())
1386 self.assertEqual(exc.stack[0].line, "import sys")
1387
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001388 def test_locals(self):
1389 linecache.updatecache('/foo.py', globals())
1390 e = Exception("uh oh")
1391 c = test_code('/foo.py', 'method')
1392 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1393 tb = test_tb(f, 6, None)
1394 exc = traceback.TracebackException(
1395 Exception, e, tb, capture_locals=True)
1396 self.assertEqual(
1397 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1398
1399 def test_no_locals(self):
1400 linecache.updatecache('/foo.py', globals())
1401 e = Exception("uh oh")
1402 c = test_code('/foo.py', 'method')
1403 f = test_frame(c, globals(), {'something': 1})
1404 tb = test_tb(f, 6, None)
1405 exc = traceback.TracebackException(Exception, e, tb)
1406 self.assertEqual(exc.stack[0].locals, None)
1407
Berker Peksagc3f417d2015-07-24 17:36:21 +03001408 def test_traceback_header(self):
1409 # do not print a traceback header if exc_traceback is None
1410 # see issue #24695
1411 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1412 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1413
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001414
Berker Peksag716b3d32015-04-08 09:47:14 +03001415class MiscTest(unittest.TestCase):
1416
1417 def test_all(self):
1418 expected = set()
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001419 denylist = {'print_list'}
Berker Peksag716b3d32015-04-08 09:47:14 +03001420 for name in dir(traceback):
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001421 if name.startswith('_') or name in denylist:
Berker Peksag716b3d32015-04-08 09:47:14 +03001422 continue
1423 module_object = getattr(traceback, name)
1424 if getattr(module_object, '__module__', None) == 'traceback':
1425 expected.add(name)
1426 self.assertCountEqual(traceback.__all__, expected)
1427
Fred Drake2e2be372001-09-20 21:33:42 +00001428
1429if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001430 unittest.main()