blob: 33bdda026662cc03edbe330c68c8f1753d6c59db [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
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00007import unittest
Benjamin Petersone6528212008-07-15 15:32:09 +00008import re
Serhiy Storchaka24559e42015-05-03 13:19:46 +03009from test import support
Hai Shideb01622020-07-06 20:29:49 +080010from test.support import Error, captured_output, cpython_only, ALWAYS_EQ
11from test.support.os_helper import TESTFN, unlink
Berker Peksagce643912015-05-06 06:33:17 +030012from test.support.script_helper import assert_python_ok
Victor Stinner9d279b82014-12-05 10:18:30 +010013import textwrap
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000014
15import traceback
16
Christian Heimes81ee3ef2008-05-04 22:42:01 +000017
Robert Collins6bc2c1e2015-03-05 12:07:57 +130018test_code = namedtuple('code', ['co_filename', 'co_name'])
Robert Collinsd7c7e0e2015-03-05 20:28:52 +130019test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals'])
Robert Collins6bc2c1e2015-03-05 12:07:57 +130020test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next'])
21
22
Martin Panterbb8b1cb2016-09-22 09:37:39 +000023class TracebackCases(unittest.TestCase):
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000024 # For now, a very minimal set of tests. I want to be sure that
25 # formatting of SyntaxErrors works based on changes for 2.1.
26
27 def get_exception_format(self, func, exc):
28 try:
29 func()
Guido van Rossumb940e112007-01-10 16:19:56 +000030 except exc as value:
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000031 return traceback.format_exception_only(exc, value)
32 else:
Collin Winter3add4d72007-08-29 23:37:32 +000033 raise ValueError("call did not raise exception")
Tim Peters7e01e282001-04-08 07:44:07 +000034
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000035 def syntax_error_with_caret(self):
36 compile("def fact(x):\n\treturn x!\n", "?", "exec")
37
Georg Brandl751899a2009-06-04 19:41:00 +000038 def syntax_error_with_caret_2(self):
39 compile("1 +\n", "?", "exec")
40
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000041 def syntax_error_bad_indentation(self):
Georg Brandl88fc6642007-02-09 21:28:07 +000042 compile("def spam():\n print(1)\n print(2)", "?", "exec")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000043
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020044 def syntax_error_with_caret_non_ascii(self):
45 compile('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', "?", "exec")
46
Florent Xicluna758fa5e2014-01-22 01:11:43 +010047 def syntax_error_bad_indentation2(self):
48 compile(" print(2)", "?", "exec")
49
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000050 def test_caret(self):
51 err = self.get_exception_format(self.syntax_error_with_caret,
52 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000053 self.assertEqual(len(err), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000054 self.assertTrue(err[1].strip() == "return x!")
Benjamin Peterson577473f2010-01-19 00:09:57 +000055 self.assertIn("^", err[2]) # third line has caret
Guido van Rossume61fd5b2007-07-11 12:20:59 +000056 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place
Tim Peters7e01e282001-04-08 07:44:07 +000057
Georg Brandl751899a2009-06-04 19:41:00 +000058 err = self.get_exception_format(self.syntax_error_with_caret_2,
59 SyntaxError)
Benjamin Peterson577473f2010-01-19 00:09:57 +000060 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010061 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070062 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Georg Brandl751899a2009-06-04 19:41:00 +000063
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020064 err = self.get_exception_format(self.syntax_error_with_caret_non_ascii,
65 SyntaxError)
66 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010067 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070068 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020069
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000070 def test_nocaret(self):
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000071 exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
72 err = traceback.format_exception_only(SyntaxError, exc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000073 self.assertEqual(len(err), 3)
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000074 self.assertEqual(err[1].strip(), "bad syntax")
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000075
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000076 def test_bad_indentation(self):
77 err = self.get_exception_format(self.syntax_error_bad_indentation,
78 IndentationError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000079 self.assertEqual(len(err), 4)
80 self.assertEqual(err[1].strip(), "print(2)")
Benjamin Peterson577473f2010-01-19 00:09:57 +000081 self.assertIn("^", err[2])
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070082 self.assertEqual(err[1].find(")") + 1, err[2].find("^"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000083
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070084 # No caret for "unexpected indent"
Florent Xicluna758fa5e2014-01-22 01:11:43 +010085 err = self.get_exception_format(self.syntax_error_bad_indentation2,
86 IndentationError)
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070087 self.assertEqual(len(err), 3)
Florent Xicluna758fa5e2014-01-22 01:11:43 +010088 self.assertEqual(err[1].strip(), "print(2)")
Florent Xicluna758fa5e2014-01-22 01:11:43 +010089
Thomas Wouters477c8d52006-05-27 19:21:47 +000090 def test_base_exception(self):
91 # Test that exceptions derived from BaseException are formatted right
92 e = KeyboardInterrupt()
93 lst = traceback.format_exception_only(e.__class__, e)
94 self.assertEqual(lst, ['KeyboardInterrupt\n'])
95
Thomas Wouters0e3f5912006-08-11 14:57:12 +000096 def test_format_exception_only_bad__str__(self):
97 class X(Exception):
98 def __str__(self):
99 1/0
100 err = traceback.format_exception_only(X, X())
101 self.assertEqual(len(err), 1)
102 str_value = '<unprintable %s object>' % X.__name__
Georg Brandl1a3284e2007-12-02 09:40:06 +0000103 if X.__module__ in ('__main__', 'builtins'):
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300104 str_name = X.__qualname__
Brett Cannon44c52612007-02-27 00:12:43 +0000105 else:
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300106 str_name = '.'.join([X.__module__, X.__qualname__])
Brett Cannon44c52612007-02-27 00:12:43 +0000107 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000108
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000109 def test_encoded_file(self):
110 # Test that tracebacks are correctly printed for encoded source files:
111 # - correct line number (Issue2384)
112 # - respect file encoding (Issue3975)
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)90d0cfb2018-11-16 21:02:58 +0530113 import sys, subprocess
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000114
115 # The spawned subprocess has its stdout redirected to a PIPE, and its
116 # encoding may be different from the current interpreter, on Windows
117 # at least.
118 process = subprocess.Popen([sys.executable, "-c",
119 "import sys; print(sys.stdout.encoding)"],
120 stdout=subprocess.PIPE,
121 stderr=subprocess.STDOUT)
122 stdout, stderr = process.communicate()
123 output_encoding = str(stdout, 'ascii').splitlines()[0]
124
125 def do_test(firstlines, message, charset, lineno):
126 # Raise the message in a subprocess, and catch the output
127 try:
Victor Stinner51d8c522016-02-08 17:57:02 +0100128 with open(TESTFN, "w", encoding=charset) as output:
129 output.write("""{0}if 1:
130 import traceback;
131 raise RuntimeError('{1}')
132 """.format(firstlines, message))
133
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000134 process = subprocess.Popen([sys.executable, TESTFN],
135 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
136 stdout, stderr = process.communicate()
137 stdout = stdout.decode(output_encoding).splitlines()
138 finally:
139 unlink(TESTFN)
140
141 # The source lines are encoded with the 'backslashreplace' handler
142 encoded_message = message.encode(output_encoding,
143 'backslashreplace')
144 # and we just decoded them with the output_encoding.
145 message_ascii = encoded_message.decode(output_encoding)
146
147 err_line = "raise RuntimeError('{0}')".format(message_ascii)
148 err_msg = "RuntimeError: {0}".format(message_ascii)
149
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000150 self.assertIn(("line %s" % lineno), stdout[1],
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000151 "Invalid line number: {0!r} instead of {1}".format(
152 stdout[1], lineno))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000153 self.assertTrue(stdout[2].endswith(err_line),
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000154 "Invalid traceback line: {0!r} instead of {1!r}".format(
155 stdout[2], err_line))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000156 self.assertTrue(stdout[3] == err_msg,
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000157 "Invalid error message: {0!r} instead of {1!r}".format(
158 stdout[3], err_msg))
159
160 do_test("", "foo", "ascii", 3)
161 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
162 if charset == "ascii":
163 text = "foo"
164 elif charset == "GBK":
165 text = "\u4E02\u5100"
166 else:
167 text = "h\xe9 ho"
168 do_test("# coding: {0}\n".format(charset),
169 text, charset, 4)
170 do_test("#!shebang\n# coding: {0}\n".format(charset),
171 text, charset, 5)
Serhiy Storchaka1064a132014-01-09 20:12:49 +0200172 do_test(" \t\f\n# coding: {0}\n".format(charset),
173 text, charset, 5)
Martin Panter614827c2016-04-19 04:05:59 +0000174 # Issue #18960: coding spec should have no effect
Victor Stinner51d8c522016-02-08 17:57:02 +0100175 do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000176
Victor Stinner9d279b82014-12-05 10:18:30 +0100177 def test_print_traceback_at_exit(self):
178 # Issue #22599: Ensure that it is possible to use the traceback module
179 # to display an exception at Python exit
180 code = textwrap.dedent("""
181 import sys
182 import traceback
183
184 class PrintExceptionAtExit(object):
185 def __init__(self):
186 try:
187 x = 1 / 0
188 except Exception:
189 self.exc_info = sys.exc_info()
190 # self.exc_info[1] (traceback) contains frames:
191 # explicitly clear the reference to self in the current
192 # frame to break a reference cycle
193 self = None
194
195 def __del__(self):
196 traceback.print_exception(*self.exc_info)
197
198 # Keep a reference in the module namespace to call the destructor
199 # when the module is unloaded
200 obj = PrintExceptionAtExit()
201 """)
202 rc, stdout, stderr = assert_python_ok('-c', code)
203 expected = [b'Traceback (most recent call last):',
204 b' File "<string>", line 8, in __init__',
205 b'ZeroDivisionError: division by zero']
206 self.assertEqual(stderr.splitlines(), expected)
207
Berker Peksagc3f417d2015-07-24 17:36:21 +0300208 def test_print_exception(self):
209 output = StringIO()
210 traceback.print_exception(
211 Exception, Exception("projector"), None, file=output
212 )
213 self.assertEqual(output.getvalue(), "Exception: projector\n")
214
Zackery Spytz91e93792020-11-05 15:18:44 -0700215 def test_print_exception_exc(self):
216 output = StringIO()
217 traceback.print_exception(Exception("projector"), file=output)
218 self.assertEqual(output.getvalue(), "Exception: projector\n")
219
220 def test_format_exception_exc(self):
221 e = Exception("projector")
222 output = traceback.format_exception(e)
223 self.assertEqual(output, ["Exception: projector\n"])
224 with self.assertRaisesRegex(ValueError, 'Both or neither'):
225 traceback.format_exception(e.__class__, e)
226 with self.assertRaisesRegex(ValueError, 'Both or neither'):
227 traceback.format_exception(e.__class__, tb=e.__traceback__)
228 with self.assertRaisesRegex(TypeError, 'positional-only'):
229 traceback.format_exception(exc=e)
230
231 def test_format_exception_only_exc(self):
232 output = traceback.format_exception_only(Exception("projector"))
233 self.assertEqual(output, ["Exception: projector\n"])
234
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000235
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000236class TracebackFormatTests(unittest.TestCase):
237
Antoine Pitrou58720d62013-08-05 23:26:40 +0200238 def some_exception(self):
239 raise KeyError('blah')
240
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200241 @cpython_only
Antoine Pitrou58720d62013-08-05 23:26:40 +0200242 def check_traceback_format(self, cleanup_func=None):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200243 from _testcapi import traceback_print
Georg Brandl236f7972009-04-05 14:28:42 +0000244 try:
Antoine Pitrou58720d62013-08-05 23:26:40 +0200245 self.some_exception()
Georg Brandl236f7972009-04-05 14:28:42 +0000246 except KeyError:
247 type_, value, tb = sys.exc_info()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200248 if cleanup_func is not None:
249 # Clear the inner frames, not this one
250 cleanup_func(tb.tb_next)
Georg Brandl236f7972009-04-05 14:28:42 +0000251 traceback_fmt = 'Traceback (most recent call last):\n' + \
252 ''.join(traceback.format_tb(tb))
253 file_ = StringIO()
254 traceback_print(tb, file_)
255 python_fmt = file_.getvalue()
Benjamin Petersond9fec152013-04-29 16:09:39 -0400256 # Call all _tb and _exc functions
257 with captured_output("stderr") as tbstderr:
258 traceback.print_tb(tb)
259 tbfile = StringIO()
260 traceback.print_tb(tb, file=tbfile)
261 with captured_output("stderr") as excstderr:
262 traceback.print_exc()
263 excfmt = traceback.format_exc()
264 excfile = StringIO()
265 traceback.print_exc(file=excfile)
Georg Brandl236f7972009-04-05 14:28:42 +0000266 else:
267 raise Error("unable to create test traceback string")
268
269 # Make sure that Python and the traceback module format the same thing
Ezio Melottib3aedd42010-11-20 19:04:17 +0000270 self.assertEqual(traceback_fmt, python_fmt)
Benjamin Petersond9fec152013-04-29 16:09:39 -0400271 # Now verify the _tb func output
272 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
273 # Now verify the _exc func output
274 self.assertEqual(excstderr.getvalue(), excfile.getvalue())
275 self.assertEqual(excfmt, excfile.getvalue())
Georg Brandl236f7972009-04-05 14:28:42 +0000276
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000277 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000278 tb_lines = python_fmt.splitlines()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200279 self.assertEqual(len(tb_lines), 5)
280 banner = tb_lines[0]
281 location, source_line = tb_lines[-2:]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000282 self.assertTrue(banner.startswith('Traceback'))
283 self.assertTrue(location.startswith(' File'))
284 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000285
Antoine Pitrou58720d62013-08-05 23:26:40 +0200286 def test_traceback_format(self):
287 self.check_traceback_format()
288
289 def test_traceback_format_with_cleared_frames(self):
290 # Check that traceback formatting also works with a clear()ed frame
291 def cleanup_tb(tb):
292 tb.tb_frame.clear()
293 self.check_traceback_format(cleanup_tb)
294
Benjamin Petersond9fec152013-04-29 16:09:39 -0400295 def test_stack_format(self):
296 # Verify _stack functions. Note we have to use _getframe(1) to
297 # compare them without this frame appearing in the output
298 with captured_output("stderr") as ststderr:
299 traceback.print_stack(sys._getframe(1))
300 stfile = StringIO()
301 traceback.print_stack(sys._getframe(1), file=stfile)
302 self.assertEqual(ststderr.getvalue(), stfile.getvalue())
303
304 stfmt = traceback.format_stack(sys._getframe(1))
305
306 self.assertEqual(ststderr.getvalue(), "".join(stfmt))
307
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300308 def test_print_stack(self):
309 def prn():
310 traceback.print_stack()
311 with captured_output("stderr") as stderr:
312 prn()
313 lineno = prn.__code__.co_firstlineno
314 self.assertEqual(stderr.getvalue().splitlines()[-4:], [
315 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3),
316 ' prn()',
317 ' File "%s", line %d, in prn' % (__file__, lineno+1),
318 ' traceback.print_stack()',
319 ])
320
Nick Coghland0034232016-08-15 13:11:34 +1000321 # issue 26823 - Shrink recursive tracebacks
322 def _check_recursive_traceback_display(self, render_exc):
323 # Always show full diffs when this test fails
324 # Note that rearranging things may require adjusting
325 # the relative line numbers in the expected tracebacks
326 self.maxDiff = None
327
328 # Check hitting the recursion limit
329 def f():
330 f()
331
332 with captured_output("stderr") as stderr_f:
333 try:
334 f()
Pablo Galindo293dd232019-11-19 21:34:03 +0000335 except RecursionError:
Nick Coghland0034232016-08-15 13:11:34 +1000336 render_exc()
337 else:
338 self.fail("no recursion occurred")
339
340 lineno_f = f.__code__.co_firstlineno
341 result_f = (
342 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400343 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000344 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400345 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000346 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400347 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000348 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400349 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000350 ' f()\n'
351 # XXX: The following line changes depending on whether the tests
352 # are run through the interactive interpreter or with -m
353 # It also varies depending on the platform (stack size)
354 # Fortunately, we don't care about exactness here, so we use regex
355 r' \[Previous line repeated (\d+) more times\]' '\n'
356 'RecursionError: maximum recursion depth exceeded\n'
357 )
358
359 expected = result_f.splitlines()
360 actual = stderr_f.getvalue().splitlines()
361
362 # Check the output text matches expectations
363 # 2nd last line contains the repetition count
364 self.assertEqual(actual[:-2], expected[:-2])
365 self.assertRegex(actual[-2], expected[-2])
codedragon3480ef92017-05-24 14:23:46 -0700366 # last line can have additional text appended
367 self.assertIn(expected[-1], actual[-1])
Nick Coghland0034232016-08-15 13:11:34 +1000368
369 # Check the recursion count is roughly as expected
370 rec_limit = sys.getrecursionlimit()
Serhiy Storchakab7281052016-09-12 00:52:40 +0300371 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
Nick Coghland0034232016-08-15 13:11:34 +1000372
373 # Check a known (limited) number of recursive invocations
374 def g(count=10):
375 if count:
376 return g(count-1)
377 raise ValueError
378
379 with captured_output("stderr") as stderr_g:
380 try:
381 g()
Pablo Galindo293dd232019-11-19 21:34:03 +0000382 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000383 render_exc()
384 else:
385 self.fail("no value error was raised")
386
387 lineno_g = g.__code__.co_firstlineno
388 result_g = (
Eric V. Smith451d0e32016-09-09 21:56:20 -0400389 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000390 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400391 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000392 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400393 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000394 ' return g(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700395 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400396 f' File "{__file__}", line {lineno_g+3}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000397 ' raise ValueError\n'
398 'ValueError\n'
399 )
400 tb_line = (
401 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400402 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000403 ' g()\n'
404 )
405 expected = (tb_line + result_g).splitlines()
406 actual = stderr_g.getvalue().splitlines()
407 self.assertEqual(actual, expected)
408
409 # Check 2 different repetitive sections
410 def h(count=10):
411 if count:
412 return h(count-1)
413 g()
414
415 with captured_output("stderr") as stderr_h:
416 try:
417 h()
Pablo Galindo293dd232019-11-19 21:34:03 +0000418 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000419 render_exc()
420 else:
421 self.fail("no value error was raised")
422
423 lineno_h = h.__code__.co_firstlineno
424 result_h = (
425 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400426 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000427 ' h()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400428 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000429 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400430 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000431 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400432 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000433 ' return h(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_h+3}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000436 ' g()\n'
437 )
438 expected = (result_h + result_g).splitlines()
439 actual = stderr_h.getvalue().splitlines()
440 self.assertEqual(actual, expected)
441
Benjamin Petersond5458692018-09-10 08:43:10 -0700442 # Check the boundary conditions. First, test just below the cutoff.
443 with captured_output("stderr") as stderr_g:
444 try:
445 g(traceback._RECURSIVE_CUTOFF)
Pablo Galindo293dd232019-11-19 21:34:03 +0000446 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700447 render_exc()
448 else:
449 self.fail("no error raised")
450 result_g = (
451 f' File "{__file__}", line {lineno_g+2}, in g\n'
452 ' return g(count-1)\n'
453 f' File "{__file__}", line {lineno_g+2}, in g\n'
454 ' return g(count-1)\n'
455 f' File "{__file__}", line {lineno_g+2}, in g\n'
456 ' return g(count-1)\n'
457 f' File "{__file__}", line {lineno_g+3}, in g\n'
458 ' raise ValueError\n'
459 'ValueError\n'
460 )
461 tb_line = (
462 'Traceback (most recent call last):\n'
463 f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n'
464 ' g(traceback._RECURSIVE_CUTOFF)\n'
465 )
466 expected = (tb_line + result_g).splitlines()
467 actual = stderr_g.getvalue().splitlines()
468 self.assertEqual(actual, expected)
469
470 # Second, test just above the cutoff.
471 with captured_output("stderr") as stderr_g:
472 try:
473 g(traceback._RECURSIVE_CUTOFF + 1)
Pablo Galindo293dd232019-11-19 21:34:03 +0000474 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700475 render_exc()
476 else:
477 self.fail("no error raised")
478 result_g = (
479 f' File "{__file__}", line {lineno_g+2}, in g\n'
480 ' return g(count-1)\n'
481 f' File "{__file__}", line {lineno_g+2}, in g\n'
482 ' return g(count-1)\n'
483 f' File "{__file__}", line {lineno_g+2}, in g\n'
484 ' return g(count-1)\n'
485 ' [Previous line repeated 1 more time]\n'
486 f' File "{__file__}", line {lineno_g+3}, in g\n'
487 ' raise ValueError\n'
488 'ValueError\n'
489 )
490 tb_line = (
491 'Traceback (most recent call last):\n'
492 f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n'
493 ' g(traceback._RECURSIVE_CUTOFF + 1)\n'
494 )
495 expected = (tb_line + result_g).splitlines()
496 actual = stderr_g.getvalue().splitlines()
497 self.assertEqual(actual, expected)
498
Nick Coghland0034232016-08-15 13:11:34 +1000499 def test_recursive_traceback_python(self):
500 self._check_recursive_traceback_display(traceback.print_exc)
501
502 @cpython_only
503 def test_recursive_traceback_cpython_internal(self):
504 from _testcapi import exception_print
505 def render_exc():
506 exc_type, exc_value, exc_tb = sys.exc_info()
507 exception_print(exc_value)
508 self._check_recursive_traceback_display(render_exc)
509
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300510 def test_format_stack(self):
511 def fmt():
512 return traceback.format_stack()
513 result = fmt()
514 lineno = fmt.__code__.co_firstlineno
515 self.assertEqual(result[-2:], [
516 ' File "%s", line %d, in test_format_stack\n'
517 ' result = fmt()\n' % (__file__, lineno+2),
518 ' File "%s", line %d, in fmt\n'
519 ' return traceback.format_stack()\n' % (__file__, lineno+1),
520 ])
521
Zane Bitterde860732017-10-17 17:29:39 -0400522 @cpython_only
523 def test_unhashable(self):
524 from _testcapi import exception_print
525
526 class UnhashableException(Exception):
527 def __eq__(self, other):
528 return True
529
530 ex1 = UnhashableException('ex1')
531 ex2 = UnhashableException('ex2')
532 try:
533 raise ex2 from ex1
534 except UnhashableException:
535 try:
536 raise ex1
537 except UnhashableException:
538 exc_type, exc_val, exc_tb = sys.exc_info()
539
540 with captured_output("stderr") as stderr_f:
541 exception_print(exc_val)
542
543 tb = stderr_f.getvalue().strip().splitlines()
544 self.assertEqual(11, len(tb))
545 self.assertEqual(context_message.strip(), tb[5])
546 self.assertIn('UnhashableException: ex2', tb[3])
547 self.assertIn('UnhashableException: ex1', tb[10])
548
Benjamin Petersone6528212008-07-15 15:32:09 +0000549
550cause_message = (
551 "\nThe above exception was the direct cause "
552 "of the following exception:\n\n")
553
554context_message = (
555 "\nDuring handling of the above exception, "
556 "another exception occurred:\n\n")
557
558boundaries = re.compile(
559 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
560
561
562class BaseExceptionReportingTests:
563
564 def get_exception(self, exception_or_callable):
565 if isinstance(exception_or_callable, Exception):
566 return exception_or_callable
567 try:
568 exception_or_callable()
569 except Exception as e:
570 return e
571
572 def zero_div(self):
573 1/0 # In zero_div
574
575 def check_zero_div(self, msg):
576 lines = msg.splitlines()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000577 self.assertTrue(lines[-3].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000578 self.assertIn('1/0 # In zero_div', lines[-2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000579 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000580
581 def test_simple(self):
582 try:
583 1/0 # Marker
584 except ZeroDivisionError as _:
585 e = _
586 lines = self.get_report(e).splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000587 self.assertEqual(len(lines), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000588 self.assertTrue(lines[0].startswith('Traceback'))
589 self.assertTrue(lines[1].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000590 self.assertIn('1/0 # Marker', lines[2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000591 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000592
593 def test_cause(self):
594 def inner_raise():
595 try:
596 self.zero_div()
597 except ZeroDivisionError as e:
598 raise KeyError from e
599 def outer_raise():
600 inner_raise() # Marker
601 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000602 self.assertEqual(len(blocks), 3)
603 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000604 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000605 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000606
607 def test_context(self):
608 def inner_raise():
609 try:
610 self.zero_div()
611 except ZeroDivisionError:
612 raise KeyError
613 def outer_raise():
614 inner_raise() # Marker
615 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000616 self.assertEqual(len(blocks), 3)
617 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000618 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000619 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000620
Nick Coghlanab7bf212012-02-26 17:49:52 +1000621 def test_context_suppression(self):
622 try:
623 try:
624 raise Exception
625 except:
626 raise ZeroDivisionError from None
627 except ZeroDivisionError as _:
628 e = _
629 lines = self.get_report(e).splitlines()
630 self.assertEqual(len(lines), 4)
631 self.assertTrue(lines[0].startswith('Traceback'))
632 self.assertTrue(lines[1].startswith(' File'))
633 self.assertIn('ZeroDivisionError from None', lines[2])
634 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
635
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000636 def test_cause_and_context(self):
637 # When both a cause and a context are set, only the cause should be
638 # displayed and the context should be muted.
639 def inner_raise():
640 try:
641 self.zero_div()
642 except ZeroDivisionError as _e:
643 e = _e
644 try:
645 xyzzy
646 except NameError:
647 raise KeyError from e
648 def outer_raise():
649 inner_raise() # Marker
650 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000651 self.assertEqual(len(blocks), 3)
652 self.assertEqual(blocks[1], cause_message)
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000653 self.check_zero_div(blocks[0])
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000654 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000655
Benjamin Petersone6528212008-07-15 15:32:09 +0000656 def test_cause_recursive(self):
657 def inner_raise():
658 try:
659 try:
660 self.zero_div()
661 except ZeroDivisionError as e:
662 z = e
663 raise KeyError from e
664 except KeyError as e:
665 raise z from e
666 def outer_raise():
667 inner_raise() # Marker
668 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000669 self.assertEqual(len(blocks), 3)
670 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000671 # The first block is the KeyError raised from the ZeroDivisionError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000672 self.assertIn('raise KeyError from e', blocks[0])
673 self.assertNotIn('1/0', blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000674 # The second block (apart from the boundary) is the ZeroDivisionError
675 # re-raised from the KeyError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000676 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000677 self.check_zero_div(blocks[2])
678
Benjamin Peterson503d6c52010-10-24 02:52:05 +0000679 def test_syntax_error_offset_at_eol(self):
680 # See #10186.
681 def e():
682 raise SyntaxError('', ('', 0, 5, 'hello'))
683 msg = self.get_report(e).splitlines()
684 self.assertEqual(msg[-2], " ^")
Benjamin Petersona95e9772010-10-29 03:28:14 +0000685 def e():
686 exec("x = 5 | 4 |")
687 msg = self.get_report(e).splitlines()
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700688 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000689
Irit Katriel069560b2020-12-22 19:53:09 +0000690 def test_syntax_error_no_lineno(self):
691 # See #34463.
692
693 # Without filename
694 e = SyntaxError('bad syntax')
695 msg = self.get_report(e).splitlines()
696 self.assertEqual(msg,
697 ['SyntaxError: bad syntax'])
698 e.lineno = 100
699 msg = self.get_report(e).splitlines()
700 self.assertEqual(msg,
701 [' File "<string>", line 100', 'SyntaxError: bad syntax'])
702
703 # With filename
704 e = SyntaxError('bad syntax')
705 e.filename = 'myfile.py'
706
707 msg = self.get_report(e).splitlines()
708 self.assertEqual(msg,
709 ['SyntaxError: bad syntax (myfile.py)'])
710 e.lineno = 100
711 msg = self.get_report(e).splitlines()
712 self.assertEqual(msg,
713 [' File "myfile.py", line 100', 'SyntaxError: bad syntax'])
714
Martin Panterbb8b1cb2016-09-22 09:37:39 +0000715 def test_message_none(self):
716 # A message that looks like "None" should not be treated specially
717 err = self.get_report(Exception(None))
718 self.assertIn('Exception: None\n', err)
719 err = self.get_report(Exception('None'))
720 self.assertIn('Exception: None\n', err)
721 err = self.get_report(Exception())
722 self.assertIn('Exception\n', err)
723 err = self.get_report(Exception(''))
724 self.assertIn('Exception\n', err)
725
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700726 def test_syntax_error_various_offsets(self):
727 for offset in range(-5, 10):
728 for add in [0, 2]:
729 text = " "*add + "text%d" % offset
730 expected = [' File "file.py", line 1']
731 if offset < 1:
732 expected.append(" %s" % text.lstrip())
733 elif offset <= 6:
734 expected.append(" %s" % text.lstrip())
735 expected.append(" %s^" % (" "*(offset-1)))
736 else:
737 expected.append(" %s" % text.lstrip())
738 expected.append(" %s^" % (" "*5))
739 expected.append("SyntaxError: msg")
740 expected.append("")
741 err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text)))
742 exp = "\n".join(expected)
743 self.assertEqual(exp, err)
744
Benjamin Petersone6528212008-07-15 15:32:09 +0000745
746class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
747 #
748 # This checks reporting through the 'traceback' module, with both
749 # format_exception() and print_exception().
750 #
751
752 def get_report(self, e):
753 e = self.get_exception(e)
754 s = ''.join(
755 traceback.format_exception(type(e), e, e.__traceback__))
756 with captured_output("stderr") as sio:
757 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000758 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000759 return s
760
761
762class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
763 #
764 # This checks built-in reporting by the interpreter.
765 #
766
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200767 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000768 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200769 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000770 e = self.get_exception(e)
771 with captured_output("stderr") as s:
772 exception_print(e)
773 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000774
775
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300776class LimitTests(unittest.TestCase):
777
778 ''' Tests for limit argument.
779 It's enough to test extact_tb, extract_stack and format_exception '''
780
781 def last_raises1(self):
782 raise Exception('Last raised')
783
784 def last_raises2(self):
785 self.last_raises1()
786
787 def last_raises3(self):
788 self.last_raises2()
789
790 def last_raises4(self):
791 self.last_raises3()
792
793 def last_raises5(self):
794 self.last_raises4()
795
796 def last_returns_frame1(self):
797 return sys._getframe()
798
799 def last_returns_frame2(self):
800 return self.last_returns_frame1()
801
802 def last_returns_frame3(self):
803 return self.last_returns_frame2()
804
805 def last_returns_frame4(self):
806 return self.last_returns_frame3()
807
808 def last_returns_frame5(self):
809 return self.last_returns_frame4()
810
811 def test_extract_stack(self):
812 frame = self.last_returns_frame5()
813 def extract(**kwargs):
814 return traceback.extract_stack(frame, **kwargs)
815 def assertEqualExcept(actual, expected, ignore):
816 self.assertEqual(actual[:ignore], expected[:ignore])
817 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
818 self.assertEqual(len(actual), len(expected))
819
820 with support.swap_attr(sys, 'tracebacklimit', 1000):
821 nolim = extract()
822 self.assertGreater(len(nolim), 5)
823 self.assertEqual(extract(limit=2), nolim[-2:])
824 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
825 self.assertEqual(extract(limit=-2), nolim[:2])
826 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
827 self.assertEqual(extract(limit=0), [])
828 del sys.tracebacklimit
829 assertEqualExcept(extract(), nolim, -5-1)
830 sys.tracebacklimit = 2
831 self.assertEqual(extract(), nolim[-2:])
832 self.assertEqual(extract(limit=3), nolim[-3:])
833 self.assertEqual(extract(limit=-3), nolim[:3])
834 sys.tracebacklimit = 0
835 self.assertEqual(extract(), [])
836 sys.tracebacklimit = -1
837 self.assertEqual(extract(), [])
838
839 def test_extract_tb(self):
840 try:
841 self.last_raises5()
842 except Exception:
843 exc_type, exc_value, tb = sys.exc_info()
844 def extract(**kwargs):
845 return traceback.extract_tb(tb, **kwargs)
846
847 with support.swap_attr(sys, 'tracebacklimit', 1000):
848 nolim = extract()
849 self.assertEqual(len(nolim), 5+1)
850 self.assertEqual(extract(limit=2), nolim[:2])
851 self.assertEqual(extract(limit=10), nolim)
852 self.assertEqual(extract(limit=-2), nolim[-2:])
853 self.assertEqual(extract(limit=-10), nolim)
854 self.assertEqual(extract(limit=0), [])
855 del sys.tracebacklimit
856 self.assertEqual(extract(), nolim)
857 sys.tracebacklimit = 2
858 self.assertEqual(extract(), nolim[:2])
859 self.assertEqual(extract(limit=3), nolim[:3])
860 self.assertEqual(extract(limit=-3), nolim[-3:])
861 sys.tracebacklimit = 0
862 self.assertEqual(extract(), [])
863 sys.tracebacklimit = -1
864 self.assertEqual(extract(), [])
865
866 def test_format_exception(self):
867 try:
868 self.last_raises5()
869 except Exception:
870 exc_type, exc_value, tb = sys.exc_info()
871 # [1:-1] to exclude "Traceback (...)" header and
872 # exception type and value
873 def extract(**kwargs):
874 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
875
876 with support.swap_attr(sys, 'tracebacklimit', 1000):
877 nolim = extract()
878 self.assertEqual(len(nolim), 5+1)
879 self.assertEqual(extract(limit=2), nolim[:2])
880 self.assertEqual(extract(limit=10), nolim)
881 self.assertEqual(extract(limit=-2), nolim[-2:])
882 self.assertEqual(extract(limit=-10), nolim)
883 self.assertEqual(extract(limit=0), [])
884 del sys.tracebacklimit
885 self.assertEqual(extract(), nolim)
886 sys.tracebacklimit = 2
887 self.assertEqual(extract(), nolim[:2])
888 self.assertEqual(extract(limit=3), nolim[:3])
889 self.assertEqual(extract(limit=-3), nolim[-3:])
890 sys.tracebacklimit = 0
891 self.assertEqual(extract(), [])
892 sys.tracebacklimit = -1
893 self.assertEqual(extract(), [])
894
895
Andrew Kuchling173a1572013-09-15 18:15:56 -0400896class MiscTracebackCases(unittest.TestCase):
897 #
898 # Check non-printing functions in traceback module
899 #
900
901 def test_clear(self):
902 def outer():
903 middle()
904 def middle():
905 inner()
906 def inner():
907 i = 1
908 1/0
909
910 try:
911 outer()
912 except:
913 type_, value, tb = sys.exc_info()
914
915 # Initial assertion: there's one local in the inner frame.
916 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
917 self.assertEqual(len(inner_frame.f_locals), 1)
918
919 # Clear traceback frames
920 traceback.clear_frames(tb)
921
922 # Local variable dict should now be empty.
923 self.assertEqual(len(inner_frame.f_locals), 0)
924
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300925 def test_extract_stack(self):
926 def extract():
927 return traceback.extract_stack()
928 result = extract()
929 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300930 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300931 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
932 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
933 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300934 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300935
Andrew Kuchling173a1572013-09-15 18:15:56 -0400936
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300937class TestFrame(unittest.TestCase):
938
939 def test_basics(self):
940 linecache.clearcache()
941 linecache.lazycache("f", globals())
942 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300943 self.assertEqual(f,
944 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
945 self.assertEqual(tuple(f),
946 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
947 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
948 self.assertEqual(f, tuple(f))
949 # Since tuple.__eq__ doesn't support FrameSummary, the equality
950 # operator fallbacks to FrameSummary.__eq__.
951 self.assertEqual(tuple(f), f)
952 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +0300953 self.assertNotEqual(f, object())
954 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300955
956 def test_lazy_lines(self):
957 linecache.clearcache()
958 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
959 self.assertEqual(None, f._line)
960 linecache.lazycache("f", globals())
961 self.assertEqual(
962 '"""Test cases for traceback module"""',
963 f.line)
964
965 def test_explicit_line(self):
966 f = traceback.FrameSummary("f", 1, "dummy", line="line")
967 self.assertEqual("line", f.line)
968
Berker Peksag9797b7a2018-09-10 20:02:33 +0300969 def test_len(self):
970 f = traceback.FrameSummary("f", 1, "dummy", line="line")
971 self.assertEqual(len(f), 4)
972
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300973
974class TestStack(unittest.TestCase):
975
976 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +0300977 def deeper():
978 return list(traceback.walk_stack(None))
979 s1 = list(traceback.walk_stack(None))
980 s2 = deeper()
981 self.assertEqual(len(s2) - len(s1), 1)
982 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300983
984 def test_walk_tb(self):
985 try:
986 1/0
987 except Exception:
988 _, _, tb = sys.exc_info()
989 s = list(traceback.walk_tb(tb))
990 self.assertEqual(len(s), 1)
991
992 def test_extract_stack(self):
993 s = traceback.StackSummary.extract(traceback.walk_stack(None))
994 self.assertIsInstance(s, traceback.StackSummary)
995
996 def test_extract_stack_limit(self):
997 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
998 self.assertEqual(len(s), 5)
999
1000 def test_extract_stack_lookup_lines(self):
1001 linecache.clearcache()
1002 linecache.updatecache('/foo.py', globals())
1003 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001004 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001005 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
1006 linecache.clearcache()
1007 self.assertEqual(s[0].line, "import sys")
1008
1009 def test_extract_stackup_deferred_lookup_lines(self):
1010 linecache.clearcache()
1011 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001012 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001013 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
1014 self.assertEqual({}, linecache.cache)
1015 linecache.updatecache('/foo.py', globals())
1016 self.assertEqual(s[0].line, "import sys")
1017
1018 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001019 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001020 self.assertEqual(
1021 [' File "foo.py", line 1, in fred\n line\n'],
1022 s.format())
1023
Robert Collinsbbb8ade2015-03-16 15:27:16 +13001024 def test_from_list_edited_stack(self):
1025 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
1026 s[0] = ('foo.py', 2, 'fred', 'line')
1027 s2 = traceback.StackSummary.from_list(s)
1028 self.assertEqual(
1029 [' File "foo.py", line 2, in fred\n line\n'],
1030 s2.format())
1031
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001032 def test_format_smoke(self):
1033 # For detailed tests see the format_list tests, which consume the same
1034 # code.
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001035 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001036 self.assertEqual(
1037 [' File "foo.py", line 1, in fred\n line\n'],
1038 s.format())
1039
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001040 def test_locals(self):
1041 linecache.updatecache('/foo.py', globals())
1042 c = test_code('/foo.py', 'method')
1043 f = test_frame(c, globals(), {'something': 1})
1044 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
1045 self.assertEqual(s[0].locals, {'something': '1'})
1046
1047 def test_no_locals(self):
1048 linecache.updatecache('/foo.py', globals())
1049 c = test_code('/foo.py', 'method')
1050 f = test_frame(c, globals(), {'something': 1})
1051 s = traceback.StackSummary.extract(iter([(f, 6)]))
1052 self.assertEqual(s[0].locals, None)
1053
1054 def test_format_locals(self):
1055 def some_inner(k, v):
1056 a = 1
1057 b = 2
1058 return traceback.StackSummary.extract(
1059 traceback.walk_stack(None), capture_locals=True, limit=1)
1060 s = some_inner(3, 4)
1061 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +03001062 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001063 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001064 ' a = 1\n'
1065 ' b = 2\n'
1066 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001067 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001068 ], s.format())
1069
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001070class TestTracebackException(unittest.TestCase):
1071
1072 def test_smoke(self):
1073 try:
1074 1/0
1075 except Exception:
1076 exc_info = sys.exc_info()
1077 exc = traceback.TracebackException(*exc_info)
1078 expected_stack = traceback.StackSummary.extract(
1079 traceback.walk_tb(exc_info[2]))
1080 self.assertEqual(None, exc.__cause__)
1081 self.assertEqual(None, exc.__context__)
1082 self.assertEqual(False, exc.__suppress_context__)
1083 self.assertEqual(expected_stack, exc.stack)
1084 self.assertEqual(exc_info[0], exc.exc_type)
1085 self.assertEqual(str(exc_info[1]), str(exc))
1086
1087 def test_from_exception(self):
1088 # Check all the parameters are accepted.
1089 def foo():
1090 1/0
1091 try:
1092 foo()
1093 except Exception as e:
1094 exc_info = sys.exc_info()
1095 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001096 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1097 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001098 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001099 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001100 expected_stack = self.expected_stack
1101 exc = self.exc
1102 self.assertEqual(None, exc.__cause__)
1103 self.assertEqual(None, exc.__context__)
1104 self.assertEqual(False, exc.__suppress_context__)
1105 self.assertEqual(expected_stack, exc.stack)
1106 self.assertEqual(exc_info[0], exc.exc_type)
1107 self.assertEqual(str(exc_info[1]), str(exc))
1108
1109 def test_cause(self):
1110 try:
1111 try:
1112 1/0
1113 finally:
1114 exc_info_context = sys.exc_info()
1115 exc_context = traceback.TracebackException(*exc_info_context)
1116 cause = Exception("cause")
1117 raise Exception("uh oh") from cause
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 exc_cause = traceback.TracebackException(Exception, cause, None)
1124 self.assertEqual(exc_cause, exc.__cause__)
1125 self.assertEqual(exc_context, exc.__context__)
1126 self.assertEqual(True, exc.__suppress_context__)
1127 self.assertEqual(expected_stack, exc.stack)
1128 self.assertEqual(exc_info[0], exc.exc_type)
1129 self.assertEqual(str(exc_info[1]), str(exc))
1130
1131 def test_context(self):
1132 try:
1133 try:
1134 1/0
1135 finally:
1136 exc_info_context = sys.exc_info()
1137 exc_context = traceback.TracebackException(*exc_info_context)
1138 raise Exception("uh oh")
1139 except Exception:
1140 exc_info = sys.exc_info()
1141 exc = traceback.TracebackException(*exc_info)
1142 expected_stack = traceback.StackSummary.extract(
1143 traceback.walk_tb(exc_info[2]))
1144 self.assertEqual(None, exc.__cause__)
1145 self.assertEqual(exc_context, exc.__context__)
1146 self.assertEqual(False, exc.__suppress_context__)
1147 self.assertEqual(expected_stack, exc.stack)
1148 self.assertEqual(exc_info[0], exc.exc_type)
1149 self.assertEqual(str(exc_info[1]), str(exc))
1150
Irit Katriel6dfd1732021-01-12 22:14:27 +00001151 def test_long_context_chain(self):
1152 def f():
1153 try:
1154 1/0
1155 except:
1156 f()
1157
1158 try:
1159 f()
1160 except RecursionError:
1161 exc_info = sys.exc_info()
1162 else:
1163 self.fail("Exception not raised")
1164
1165 te = traceback.TracebackException(*exc_info)
1166 res = list(te.format())
1167
1168 # many ZeroDiv errors followed by the RecursionError
1169 self.assertGreater(len(res), sys.getrecursionlimit())
1170 self.assertGreater(
1171 len([l for l in res if 'ZeroDivisionError:' in l]),
1172 sys.getrecursionlimit() * 0.5)
1173 self.assertIn(
1174 "RecursionError: maximum recursion depth exceeded", res[-1])
1175
Irit Katriel4c94d742021-01-15 02:45:02 +00001176 def test_compact_with_cause(self):
1177 try:
1178 try:
1179 1/0
1180 finally:
1181 cause = Exception("cause")
1182 raise Exception("uh oh") from cause
1183 except Exception:
1184 exc_info = sys.exc_info()
1185 exc = traceback.TracebackException(*exc_info, compact=True)
1186 expected_stack = traceback.StackSummary.extract(
1187 traceback.walk_tb(exc_info[2]))
1188 exc_cause = traceback.TracebackException(Exception, cause, None)
1189 self.assertEqual(exc_cause, exc.__cause__)
1190 self.assertEqual(None, exc.__context__)
1191 self.assertEqual(True, exc.__suppress_context__)
1192 self.assertEqual(expected_stack, exc.stack)
1193 self.assertEqual(exc_info[0], exc.exc_type)
1194 self.assertEqual(str(exc_info[1]), str(exc))
1195
1196 def test_compact_no_cause(self):
1197 try:
1198 try:
1199 1/0
1200 finally:
1201 exc_info_context = sys.exc_info()
1202 exc_context = traceback.TracebackException(*exc_info_context)
1203 raise Exception("uh oh")
1204 except Exception:
1205 exc_info = sys.exc_info()
1206 exc = traceback.TracebackException(*exc_info, compact=True)
1207 expected_stack = traceback.StackSummary.extract(
1208 traceback.walk_tb(exc_info[2]))
1209 self.assertEqual(None, exc.__cause__)
1210 self.assertEqual(exc_context, exc.__context__)
1211 self.assertEqual(False, exc.__suppress_context__)
1212 self.assertEqual(expected_stack, exc.stack)
1213 self.assertEqual(exc_info[0], exc.exc_type)
1214 self.assertEqual(str(exc_info[1]), str(exc))
1215
Irit Katriel427613f2020-12-01 01:35:25 +00001216 def test_no_refs_to_exception_and_traceback_objects(self):
1217 try:
1218 1/0
1219 except Exception:
1220 exc_info = sys.exc_info()
1221
1222 refcnt1 = sys.getrefcount(exc_info[1])
1223 refcnt2 = sys.getrefcount(exc_info[2])
1224 exc = traceback.TracebackException(*exc_info)
1225 self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
1226 self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
1227
Irit Katriel44ca05a2020-11-27 16:38:54 +00001228 def test_comparison_basic(self):
Serhiy Storchaka662db122019-08-08 08:42:54 +03001229 try:
1230 1/0
1231 except Exception:
1232 exc_info = sys.exc_info()
1233 exc = traceback.TracebackException(*exc_info)
1234 exc2 = traceback.TracebackException(*exc_info)
1235 self.assertIsNot(exc, exc2)
1236 self.assertEqual(exc, exc2)
1237 self.assertNotEqual(exc, object())
1238 self.assertEqual(exc, ALWAYS_EQ)
1239
Irit Katriel44ca05a2020-11-27 16:38:54 +00001240 def test_comparison_params_variations(self):
1241 def raise_exc():
1242 try:
1243 raise ValueError('bad value')
1244 except:
1245 raise
1246
1247 def raise_with_locals():
1248 x, y = 1, 2
1249 raise_exc()
1250
1251 try:
1252 raise_with_locals()
1253 except Exception:
1254 exc_info = sys.exc_info()
1255
1256 exc = traceback.TracebackException(*exc_info)
1257 exc1 = traceback.TracebackException(*exc_info, limit=10)
1258 exc2 = traceback.TracebackException(*exc_info, limit=2)
1259
1260 self.assertEqual(exc, exc1) # limit=10 gets all frames
1261 self.assertNotEqual(exc, exc2) # limit=2 truncates the output
1262
1263 # locals change the output
1264 exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
1265 self.assertNotEqual(exc, exc3)
1266
1267 # there are no locals in the innermost frame
1268 exc4 = traceback.TracebackException(*exc_info, limit=-1)
1269 exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
1270 self.assertEqual(exc4, exc5)
1271
1272 # there are locals in the next-to-innermost frame
1273 exc6 = traceback.TracebackException(*exc_info, limit=-2)
1274 exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
1275 self.assertNotEqual(exc6, exc7)
1276
Irit Katriel427613f2020-12-01 01:35:25 +00001277 def test_comparison_equivalent_exceptions_are_equal(self):
1278 excs = []
1279 for _ in range(2):
1280 try:
1281 1/0
1282 except:
1283 excs.append(traceback.TracebackException(*sys.exc_info()))
1284 self.assertEqual(excs[0], excs[1])
1285 self.assertEqual(list(excs[0].format()), list(excs[1].format()))
1286
Zane Bitterde860732017-10-17 17:29:39 -04001287 def test_unhashable(self):
1288 class UnhashableException(Exception):
1289 def __eq__(self, other):
1290 return True
1291
1292 ex1 = UnhashableException('ex1')
1293 ex2 = UnhashableException('ex2')
1294 try:
1295 raise ex2 from ex1
1296 except UnhashableException:
1297 try:
1298 raise ex1
1299 except UnhashableException:
1300 exc_info = sys.exc_info()
1301 exc = traceback.TracebackException(*exc_info)
1302 formatted = list(exc.format())
1303 self.assertIn('UnhashableException: ex2\n', formatted[2])
1304 self.assertIn('UnhashableException: ex1\n', formatted[6])
1305
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001306 def test_limit(self):
1307 def recurse(n):
1308 if n:
1309 recurse(n-1)
1310 else:
1311 1/0
1312 try:
1313 recurse(10)
1314 except Exception:
1315 exc_info = sys.exc_info()
1316 exc = traceback.TracebackException(*exc_info, limit=5)
1317 expected_stack = traceback.StackSummary.extract(
1318 traceback.walk_tb(exc_info[2]), limit=5)
1319 self.assertEqual(expected_stack, exc.stack)
1320
1321 def test_lookup_lines(self):
1322 linecache.clearcache()
1323 e = Exception("uh oh")
1324 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001325 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001326 tb = test_tb(f, 6, None)
1327 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
Irit Katriel44ca05a2020-11-27 16:38:54 +00001328 self.assertEqual(linecache.cache, {})
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001329 linecache.updatecache('/foo.py', globals())
1330 self.assertEqual(exc.stack[0].line, "import sys")
1331
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001332 def test_locals(self):
1333 linecache.updatecache('/foo.py', globals())
1334 e = Exception("uh oh")
1335 c = test_code('/foo.py', 'method')
1336 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1337 tb = test_tb(f, 6, None)
1338 exc = traceback.TracebackException(
1339 Exception, e, tb, capture_locals=True)
1340 self.assertEqual(
1341 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1342
1343 def test_no_locals(self):
1344 linecache.updatecache('/foo.py', globals())
1345 e = Exception("uh oh")
1346 c = test_code('/foo.py', 'method')
1347 f = test_frame(c, globals(), {'something': 1})
1348 tb = test_tb(f, 6, None)
1349 exc = traceback.TracebackException(Exception, e, tb)
1350 self.assertEqual(exc.stack[0].locals, None)
1351
Berker Peksagc3f417d2015-07-24 17:36:21 +03001352 def test_traceback_header(self):
1353 # do not print a traceback header if exc_traceback is None
1354 # see issue #24695
1355 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1356 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1357
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001358
Berker Peksag716b3d32015-04-08 09:47:14 +03001359class MiscTest(unittest.TestCase):
1360
1361 def test_all(self):
1362 expected = set()
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001363 denylist = {'print_list'}
Berker Peksag716b3d32015-04-08 09:47:14 +03001364 for name in dir(traceback):
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001365 if name.startswith('_') or name in denylist:
Berker Peksag716b3d32015-04-08 09:47:14 +03001366 continue
1367 module_object = getattr(traceback, name)
1368 if getattr(module_object, '__module__', None) == 'traceback':
1369 expected.add(name)
1370 self.assertCountEqual(traceback.__all__, expected)
1371
Fred Drake2e2be372001-09-20 21:33:42 +00001372
1373if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001374 unittest.main()