blob: 5df701caf0f01e71afa6f5fcffc94410e022e3af [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
Martin Panterbb8b1cb2016-09-22 09:37:39 +0000690 def test_message_none(self):
691 # A message that looks like "None" should not be treated specially
692 err = self.get_report(Exception(None))
693 self.assertIn('Exception: None\n', err)
694 err = self.get_report(Exception('None'))
695 self.assertIn('Exception: None\n', err)
696 err = self.get_report(Exception())
697 self.assertIn('Exception\n', err)
698 err = self.get_report(Exception(''))
699 self.assertIn('Exception\n', err)
700
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700701 def test_syntax_error_various_offsets(self):
702 for offset in range(-5, 10):
703 for add in [0, 2]:
704 text = " "*add + "text%d" % offset
705 expected = [' File "file.py", line 1']
706 if offset < 1:
707 expected.append(" %s" % text.lstrip())
708 elif offset <= 6:
709 expected.append(" %s" % text.lstrip())
710 expected.append(" %s^" % (" "*(offset-1)))
711 else:
712 expected.append(" %s" % text.lstrip())
713 expected.append(" %s^" % (" "*5))
714 expected.append("SyntaxError: msg")
715 expected.append("")
716 err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text)))
717 exp = "\n".join(expected)
718 self.assertEqual(exp, err)
719
Benjamin Petersone6528212008-07-15 15:32:09 +0000720
721class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
722 #
723 # This checks reporting through the 'traceback' module, with both
724 # format_exception() and print_exception().
725 #
726
727 def get_report(self, e):
728 e = self.get_exception(e)
729 s = ''.join(
730 traceback.format_exception(type(e), e, e.__traceback__))
731 with captured_output("stderr") as sio:
732 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000733 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000734 return s
735
736
737class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
738 #
739 # This checks built-in reporting by the interpreter.
740 #
741
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200742 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000743 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200744 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000745 e = self.get_exception(e)
746 with captured_output("stderr") as s:
747 exception_print(e)
748 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000749
750
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300751class LimitTests(unittest.TestCase):
752
753 ''' Tests for limit argument.
754 It's enough to test extact_tb, extract_stack and format_exception '''
755
756 def last_raises1(self):
757 raise Exception('Last raised')
758
759 def last_raises2(self):
760 self.last_raises1()
761
762 def last_raises3(self):
763 self.last_raises2()
764
765 def last_raises4(self):
766 self.last_raises3()
767
768 def last_raises5(self):
769 self.last_raises4()
770
771 def last_returns_frame1(self):
772 return sys._getframe()
773
774 def last_returns_frame2(self):
775 return self.last_returns_frame1()
776
777 def last_returns_frame3(self):
778 return self.last_returns_frame2()
779
780 def last_returns_frame4(self):
781 return self.last_returns_frame3()
782
783 def last_returns_frame5(self):
784 return self.last_returns_frame4()
785
786 def test_extract_stack(self):
787 frame = self.last_returns_frame5()
788 def extract(**kwargs):
789 return traceback.extract_stack(frame, **kwargs)
790 def assertEqualExcept(actual, expected, ignore):
791 self.assertEqual(actual[:ignore], expected[:ignore])
792 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
793 self.assertEqual(len(actual), len(expected))
794
795 with support.swap_attr(sys, 'tracebacklimit', 1000):
796 nolim = extract()
797 self.assertGreater(len(nolim), 5)
798 self.assertEqual(extract(limit=2), nolim[-2:])
799 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
800 self.assertEqual(extract(limit=-2), nolim[:2])
801 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
802 self.assertEqual(extract(limit=0), [])
803 del sys.tracebacklimit
804 assertEqualExcept(extract(), nolim, -5-1)
805 sys.tracebacklimit = 2
806 self.assertEqual(extract(), nolim[-2:])
807 self.assertEqual(extract(limit=3), nolim[-3:])
808 self.assertEqual(extract(limit=-3), nolim[:3])
809 sys.tracebacklimit = 0
810 self.assertEqual(extract(), [])
811 sys.tracebacklimit = -1
812 self.assertEqual(extract(), [])
813
814 def test_extract_tb(self):
815 try:
816 self.last_raises5()
817 except Exception:
818 exc_type, exc_value, tb = sys.exc_info()
819 def extract(**kwargs):
820 return traceback.extract_tb(tb, **kwargs)
821
822 with support.swap_attr(sys, 'tracebacklimit', 1000):
823 nolim = extract()
824 self.assertEqual(len(nolim), 5+1)
825 self.assertEqual(extract(limit=2), nolim[:2])
826 self.assertEqual(extract(limit=10), nolim)
827 self.assertEqual(extract(limit=-2), nolim[-2:])
828 self.assertEqual(extract(limit=-10), nolim)
829 self.assertEqual(extract(limit=0), [])
830 del sys.tracebacklimit
831 self.assertEqual(extract(), nolim)
832 sys.tracebacklimit = 2
833 self.assertEqual(extract(), nolim[:2])
834 self.assertEqual(extract(limit=3), nolim[:3])
835 self.assertEqual(extract(limit=-3), nolim[-3:])
836 sys.tracebacklimit = 0
837 self.assertEqual(extract(), [])
838 sys.tracebacklimit = -1
839 self.assertEqual(extract(), [])
840
841 def test_format_exception(self):
842 try:
843 self.last_raises5()
844 except Exception:
845 exc_type, exc_value, tb = sys.exc_info()
846 # [1:-1] to exclude "Traceback (...)" header and
847 # exception type and value
848 def extract(**kwargs):
849 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
850
851 with support.swap_attr(sys, 'tracebacklimit', 1000):
852 nolim = extract()
853 self.assertEqual(len(nolim), 5+1)
854 self.assertEqual(extract(limit=2), nolim[:2])
855 self.assertEqual(extract(limit=10), nolim)
856 self.assertEqual(extract(limit=-2), nolim[-2:])
857 self.assertEqual(extract(limit=-10), nolim)
858 self.assertEqual(extract(limit=0), [])
859 del sys.tracebacklimit
860 self.assertEqual(extract(), nolim)
861 sys.tracebacklimit = 2
862 self.assertEqual(extract(), nolim[:2])
863 self.assertEqual(extract(limit=3), nolim[:3])
864 self.assertEqual(extract(limit=-3), nolim[-3:])
865 sys.tracebacklimit = 0
866 self.assertEqual(extract(), [])
867 sys.tracebacklimit = -1
868 self.assertEqual(extract(), [])
869
870
Andrew Kuchling173a1572013-09-15 18:15:56 -0400871class MiscTracebackCases(unittest.TestCase):
872 #
873 # Check non-printing functions in traceback module
874 #
875
876 def test_clear(self):
877 def outer():
878 middle()
879 def middle():
880 inner()
881 def inner():
882 i = 1
883 1/0
884
885 try:
886 outer()
887 except:
888 type_, value, tb = sys.exc_info()
889
890 # Initial assertion: there's one local in the inner frame.
891 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
892 self.assertEqual(len(inner_frame.f_locals), 1)
893
894 # Clear traceback frames
895 traceback.clear_frames(tb)
896
897 # Local variable dict should now be empty.
898 self.assertEqual(len(inner_frame.f_locals), 0)
899
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300900 def test_extract_stack(self):
901 def extract():
902 return traceback.extract_stack()
903 result = extract()
904 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300905 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300906 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
907 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
908 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300909 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300910
Andrew Kuchling173a1572013-09-15 18:15:56 -0400911
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300912class TestFrame(unittest.TestCase):
913
914 def test_basics(self):
915 linecache.clearcache()
916 linecache.lazycache("f", globals())
917 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300918 self.assertEqual(f,
919 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
920 self.assertEqual(tuple(f),
921 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
922 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
923 self.assertEqual(f, tuple(f))
924 # Since tuple.__eq__ doesn't support FrameSummary, the equality
925 # operator fallbacks to FrameSummary.__eq__.
926 self.assertEqual(tuple(f), f)
927 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +0300928 self.assertNotEqual(f, object())
929 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300930
931 def test_lazy_lines(self):
932 linecache.clearcache()
933 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
934 self.assertEqual(None, f._line)
935 linecache.lazycache("f", globals())
936 self.assertEqual(
937 '"""Test cases for traceback module"""',
938 f.line)
939
940 def test_explicit_line(self):
941 f = traceback.FrameSummary("f", 1, "dummy", line="line")
942 self.assertEqual("line", f.line)
943
Berker Peksag9797b7a2018-09-10 20:02:33 +0300944 def test_len(self):
945 f = traceback.FrameSummary("f", 1, "dummy", line="line")
946 self.assertEqual(len(f), 4)
947
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300948
949class TestStack(unittest.TestCase):
950
951 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +0300952 def deeper():
953 return list(traceback.walk_stack(None))
954 s1 = list(traceback.walk_stack(None))
955 s2 = deeper()
956 self.assertEqual(len(s2) - len(s1), 1)
957 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300958
959 def test_walk_tb(self):
960 try:
961 1/0
962 except Exception:
963 _, _, tb = sys.exc_info()
964 s = list(traceback.walk_tb(tb))
965 self.assertEqual(len(s), 1)
966
967 def test_extract_stack(self):
968 s = traceback.StackSummary.extract(traceback.walk_stack(None))
969 self.assertIsInstance(s, traceback.StackSummary)
970
971 def test_extract_stack_limit(self):
972 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
973 self.assertEqual(len(s), 5)
974
975 def test_extract_stack_lookup_lines(self):
976 linecache.clearcache()
977 linecache.updatecache('/foo.py', globals())
978 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300979 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300980 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
981 linecache.clearcache()
982 self.assertEqual(s[0].line, "import sys")
983
984 def test_extract_stackup_deferred_lookup_lines(self):
985 linecache.clearcache()
986 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300987 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300988 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
989 self.assertEqual({}, linecache.cache)
990 linecache.updatecache('/foo.py', globals())
991 self.assertEqual(s[0].line, "import sys")
992
993 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300994 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300995 self.assertEqual(
996 [' File "foo.py", line 1, in fred\n line\n'],
997 s.format())
998
Robert Collinsbbb8ade2015-03-16 15:27:16 +1300999 def test_from_list_edited_stack(self):
1000 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
1001 s[0] = ('foo.py', 2, 'fred', 'line')
1002 s2 = traceback.StackSummary.from_list(s)
1003 self.assertEqual(
1004 [' File "foo.py", line 2, in fred\n line\n'],
1005 s2.format())
1006
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001007 def test_format_smoke(self):
1008 # For detailed tests see the format_list tests, which consume the same
1009 # code.
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001010 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001011 self.assertEqual(
1012 [' File "foo.py", line 1, in fred\n line\n'],
1013 s.format())
1014
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001015 def test_locals(self):
1016 linecache.updatecache('/foo.py', globals())
1017 c = test_code('/foo.py', 'method')
1018 f = test_frame(c, globals(), {'something': 1})
1019 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
1020 self.assertEqual(s[0].locals, {'something': '1'})
1021
1022 def test_no_locals(self):
1023 linecache.updatecache('/foo.py', globals())
1024 c = test_code('/foo.py', 'method')
1025 f = test_frame(c, globals(), {'something': 1})
1026 s = traceback.StackSummary.extract(iter([(f, 6)]))
1027 self.assertEqual(s[0].locals, None)
1028
1029 def test_format_locals(self):
1030 def some_inner(k, v):
1031 a = 1
1032 b = 2
1033 return traceback.StackSummary.extract(
1034 traceback.walk_stack(None), capture_locals=True, limit=1)
1035 s = some_inner(3, 4)
1036 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +03001037 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001038 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001039 ' a = 1\n'
1040 ' b = 2\n'
1041 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001042 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001043 ], s.format())
1044
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001045class TestTracebackException(unittest.TestCase):
1046
1047 def test_smoke(self):
1048 try:
1049 1/0
1050 except Exception:
1051 exc_info = sys.exc_info()
1052 exc = traceback.TracebackException(*exc_info)
1053 expected_stack = traceback.StackSummary.extract(
1054 traceback.walk_tb(exc_info[2]))
1055 self.assertEqual(None, exc.__cause__)
1056 self.assertEqual(None, exc.__context__)
1057 self.assertEqual(False, exc.__suppress_context__)
1058 self.assertEqual(expected_stack, exc.stack)
1059 self.assertEqual(exc_info[0], exc.exc_type)
1060 self.assertEqual(str(exc_info[1]), str(exc))
1061
1062 def test_from_exception(self):
1063 # Check all the parameters are accepted.
1064 def foo():
1065 1/0
1066 try:
1067 foo()
1068 except Exception as e:
1069 exc_info = sys.exc_info()
1070 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001071 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1072 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001073 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001074 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001075 expected_stack = self.expected_stack
1076 exc = self.exc
1077 self.assertEqual(None, exc.__cause__)
1078 self.assertEqual(None, exc.__context__)
1079 self.assertEqual(False, exc.__suppress_context__)
1080 self.assertEqual(expected_stack, exc.stack)
1081 self.assertEqual(exc_info[0], exc.exc_type)
1082 self.assertEqual(str(exc_info[1]), str(exc))
1083
1084 def test_cause(self):
1085 try:
1086 try:
1087 1/0
1088 finally:
1089 exc_info_context = sys.exc_info()
1090 exc_context = traceback.TracebackException(*exc_info_context)
1091 cause = Exception("cause")
1092 raise Exception("uh oh") from cause
1093 except Exception:
1094 exc_info = sys.exc_info()
1095 exc = traceback.TracebackException(*exc_info)
1096 expected_stack = traceback.StackSummary.extract(
1097 traceback.walk_tb(exc_info[2]))
1098 exc_cause = traceback.TracebackException(Exception, cause, None)
1099 self.assertEqual(exc_cause, exc.__cause__)
1100 self.assertEqual(exc_context, exc.__context__)
1101 self.assertEqual(True, exc.__suppress_context__)
1102 self.assertEqual(expected_stack, exc.stack)
1103 self.assertEqual(exc_info[0], exc.exc_type)
1104 self.assertEqual(str(exc_info[1]), str(exc))
1105
1106 def test_context(self):
1107 try:
1108 try:
1109 1/0
1110 finally:
1111 exc_info_context = sys.exc_info()
1112 exc_context = traceback.TracebackException(*exc_info_context)
1113 raise Exception("uh oh")
1114 except Exception:
1115 exc_info = sys.exc_info()
1116 exc = traceback.TracebackException(*exc_info)
1117 expected_stack = traceback.StackSummary.extract(
1118 traceback.walk_tb(exc_info[2]))
1119 self.assertEqual(None, exc.__cause__)
1120 self.assertEqual(exc_context, exc.__context__)
1121 self.assertEqual(False, exc.__suppress_context__)
1122 self.assertEqual(expected_stack, exc.stack)
1123 self.assertEqual(exc_info[0], exc.exc_type)
1124 self.assertEqual(str(exc_info[1]), str(exc))
1125
Irit Katriel427613f2020-12-01 01:35:25 +00001126 def test_no_refs_to_exception_and_traceback_objects(self):
1127 try:
1128 1/0
1129 except Exception:
1130 exc_info = sys.exc_info()
1131
1132 refcnt1 = sys.getrefcount(exc_info[1])
1133 refcnt2 = sys.getrefcount(exc_info[2])
1134 exc = traceback.TracebackException(*exc_info)
1135 self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
1136 self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
1137
Irit Katriel44ca05a2020-11-27 16:38:54 +00001138 def test_comparison_basic(self):
Serhiy Storchaka662db122019-08-08 08:42:54 +03001139 try:
1140 1/0
1141 except Exception:
1142 exc_info = sys.exc_info()
1143 exc = traceback.TracebackException(*exc_info)
1144 exc2 = traceback.TracebackException(*exc_info)
1145 self.assertIsNot(exc, exc2)
1146 self.assertEqual(exc, exc2)
1147 self.assertNotEqual(exc, object())
1148 self.assertEqual(exc, ALWAYS_EQ)
1149
Irit Katriel44ca05a2020-11-27 16:38:54 +00001150 def test_comparison_params_variations(self):
1151 def raise_exc():
1152 try:
1153 raise ValueError('bad value')
1154 except:
1155 raise
1156
1157 def raise_with_locals():
1158 x, y = 1, 2
1159 raise_exc()
1160
1161 try:
1162 raise_with_locals()
1163 except Exception:
1164 exc_info = sys.exc_info()
1165
1166 exc = traceback.TracebackException(*exc_info)
1167 exc1 = traceback.TracebackException(*exc_info, limit=10)
1168 exc2 = traceback.TracebackException(*exc_info, limit=2)
1169
1170 self.assertEqual(exc, exc1) # limit=10 gets all frames
1171 self.assertNotEqual(exc, exc2) # limit=2 truncates the output
1172
1173 # locals change the output
1174 exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
1175 self.assertNotEqual(exc, exc3)
1176
1177 # there are no locals in the innermost frame
1178 exc4 = traceback.TracebackException(*exc_info, limit=-1)
1179 exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
1180 self.assertEqual(exc4, exc5)
1181
1182 # there are locals in the next-to-innermost frame
1183 exc6 = traceback.TracebackException(*exc_info, limit=-2)
1184 exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
1185 self.assertNotEqual(exc6, exc7)
1186
Irit Katriel427613f2020-12-01 01:35:25 +00001187 def test_comparison_equivalent_exceptions_are_equal(self):
1188 excs = []
1189 for _ in range(2):
1190 try:
1191 1/0
1192 except:
1193 excs.append(traceback.TracebackException(*sys.exc_info()))
1194 self.assertEqual(excs[0], excs[1])
1195 self.assertEqual(list(excs[0].format()), list(excs[1].format()))
1196
Zane Bitterde860732017-10-17 17:29:39 -04001197 def test_unhashable(self):
1198 class UnhashableException(Exception):
1199 def __eq__(self, other):
1200 return True
1201
1202 ex1 = UnhashableException('ex1')
1203 ex2 = UnhashableException('ex2')
1204 try:
1205 raise ex2 from ex1
1206 except UnhashableException:
1207 try:
1208 raise ex1
1209 except UnhashableException:
1210 exc_info = sys.exc_info()
1211 exc = traceback.TracebackException(*exc_info)
1212 formatted = list(exc.format())
1213 self.assertIn('UnhashableException: ex2\n', formatted[2])
1214 self.assertIn('UnhashableException: ex1\n', formatted[6])
1215
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001216 def test_limit(self):
1217 def recurse(n):
1218 if n:
1219 recurse(n-1)
1220 else:
1221 1/0
1222 try:
1223 recurse(10)
1224 except Exception:
1225 exc_info = sys.exc_info()
1226 exc = traceback.TracebackException(*exc_info, limit=5)
1227 expected_stack = traceback.StackSummary.extract(
1228 traceback.walk_tb(exc_info[2]), limit=5)
1229 self.assertEqual(expected_stack, exc.stack)
1230
1231 def test_lookup_lines(self):
1232 linecache.clearcache()
1233 e = Exception("uh oh")
1234 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001235 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001236 tb = test_tb(f, 6, None)
1237 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
Irit Katriel44ca05a2020-11-27 16:38:54 +00001238 self.assertEqual(linecache.cache, {})
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001239 linecache.updatecache('/foo.py', globals())
1240 self.assertEqual(exc.stack[0].line, "import sys")
1241
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001242 def test_locals(self):
1243 linecache.updatecache('/foo.py', globals())
1244 e = Exception("uh oh")
1245 c = test_code('/foo.py', 'method')
1246 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1247 tb = test_tb(f, 6, None)
1248 exc = traceback.TracebackException(
1249 Exception, e, tb, capture_locals=True)
1250 self.assertEqual(
1251 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1252
1253 def test_no_locals(self):
1254 linecache.updatecache('/foo.py', globals())
1255 e = Exception("uh oh")
1256 c = test_code('/foo.py', 'method')
1257 f = test_frame(c, globals(), {'something': 1})
1258 tb = test_tb(f, 6, None)
1259 exc = traceback.TracebackException(Exception, e, tb)
1260 self.assertEqual(exc.stack[0].locals, None)
1261
Berker Peksagc3f417d2015-07-24 17:36:21 +03001262 def test_traceback_header(self):
1263 # do not print a traceback header if exc_traceback is None
1264 # see issue #24695
1265 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1266 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1267
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001268
Berker Peksag716b3d32015-04-08 09:47:14 +03001269class MiscTest(unittest.TestCase):
1270
1271 def test_all(self):
1272 expected = set()
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001273 denylist = {'print_list'}
Berker Peksag716b3d32015-04-08 09:47:14 +03001274 for name in dir(traceback):
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001275 if name.startswith('_') or name in denylist:
Berker Peksag716b3d32015-04-08 09:47:14 +03001276 continue
1277 module_object = getattr(traceback, name)
1278 if getattr(module_object, '__module__', None) == 'traceback':
1279 expected.add(name)
1280 self.assertCountEqual(traceback.__all__, expected)
1281
Fred Drake2e2be372001-09-20 21:33:42 +00001282
1283if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001284 unittest.main()