blob: 5bd969d62493a452f5777f53452598547deba91e [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
Irit Katriel26f18b82021-02-23 14:58:47 +0000235 def test_exception_is_None(self):
236 NONE_EXC_STRING = 'NoneType: None\n'
237 excfile = StringIO()
Irit Katrielb798ab02021-02-23 17:43:04 +0000238 traceback.print_exception(None, file=excfile)
239 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
240
241 excfile = StringIO()
Irit Katriel26f18b82021-02-23 14:58:47 +0000242 traceback.print_exception(None, None, None, file=excfile)
243 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
244
245 excfile = StringIO()
246 traceback.print_exc(None, file=excfile)
247 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
248
249 self.assertEqual(traceback.format_exc(None), NONE_EXC_STRING)
Irit Katrielb798ab02021-02-23 17:43:04 +0000250 self.assertEqual(traceback.format_exception(None), [NONE_EXC_STRING])
Irit Katriel26f18b82021-02-23 14:58:47 +0000251 self.assertEqual(
252 traceback.format_exception(None, None, None), [NONE_EXC_STRING])
253 self.assertEqual(
254 traceback.format_exception_only(None), [NONE_EXC_STRING])
255 self.assertEqual(
256 traceback.format_exception_only(None, None), [NONE_EXC_STRING])
257
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000258
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000259class TracebackFormatTests(unittest.TestCase):
260
Antoine Pitrou58720d62013-08-05 23:26:40 +0200261 def some_exception(self):
262 raise KeyError('blah')
263
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200264 @cpython_only
Antoine Pitrou58720d62013-08-05 23:26:40 +0200265 def check_traceback_format(self, cleanup_func=None):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200266 from _testcapi import traceback_print
Georg Brandl236f7972009-04-05 14:28:42 +0000267 try:
Antoine Pitrou58720d62013-08-05 23:26:40 +0200268 self.some_exception()
Georg Brandl236f7972009-04-05 14:28:42 +0000269 except KeyError:
270 type_, value, tb = sys.exc_info()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200271 if cleanup_func is not None:
272 # Clear the inner frames, not this one
273 cleanup_func(tb.tb_next)
Georg Brandl236f7972009-04-05 14:28:42 +0000274 traceback_fmt = 'Traceback (most recent call last):\n' + \
275 ''.join(traceback.format_tb(tb))
276 file_ = StringIO()
277 traceback_print(tb, file_)
278 python_fmt = file_.getvalue()
Benjamin Petersond9fec152013-04-29 16:09:39 -0400279 # Call all _tb and _exc functions
280 with captured_output("stderr") as tbstderr:
281 traceback.print_tb(tb)
282 tbfile = StringIO()
283 traceback.print_tb(tb, file=tbfile)
284 with captured_output("stderr") as excstderr:
285 traceback.print_exc()
286 excfmt = traceback.format_exc()
287 excfile = StringIO()
288 traceback.print_exc(file=excfile)
Georg Brandl236f7972009-04-05 14:28:42 +0000289 else:
290 raise Error("unable to create test traceback string")
291
292 # Make sure that Python and the traceback module format the same thing
Ezio Melottib3aedd42010-11-20 19:04:17 +0000293 self.assertEqual(traceback_fmt, python_fmt)
Benjamin Petersond9fec152013-04-29 16:09:39 -0400294 # Now verify the _tb func output
295 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
296 # Now verify the _exc func output
297 self.assertEqual(excstderr.getvalue(), excfile.getvalue())
298 self.assertEqual(excfmt, excfile.getvalue())
Georg Brandl236f7972009-04-05 14:28:42 +0000299
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000300 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000301 tb_lines = python_fmt.splitlines()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200302 self.assertEqual(len(tb_lines), 5)
303 banner = tb_lines[0]
304 location, source_line = tb_lines[-2:]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000305 self.assertTrue(banner.startswith('Traceback'))
306 self.assertTrue(location.startswith(' File'))
307 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000308
Antoine Pitrou58720d62013-08-05 23:26:40 +0200309 def test_traceback_format(self):
310 self.check_traceback_format()
311
312 def test_traceback_format_with_cleared_frames(self):
313 # Check that traceback formatting also works with a clear()ed frame
314 def cleanup_tb(tb):
315 tb.tb_frame.clear()
316 self.check_traceback_format(cleanup_tb)
317
Benjamin Petersond9fec152013-04-29 16:09:39 -0400318 def test_stack_format(self):
319 # Verify _stack functions. Note we have to use _getframe(1) to
320 # compare them without this frame appearing in the output
321 with captured_output("stderr") as ststderr:
322 traceback.print_stack(sys._getframe(1))
323 stfile = StringIO()
324 traceback.print_stack(sys._getframe(1), file=stfile)
325 self.assertEqual(ststderr.getvalue(), stfile.getvalue())
326
327 stfmt = traceback.format_stack(sys._getframe(1))
328
329 self.assertEqual(ststderr.getvalue(), "".join(stfmt))
330
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300331 def test_print_stack(self):
332 def prn():
333 traceback.print_stack()
334 with captured_output("stderr") as stderr:
335 prn()
336 lineno = prn.__code__.co_firstlineno
337 self.assertEqual(stderr.getvalue().splitlines()[-4:], [
338 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3),
339 ' prn()',
340 ' File "%s", line %d, in prn' % (__file__, lineno+1),
341 ' traceback.print_stack()',
342 ])
343
Nick Coghland0034232016-08-15 13:11:34 +1000344 # issue 26823 - Shrink recursive tracebacks
345 def _check_recursive_traceback_display(self, render_exc):
346 # Always show full diffs when this test fails
347 # Note that rearranging things may require adjusting
348 # the relative line numbers in the expected tracebacks
349 self.maxDiff = None
350
351 # Check hitting the recursion limit
352 def f():
353 f()
354
355 with captured_output("stderr") as stderr_f:
356 try:
357 f()
Pablo Galindo293dd232019-11-19 21:34:03 +0000358 except RecursionError:
Nick Coghland0034232016-08-15 13:11:34 +1000359 render_exc()
360 else:
361 self.fail("no recursion occurred")
362
363 lineno_f = f.__code__.co_firstlineno
364 result_f = (
365 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400366 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000367 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400368 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000369 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400370 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000371 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400372 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000373 ' f()\n'
374 # XXX: The following line changes depending on whether the tests
375 # are run through the interactive interpreter or with -m
376 # It also varies depending on the platform (stack size)
377 # Fortunately, we don't care about exactness here, so we use regex
378 r' \[Previous line repeated (\d+) more times\]' '\n'
379 'RecursionError: maximum recursion depth exceeded\n'
380 )
381
382 expected = result_f.splitlines()
383 actual = stderr_f.getvalue().splitlines()
384
385 # Check the output text matches expectations
386 # 2nd last line contains the repetition count
387 self.assertEqual(actual[:-2], expected[:-2])
388 self.assertRegex(actual[-2], expected[-2])
codedragon3480ef92017-05-24 14:23:46 -0700389 # last line can have additional text appended
390 self.assertIn(expected[-1], actual[-1])
Nick Coghland0034232016-08-15 13:11:34 +1000391
392 # Check the recursion count is roughly as expected
393 rec_limit = sys.getrecursionlimit()
Serhiy Storchakab7281052016-09-12 00:52:40 +0300394 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
Nick Coghland0034232016-08-15 13:11:34 +1000395
396 # Check a known (limited) number of recursive invocations
397 def g(count=10):
398 if count:
399 return g(count-1)
400 raise ValueError
401
402 with captured_output("stderr") as stderr_g:
403 try:
404 g()
Pablo Galindo293dd232019-11-19 21:34:03 +0000405 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000406 render_exc()
407 else:
408 self.fail("no value error was raised")
409
410 lineno_g = g.__code__.co_firstlineno
411 result_g = (
Eric V. Smith451d0e32016-09-09 21:56:20 -0400412 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000413 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400414 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000415 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400416 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000417 ' return g(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700418 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400419 f' File "{__file__}", line {lineno_g+3}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000420 ' raise ValueError\n'
421 'ValueError\n'
422 )
423 tb_line = (
424 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400425 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000426 ' g()\n'
427 )
428 expected = (tb_line + result_g).splitlines()
429 actual = stderr_g.getvalue().splitlines()
430 self.assertEqual(actual, expected)
431
432 # Check 2 different repetitive sections
433 def h(count=10):
434 if count:
435 return h(count-1)
436 g()
437
438 with captured_output("stderr") as stderr_h:
439 try:
440 h()
Pablo Galindo293dd232019-11-19 21:34:03 +0000441 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000442 render_exc()
443 else:
444 self.fail("no value error was raised")
445
446 lineno_h = h.__code__.co_firstlineno
447 result_h = (
448 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400449 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000450 ' h()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400451 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000452 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400453 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000454 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400455 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000456 ' return h(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700457 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400458 f' File "{__file__}", line {lineno_h+3}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000459 ' g()\n'
460 )
461 expected = (result_h + result_g).splitlines()
462 actual = stderr_h.getvalue().splitlines()
463 self.assertEqual(actual, expected)
464
Benjamin Petersond5458692018-09-10 08:43:10 -0700465 # Check the boundary conditions. First, test just below the cutoff.
466 with captured_output("stderr") as stderr_g:
467 try:
468 g(traceback._RECURSIVE_CUTOFF)
Pablo Galindo293dd232019-11-19 21:34:03 +0000469 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700470 render_exc()
471 else:
472 self.fail("no error raised")
473 result_g = (
474 f' File "{__file__}", line {lineno_g+2}, in g\n'
475 ' return g(count-1)\n'
476 f' File "{__file__}", line {lineno_g+2}, in g\n'
477 ' return g(count-1)\n'
478 f' File "{__file__}", line {lineno_g+2}, in g\n'
479 ' return g(count-1)\n'
480 f' File "{__file__}", line {lineno_g+3}, in g\n'
481 ' raise ValueError\n'
482 'ValueError\n'
483 )
484 tb_line = (
485 'Traceback (most recent call last):\n'
486 f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n'
487 ' g(traceback._RECURSIVE_CUTOFF)\n'
488 )
489 expected = (tb_line + result_g).splitlines()
490 actual = stderr_g.getvalue().splitlines()
491 self.assertEqual(actual, expected)
492
493 # Second, test just above the cutoff.
494 with captured_output("stderr") as stderr_g:
495 try:
496 g(traceback._RECURSIVE_CUTOFF + 1)
Pablo Galindo293dd232019-11-19 21:34:03 +0000497 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700498 render_exc()
499 else:
500 self.fail("no error raised")
501 result_g = (
502 f' File "{__file__}", line {lineno_g+2}, in g\n'
503 ' return g(count-1)\n'
504 f' File "{__file__}", line {lineno_g+2}, in g\n'
505 ' return g(count-1)\n'
506 f' File "{__file__}", line {lineno_g+2}, in g\n'
507 ' return g(count-1)\n'
508 ' [Previous line repeated 1 more time]\n'
509 f' File "{__file__}", line {lineno_g+3}, in g\n'
510 ' raise ValueError\n'
511 'ValueError\n'
512 )
513 tb_line = (
514 'Traceback (most recent call last):\n'
515 f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n'
516 ' g(traceback._RECURSIVE_CUTOFF + 1)\n'
517 )
518 expected = (tb_line + result_g).splitlines()
519 actual = stderr_g.getvalue().splitlines()
520 self.assertEqual(actual, expected)
521
Nick Coghland0034232016-08-15 13:11:34 +1000522 def test_recursive_traceback_python(self):
523 self._check_recursive_traceback_display(traceback.print_exc)
524
525 @cpython_only
526 def test_recursive_traceback_cpython_internal(self):
527 from _testcapi import exception_print
528 def render_exc():
529 exc_type, exc_value, exc_tb = sys.exc_info()
530 exception_print(exc_value)
531 self._check_recursive_traceback_display(render_exc)
532
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300533 def test_format_stack(self):
534 def fmt():
535 return traceback.format_stack()
536 result = fmt()
537 lineno = fmt.__code__.co_firstlineno
538 self.assertEqual(result[-2:], [
539 ' File "%s", line %d, in test_format_stack\n'
540 ' result = fmt()\n' % (__file__, lineno+2),
541 ' File "%s", line %d, in fmt\n'
542 ' return traceback.format_stack()\n' % (__file__, lineno+1),
543 ])
544
Zane Bitterde860732017-10-17 17:29:39 -0400545 @cpython_only
546 def test_unhashable(self):
547 from _testcapi import exception_print
548
549 class UnhashableException(Exception):
550 def __eq__(self, other):
551 return True
552
553 ex1 = UnhashableException('ex1')
554 ex2 = UnhashableException('ex2')
555 try:
556 raise ex2 from ex1
557 except UnhashableException:
558 try:
559 raise ex1
560 except UnhashableException:
561 exc_type, exc_val, exc_tb = sys.exc_info()
562
563 with captured_output("stderr") as stderr_f:
564 exception_print(exc_val)
565
566 tb = stderr_f.getvalue().strip().splitlines()
567 self.assertEqual(11, len(tb))
568 self.assertEqual(context_message.strip(), tb[5])
569 self.assertIn('UnhashableException: ex2', tb[3])
570 self.assertIn('UnhashableException: ex1', tb[10])
571
Benjamin Petersone6528212008-07-15 15:32:09 +0000572
573cause_message = (
574 "\nThe above exception was the direct cause "
575 "of the following exception:\n\n")
576
577context_message = (
578 "\nDuring handling of the above exception, "
579 "another exception occurred:\n\n")
580
581boundaries = re.compile(
582 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
583
584
585class BaseExceptionReportingTests:
586
587 def get_exception(self, exception_or_callable):
588 if isinstance(exception_or_callable, Exception):
589 return exception_or_callable
590 try:
591 exception_or_callable()
592 except Exception as e:
593 return e
594
595 def zero_div(self):
596 1/0 # In zero_div
597
598 def check_zero_div(self, msg):
599 lines = msg.splitlines()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000600 self.assertTrue(lines[-3].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000601 self.assertIn('1/0 # In zero_div', lines[-2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000602 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000603
604 def test_simple(self):
605 try:
606 1/0 # Marker
607 except ZeroDivisionError as _:
608 e = _
609 lines = self.get_report(e).splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000610 self.assertEqual(len(lines), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000611 self.assertTrue(lines[0].startswith('Traceback'))
612 self.assertTrue(lines[1].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000613 self.assertIn('1/0 # Marker', lines[2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000614 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000615
616 def test_cause(self):
617 def inner_raise():
618 try:
619 self.zero_div()
620 except ZeroDivisionError as e:
621 raise KeyError from e
622 def outer_raise():
623 inner_raise() # Marker
624 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000625 self.assertEqual(len(blocks), 3)
626 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000627 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000628 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000629
630 def test_context(self):
631 def inner_raise():
632 try:
633 self.zero_div()
634 except ZeroDivisionError:
635 raise KeyError
636 def outer_raise():
637 inner_raise() # Marker
638 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000639 self.assertEqual(len(blocks), 3)
640 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000641 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000642 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000643
Nick Coghlanab7bf212012-02-26 17:49:52 +1000644 def test_context_suppression(self):
645 try:
646 try:
647 raise Exception
648 except:
649 raise ZeroDivisionError from None
650 except ZeroDivisionError as _:
651 e = _
652 lines = self.get_report(e).splitlines()
653 self.assertEqual(len(lines), 4)
654 self.assertTrue(lines[0].startswith('Traceback'))
655 self.assertTrue(lines[1].startswith(' File'))
656 self.assertIn('ZeroDivisionError from None', lines[2])
657 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
658
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000659 def test_cause_and_context(self):
660 # When both a cause and a context are set, only the cause should be
661 # displayed and the context should be muted.
662 def inner_raise():
663 try:
664 self.zero_div()
665 except ZeroDivisionError as _e:
666 e = _e
667 try:
668 xyzzy
669 except NameError:
670 raise KeyError from e
671 def outer_raise():
672 inner_raise() # Marker
673 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000674 self.assertEqual(len(blocks), 3)
675 self.assertEqual(blocks[1], cause_message)
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000676 self.check_zero_div(blocks[0])
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000677 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000678
Benjamin Petersone6528212008-07-15 15:32:09 +0000679 def test_cause_recursive(self):
680 def inner_raise():
681 try:
682 try:
683 self.zero_div()
684 except ZeroDivisionError as e:
685 z = e
686 raise KeyError from e
687 except KeyError as e:
688 raise z from e
689 def outer_raise():
690 inner_raise() # Marker
691 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000692 self.assertEqual(len(blocks), 3)
693 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000694 # The first block is the KeyError raised from the ZeroDivisionError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000695 self.assertIn('raise KeyError from e', blocks[0])
696 self.assertNotIn('1/0', blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000697 # The second block (apart from the boundary) is the ZeroDivisionError
698 # re-raised from the KeyError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000699 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000700 self.check_zero_div(blocks[2])
701
Benjamin Peterson503d6c52010-10-24 02:52:05 +0000702 def test_syntax_error_offset_at_eol(self):
703 # See #10186.
704 def e():
705 raise SyntaxError('', ('', 0, 5, 'hello'))
706 msg = self.get_report(e).splitlines()
707 self.assertEqual(msg[-2], " ^")
Benjamin Petersona95e9772010-10-29 03:28:14 +0000708 def e():
709 exec("x = 5 | 4 |")
710 msg = self.get_report(e).splitlines()
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700711 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000712
Irit Katriel069560b2020-12-22 19:53:09 +0000713 def test_syntax_error_no_lineno(self):
714 # See #34463.
715
716 # Without filename
717 e = SyntaxError('bad syntax')
718 msg = self.get_report(e).splitlines()
719 self.assertEqual(msg,
720 ['SyntaxError: bad syntax'])
721 e.lineno = 100
722 msg = self.get_report(e).splitlines()
723 self.assertEqual(msg,
724 [' File "<string>", line 100', 'SyntaxError: bad syntax'])
725
726 # With filename
727 e = SyntaxError('bad syntax')
728 e.filename = 'myfile.py'
729
730 msg = self.get_report(e).splitlines()
731 self.assertEqual(msg,
732 ['SyntaxError: bad syntax (myfile.py)'])
733 e.lineno = 100
734 msg = self.get_report(e).splitlines()
735 self.assertEqual(msg,
736 [' File "myfile.py", line 100', 'SyntaxError: bad syntax'])
737
Martin Panterbb8b1cb2016-09-22 09:37:39 +0000738 def test_message_none(self):
739 # A message that looks like "None" should not be treated specially
740 err = self.get_report(Exception(None))
741 self.assertIn('Exception: None\n', err)
742 err = self.get_report(Exception('None'))
743 self.assertIn('Exception: None\n', err)
744 err = self.get_report(Exception())
745 self.assertIn('Exception\n', err)
746 err = self.get_report(Exception(''))
747 self.assertIn('Exception\n', err)
748
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700749 def test_syntax_error_various_offsets(self):
750 for offset in range(-5, 10):
751 for add in [0, 2]:
752 text = " "*add + "text%d" % offset
753 expected = [' File "file.py", line 1']
754 if offset < 1:
755 expected.append(" %s" % text.lstrip())
756 elif offset <= 6:
757 expected.append(" %s" % text.lstrip())
758 expected.append(" %s^" % (" "*(offset-1)))
759 else:
760 expected.append(" %s" % text.lstrip())
761 expected.append(" %s^" % (" "*5))
762 expected.append("SyntaxError: msg")
763 expected.append("")
764 err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text)))
765 exp = "\n".join(expected)
766 self.assertEqual(exp, err)
767
Benjamin Petersone6528212008-07-15 15:32:09 +0000768
769class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
770 #
771 # This checks reporting through the 'traceback' module, with both
772 # format_exception() and print_exception().
773 #
774
775 def get_report(self, e):
776 e = self.get_exception(e)
777 s = ''.join(
778 traceback.format_exception(type(e), e, e.__traceback__))
779 with captured_output("stderr") as sio:
780 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000781 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000782 return s
783
784
785class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
786 #
787 # This checks built-in reporting by the interpreter.
788 #
789
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200790 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000791 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200792 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000793 e = self.get_exception(e)
794 with captured_output("stderr") as s:
795 exception_print(e)
796 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000797
798
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300799class LimitTests(unittest.TestCase):
800
801 ''' Tests for limit argument.
802 It's enough to test extact_tb, extract_stack and format_exception '''
803
804 def last_raises1(self):
805 raise Exception('Last raised')
806
807 def last_raises2(self):
808 self.last_raises1()
809
810 def last_raises3(self):
811 self.last_raises2()
812
813 def last_raises4(self):
814 self.last_raises3()
815
816 def last_raises5(self):
817 self.last_raises4()
818
819 def last_returns_frame1(self):
820 return sys._getframe()
821
822 def last_returns_frame2(self):
823 return self.last_returns_frame1()
824
825 def last_returns_frame3(self):
826 return self.last_returns_frame2()
827
828 def last_returns_frame4(self):
829 return self.last_returns_frame3()
830
831 def last_returns_frame5(self):
832 return self.last_returns_frame4()
833
834 def test_extract_stack(self):
835 frame = self.last_returns_frame5()
836 def extract(**kwargs):
837 return traceback.extract_stack(frame, **kwargs)
838 def assertEqualExcept(actual, expected, ignore):
839 self.assertEqual(actual[:ignore], expected[:ignore])
840 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
841 self.assertEqual(len(actual), len(expected))
842
843 with support.swap_attr(sys, 'tracebacklimit', 1000):
844 nolim = extract()
845 self.assertGreater(len(nolim), 5)
846 self.assertEqual(extract(limit=2), nolim[-2:])
847 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
848 self.assertEqual(extract(limit=-2), nolim[:2])
849 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
850 self.assertEqual(extract(limit=0), [])
851 del sys.tracebacklimit
852 assertEqualExcept(extract(), nolim, -5-1)
853 sys.tracebacklimit = 2
854 self.assertEqual(extract(), nolim[-2:])
855 self.assertEqual(extract(limit=3), nolim[-3:])
856 self.assertEqual(extract(limit=-3), nolim[:3])
857 sys.tracebacklimit = 0
858 self.assertEqual(extract(), [])
859 sys.tracebacklimit = -1
860 self.assertEqual(extract(), [])
861
862 def test_extract_tb(self):
863 try:
864 self.last_raises5()
865 except Exception:
866 exc_type, exc_value, tb = sys.exc_info()
867 def extract(**kwargs):
868 return traceback.extract_tb(tb, **kwargs)
869
870 with support.swap_attr(sys, 'tracebacklimit', 1000):
871 nolim = extract()
872 self.assertEqual(len(nolim), 5+1)
873 self.assertEqual(extract(limit=2), nolim[:2])
874 self.assertEqual(extract(limit=10), nolim)
875 self.assertEqual(extract(limit=-2), nolim[-2:])
876 self.assertEqual(extract(limit=-10), nolim)
877 self.assertEqual(extract(limit=0), [])
878 del sys.tracebacklimit
879 self.assertEqual(extract(), nolim)
880 sys.tracebacklimit = 2
881 self.assertEqual(extract(), nolim[:2])
882 self.assertEqual(extract(limit=3), nolim[:3])
883 self.assertEqual(extract(limit=-3), nolim[-3:])
884 sys.tracebacklimit = 0
885 self.assertEqual(extract(), [])
886 sys.tracebacklimit = -1
887 self.assertEqual(extract(), [])
888
889 def test_format_exception(self):
890 try:
891 self.last_raises5()
892 except Exception:
893 exc_type, exc_value, tb = sys.exc_info()
894 # [1:-1] to exclude "Traceback (...)" header and
895 # exception type and value
896 def extract(**kwargs):
897 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
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
Andrew Kuchling173a1572013-09-15 18:15:56 -0400919class MiscTracebackCases(unittest.TestCase):
920 #
921 # Check non-printing functions in traceback module
922 #
923
924 def test_clear(self):
925 def outer():
926 middle()
927 def middle():
928 inner()
929 def inner():
930 i = 1
931 1/0
932
933 try:
934 outer()
935 except:
936 type_, value, tb = sys.exc_info()
937
938 # Initial assertion: there's one local in the inner frame.
939 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
940 self.assertEqual(len(inner_frame.f_locals), 1)
941
942 # Clear traceback frames
943 traceback.clear_frames(tb)
944
945 # Local variable dict should now be empty.
946 self.assertEqual(len(inner_frame.f_locals), 0)
947
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300948 def test_extract_stack(self):
949 def extract():
950 return traceback.extract_stack()
951 result = extract()
952 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300953 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300954 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
955 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
956 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300957 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300958
Andrew Kuchling173a1572013-09-15 18:15:56 -0400959
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300960class TestFrame(unittest.TestCase):
961
962 def test_basics(self):
963 linecache.clearcache()
964 linecache.lazycache("f", globals())
965 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300966 self.assertEqual(f,
967 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
968 self.assertEqual(tuple(f),
969 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
970 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
971 self.assertEqual(f, tuple(f))
972 # Since tuple.__eq__ doesn't support FrameSummary, the equality
973 # operator fallbacks to FrameSummary.__eq__.
974 self.assertEqual(tuple(f), f)
975 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +0300976 self.assertNotEqual(f, object())
977 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300978
979 def test_lazy_lines(self):
980 linecache.clearcache()
981 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
982 self.assertEqual(None, f._line)
983 linecache.lazycache("f", globals())
984 self.assertEqual(
985 '"""Test cases for traceback module"""',
986 f.line)
987
988 def test_explicit_line(self):
989 f = traceback.FrameSummary("f", 1, "dummy", line="line")
990 self.assertEqual("line", f.line)
991
Berker Peksag9797b7a2018-09-10 20:02:33 +0300992 def test_len(self):
993 f = traceback.FrameSummary("f", 1, "dummy", line="line")
994 self.assertEqual(len(f), 4)
995
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300996
997class TestStack(unittest.TestCase):
998
999 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +03001000 def deeper():
1001 return list(traceback.walk_stack(None))
1002 s1 = list(traceback.walk_stack(None))
1003 s2 = deeper()
1004 self.assertEqual(len(s2) - len(s1), 1)
1005 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001006
1007 def test_walk_tb(self):
1008 try:
1009 1/0
1010 except Exception:
1011 _, _, tb = sys.exc_info()
1012 s = list(traceback.walk_tb(tb))
1013 self.assertEqual(len(s), 1)
1014
1015 def test_extract_stack(self):
1016 s = traceback.StackSummary.extract(traceback.walk_stack(None))
1017 self.assertIsInstance(s, traceback.StackSummary)
1018
1019 def test_extract_stack_limit(self):
1020 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
1021 self.assertEqual(len(s), 5)
1022
1023 def test_extract_stack_lookup_lines(self):
1024 linecache.clearcache()
1025 linecache.updatecache('/foo.py', globals())
1026 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001027 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001028 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
1029 linecache.clearcache()
1030 self.assertEqual(s[0].line, "import sys")
1031
1032 def test_extract_stackup_deferred_lookup_lines(self):
1033 linecache.clearcache()
1034 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001035 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001036 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
1037 self.assertEqual({}, linecache.cache)
1038 linecache.updatecache('/foo.py', globals())
1039 self.assertEqual(s[0].line, "import sys")
1040
1041 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001042 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001043 self.assertEqual(
1044 [' File "foo.py", line 1, in fred\n line\n'],
1045 s.format())
1046
Robert Collinsbbb8ade2015-03-16 15:27:16 +13001047 def test_from_list_edited_stack(self):
1048 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
1049 s[0] = ('foo.py', 2, 'fred', 'line')
1050 s2 = traceback.StackSummary.from_list(s)
1051 self.assertEqual(
1052 [' File "foo.py", line 2, in fred\n line\n'],
1053 s2.format())
1054
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001055 def test_format_smoke(self):
1056 # For detailed tests see the format_list tests, which consume the same
1057 # code.
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001058 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001059 self.assertEqual(
1060 [' File "foo.py", line 1, in fred\n line\n'],
1061 s.format())
1062
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001063 def test_locals(self):
1064 linecache.updatecache('/foo.py', globals())
1065 c = test_code('/foo.py', 'method')
1066 f = test_frame(c, globals(), {'something': 1})
1067 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
1068 self.assertEqual(s[0].locals, {'something': '1'})
1069
1070 def test_no_locals(self):
1071 linecache.updatecache('/foo.py', globals())
1072 c = test_code('/foo.py', 'method')
1073 f = test_frame(c, globals(), {'something': 1})
1074 s = traceback.StackSummary.extract(iter([(f, 6)]))
1075 self.assertEqual(s[0].locals, None)
1076
1077 def test_format_locals(self):
1078 def some_inner(k, v):
1079 a = 1
1080 b = 2
1081 return traceback.StackSummary.extract(
1082 traceback.walk_stack(None), capture_locals=True, limit=1)
1083 s = some_inner(3, 4)
1084 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +03001085 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001086 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001087 ' a = 1\n'
1088 ' b = 2\n'
1089 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001090 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001091 ], s.format())
1092
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001093class TestTracebackException(unittest.TestCase):
1094
1095 def test_smoke(self):
1096 try:
1097 1/0
1098 except Exception:
1099 exc_info = sys.exc_info()
1100 exc = traceback.TracebackException(*exc_info)
1101 expected_stack = traceback.StackSummary.extract(
1102 traceback.walk_tb(exc_info[2]))
1103 self.assertEqual(None, exc.__cause__)
1104 self.assertEqual(None, exc.__context__)
1105 self.assertEqual(False, exc.__suppress_context__)
1106 self.assertEqual(expected_stack, exc.stack)
1107 self.assertEqual(exc_info[0], exc.exc_type)
1108 self.assertEqual(str(exc_info[1]), str(exc))
1109
1110 def test_from_exception(self):
1111 # Check all the parameters are accepted.
1112 def foo():
1113 1/0
1114 try:
1115 foo()
1116 except Exception as e:
1117 exc_info = sys.exc_info()
1118 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001119 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1120 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001121 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001122 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001123 expected_stack = self.expected_stack
1124 exc = self.exc
1125 self.assertEqual(None, exc.__cause__)
1126 self.assertEqual(None, exc.__context__)
1127 self.assertEqual(False, exc.__suppress_context__)
1128 self.assertEqual(expected_stack, exc.stack)
1129 self.assertEqual(exc_info[0], exc.exc_type)
1130 self.assertEqual(str(exc_info[1]), str(exc))
1131
1132 def test_cause(self):
1133 try:
1134 try:
1135 1/0
1136 finally:
1137 exc_info_context = sys.exc_info()
1138 exc_context = traceback.TracebackException(*exc_info_context)
1139 cause = Exception("cause")
1140 raise Exception("uh oh") from cause
1141 except Exception:
1142 exc_info = sys.exc_info()
1143 exc = traceback.TracebackException(*exc_info)
1144 expected_stack = traceback.StackSummary.extract(
1145 traceback.walk_tb(exc_info[2]))
1146 exc_cause = traceback.TracebackException(Exception, cause, None)
1147 self.assertEqual(exc_cause, exc.__cause__)
1148 self.assertEqual(exc_context, exc.__context__)
1149 self.assertEqual(True, exc.__suppress_context__)
1150 self.assertEqual(expected_stack, exc.stack)
1151 self.assertEqual(exc_info[0], exc.exc_type)
1152 self.assertEqual(str(exc_info[1]), str(exc))
1153
1154 def test_context(self):
1155 try:
1156 try:
1157 1/0
1158 finally:
1159 exc_info_context = sys.exc_info()
1160 exc_context = traceback.TracebackException(*exc_info_context)
1161 raise Exception("uh oh")
1162 except Exception:
1163 exc_info = sys.exc_info()
1164 exc = traceback.TracebackException(*exc_info)
1165 expected_stack = traceback.StackSummary.extract(
1166 traceback.walk_tb(exc_info[2]))
1167 self.assertEqual(None, exc.__cause__)
1168 self.assertEqual(exc_context, exc.__context__)
1169 self.assertEqual(False, exc.__suppress_context__)
1170 self.assertEqual(expected_stack, exc.stack)
1171 self.assertEqual(exc_info[0], exc.exc_type)
1172 self.assertEqual(str(exc_info[1]), str(exc))
1173
Irit Katriel6dfd1732021-01-12 22:14:27 +00001174 def test_long_context_chain(self):
1175 def f():
1176 try:
1177 1/0
1178 except:
1179 f()
1180
1181 try:
1182 f()
1183 except RecursionError:
1184 exc_info = sys.exc_info()
1185 else:
1186 self.fail("Exception not raised")
1187
1188 te = traceback.TracebackException(*exc_info)
1189 res = list(te.format())
1190
1191 # many ZeroDiv errors followed by the RecursionError
1192 self.assertGreater(len(res), sys.getrecursionlimit())
1193 self.assertGreater(
1194 len([l for l in res if 'ZeroDivisionError:' in l]),
1195 sys.getrecursionlimit() * 0.5)
1196 self.assertIn(
1197 "RecursionError: maximum recursion depth exceeded", res[-1])
1198
Irit Katriel4c94d742021-01-15 02:45:02 +00001199 def test_compact_with_cause(self):
1200 try:
1201 try:
1202 1/0
1203 finally:
1204 cause = Exception("cause")
1205 raise Exception("uh oh") from cause
1206 except Exception:
1207 exc_info = sys.exc_info()
1208 exc = traceback.TracebackException(*exc_info, compact=True)
1209 expected_stack = traceback.StackSummary.extract(
1210 traceback.walk_tb(exc_info[2]))
1211 exc_cause = traceback.TracebackException(Exception, cause, None)
1212 self.assertEqual(exc_cause, exc.__cause__)
1213 self.assertEqual(None, exc.__context__)
1214 self.assertEqual(True, exc.__suppress_context__)
1215 self.assertEqual(expected_stack, exc.stack)
1216 self.assertEqual(exc_info[0], exc.exc_type)
1217 self.assertEqual(str(exc_info[1]), str(exc))
1218
1219 def test_compact_no_cause(self):
1220 try:
1221 try:
1222 1/0
1223 finally:
1224 exc_info_context = sys.exc_info()
1225 exc_context = traceback.TracebackException(*exc_info_context)
1226 raise Exception("uh oh")
1227 except Exception:
1228 exc_info = sys.exc_info()
1229 exc = traceback.TracebackException(*exc_info, compact=True)
1230 expected_stack = traceback.StackSummary.extract(
1231 traceback.walk_tb(exc_info[2]))
1232 self.assertEqual(None, exc.__cause__)
1233 self.assertEqual(exc_context, exc.__context__)
1234 self.assertEqual(False, exc.__suppress_context__)
1235 self.assertEqual(expected_stack, exc.stack)
1236 self.assertEqual(exc_info[0], exc.exc_type)
1237 self.assertEqual(str(exc_info[1]), str(exc))
1238
Irit Katriel427613f2020-12-01 01:35:25 +00001239 def test_no_refs_to_exception_and_traceback_objects(self):
1240 try:
1241 1/0
1242 except Exception:
1243 exc_info = sys.exc_info()
1244
1245 refcnt1 = sys.getrefcount(exc_info[1])
1246 refcnt2 = sys.getrefcount(exc_info[2])
1247 exc = traceback.TracebackException(*exc_info)
1248 self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
1249 self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
1250
Irit Katriel44ca05a2020-11-27 16:38:54 +00001251 def test_comparison_basic(self):
Serhiy Storchaka662db122019-08-08 08:42:54 +03001252 try:
1253 1/0
1254 except Exception:
1255 exc_info = sys.exc_info()
1256 exc = traceback.TracebackException(*exc_info)
1257 exc2 = traceback.TracebackException(*exc_info)
1258 self.assertIsNot(exc, exc2)
1259 self.assertEqual(exc, exc2)
1260 self.assertNotEqual(exc, object())
1261 self.assertEqual(exc, ALWAYS_EQ)
1262
Irit Katriel44ca05a2020-11-27 16:38:54 +00001263 def test_comparison_params_variations(self):
1264 def raise_exc():
1265 try:
1266 raise ValueError('bad value')
1267 except:
1268 raise
1269
1270 def raise_with_locals():
1271 x, y = 1, 2
1272 raise_exc()
1273
1274 try:
1275 raise_with_locals()
1276 except Exception:
1277 exc_info = sys.exc_info()
1278
1279 exc = traceback.TracebackException(*exc_info)
1280 exc1 = traceback.TracebackException(*exc_info, limit=10)
1281 exc2 = traceback.TracebackException(*exc_info, limit=2)
1282
1283 self.assertEqual(exc, exc1) # limit=10 gets all frames
1284 self.assertNotEqual(exc, exc2) # limit=2 truncates the output
1285
1286 # locals change the output
1287 exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
1288 self.assertNotEqual(exc, exc3)
1289
1290 # there are no locals in the innermost frame
1291 exc4 = traceback.TracebackException(*exc_info, limit=-1)
1292 exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
1293 self.assertEqual(exc4, exc5)
1294
1295 # there are locals in the next-to-innermost frame
1296 exc6 = traceback.TracebackException(*exc_info, limit=-2)
1297 exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
1298 self.assertNotEqual(exc6, exc7)
1299
Irit Katriel427613f2020-12-01 01:35:25 +00001300 def test_comparison_equivalent_exceptions_are_equal(self):
1301 excs = []
1302 for _ in range(2):
1303 try:
1304 1/0
1305 except:
1306 excs.append(traceback.TracebackException(*sys.exc_info()))
1307 self.assertEqual(excs[0], excs[1])
1308 self.assertEqual(list(excs[0].format()), list(excs[1].format()))
1309
Zane Bitterde860732017-10-17 17:29:39 -04001310 def test_unhashable(self):
1311 class UnhashableException(Exception):
1312 def __eq__(self, other):
1313 return True
1314
1315 ex1 = UnhashableException('ex1')
1316 ex2 = UnhashableException('ex2')
1317 try:
1318 raise ex2 from ex1
1319 except UnhashableException:
1320 try:
1321 raise ex1
1322 except UnhashableException:
1323 exc_info = sys.exc_info()
1324 exc = traceback.TracebackException(*exc_info)
1325 formatted = list(exc.format())
1326 self.assertIn('UnhashableException: ex2\n', formatted[2])
1327 self.assertIn('UnhashableException: ex1\n', formatted[6])
1328
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001329 def test_limit(self):
1330 def recurse(n):
1331 if n:
1332 recurse(n-1)
1333 else:
1334 1/0
1335 try:
1336 recurse(10)
1337 except Exception:
1338 exc_info = sys.exc_info()
1339 exc = traceback.TracebackException(*exc_info, limit=5)
1340 expected_stack = traceback.StackSummary.extract(
1341 traceback.walk_tb(exc_info[2]), limit=5)
1342 self.assertEqual(expected_stack, exc.stack)
1343
1344 def test_lookup_lines(self):
1345 linecache.clearcache()
1346 e = Exception("uh oh")
1347 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001348 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001349 tb = test_tb(f, 6, None)
1350 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
Irit Katriel44ca05a2020-11-27 16:38:54 +00001351 self.assertEqual(linecache.cache, {})
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001352 linecache.updatecache('/foo.py', globals())
1353 self.assertEqual(exc.stack[0].line, "import sys")
1354
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001355 def test_locals(self):
1356 linecache.updatecache('/foo.py', globals())
1357 e = Exception("uh oh")
1358 c = test_code('/foo.py', 'method')
1359 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1360 tb = test_tb(f, 6, None)
1361 exc = traceback.TracebackException(
1362 Exception, e, tb, capture_locals=True)
1363 self.assertEqual(
1364 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1365
1366 def test_no_locals(self):
1367 linecache.updatecache('/foo.py', globals())
1368 e = Exception("uh oh")
1369 c = test_code('/foo.py', 'method')
1370 f = test_frame(c, globals(), {'something': 1})
1371 tb = test_tb(f, 6, None)
1372 exc = traceback.TracebackException(Exception, e, tb)
1373 self.assertEqual(exc.stack[0].locals, None)
1374
Berker Peksagc3f417d2015-07-24 17:36:21 +03001375 def test_traceback_header(self):
1376 # do not print a traceback header if exc_traceback is None
1377 # see issue #24695
1378 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1379 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1380
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001381
Berker Peksag716b3d32015-04-08 09:47:14 +03001382class MiscTest(unittest.TestCase):
1383
1384 def test_all(self):
1385 expected = set()
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001386 denylist = {'print_list'}
Berker Peksag716b3d32015-04-08 09:47:14 +03001387 for name in dir(traceback):
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001388 if name.startswith('_') or name in denylist:
Berker Peksag716b3d32015-04-08 09:47:14 +03001389 continue
1390 module_object = getattr(traceback, name)
1391 if getattr(module_object, '__module__', None) == 'traceback':
1392 expected.add(name)
1393 self.assertCountEqual(traceback.__all__, expected)
1394
Fred Drake2e2be372001-09-20 21:33:42 +00001395
1396if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001397 unittest.main()