blob: 925a6bc32e8ea797975518b879962a8c54fc62f3 [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
Serhiy Storchaka662db122019-08-08 08:42:54 +030010from test.support import TESTFN, Error, captured_output, unlink, cpython_only, ALWAYS_EQ
Berker Peksagce643912015-05-06 06:33:17 +030011from test.support.script_helper import assert_python_ok
Victor Stinner9d279b82014-12-05 10:18:30 +010012import textwrap
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000013
14import traceback
15
Christian Heimes81ee3ef2008-05-04 22:42:01 +000016
Robert Collins6bc2c1e2015-03-05 12:07:57 +130017test_code = namedtuple('code', ['co_filename', 'co_name'])
Robert Collinsd7c7e0e2015-03-05 20:28:52 +130018test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals'])
Robert Collins6bc2c1e2015-03-05 12:07:57 +130019test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next'])
20
21
Martin Panterbb8b1cb2016-09-22 09:37:39 +000022class TracebackCases(unittest.TestCase):
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000023 # For now, a very minimal set of tests. I want to be sure that
24 # formatting of SyntaxErrors works based on changes for 2.1.
25
26 def get_exception_format(self, func, exc):
27 try:
28 func()
Guido van Rossumb940e112007-01-10 16:19:56 +000029 except exc as value:
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000030 return traceback.format_exception_only(exc, value)
31 else:
Collin Winter3add4d72007-08-29 23:37:32 +000032 raise ValueError("call did not raise exception")
Tim Peters7e01e282001-04-08 07:44:07 +000033
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000034 def syntax_error_with_caret(self):
35 compile("def fact(x):\n\treturn x!\n", "?", "exec")
36
Georg Brandl751899a2009-06-04 19:41:00 +000037 def syntax_error_with_caret_2(self):
38 compile("1 +\n", "?", "exec")
39
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000040 def syntax_error_bad_indentation(self):
Georg Brandl88fc6642007-02-09 21:28:07 +000041 compile("def spam():\n print(1)\n print(2)", "?", "exec")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000042
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020043 def syntax_error_with_caret_non_ascii(self):
44 compile('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', "?", "exec")
45
Florent Xicluna758fa5e2014-01-22 01:11:43 +010046 def syntax_error_bad_indentation2(self):
47 compile(" print(2)", "?", "exec")
48
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000049 def test_caret(self):
50 err = self.get_exception_format(self.syntax_error_with_caret,
51 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000052 self.assertEqual(len(err), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000053 self.assertTrue(err[1].strip() == "return x!")
Benjamin Peterson577473f2010-01-19 00:09:57 +000054 self.assertIn("^", err[2]) # third line has caret
Guido van Rossume61fd5b2007-07-11 12:20:59 +000055 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place
Tim Peters7e01e282001-04-08 07:44:07 +000056
Georg Brandl751899a2009-06-04 19:41:00 +000057 err = self.get_exception_format(self.syntax_error_with_caret_2,
58 SyntaxError)
Benjamin Peterson577473f2010-01-19 00:09:57 +000059 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010060 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070061 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Georg Brandl751899a2009-06-04 19:41:00 +000062
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020063 err = self.get_exception_format(self.syntax_error_with_caret_non_ascii,
64 SyntaxError)
65 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010066 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070067 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020068
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000069 def test_nocaret(self):
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000070 exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
71 err = traceback.format_exception_only(SyntaxError, exc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000072 self.assertEqual(len(err), 3)
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000073 self.assertEqual(err[1].strip(), "bad syntax")
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000074
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000075 def test_bad_indentation(self):
76 err = self.get_exception_format(self.syntax_error_bad_indentation,
77 IndentationError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000078 self.assertEqual(len(err), 4)
79 self.assertEqual(err[1].strip(), "print(2)")
Benjamin Peterson577473f2010-01-19 00:09:57 +000080 self.assertIn("^", err[2])
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070081 self.assertEqual(err[1].find(")") + 1, err[2].find("^"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000082
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070083 # No caret for "unexpected indent"
Florent Xicluna758fa5e2014-01-22 01:11:43 +010084 err = self.get_exception_format(self.syntax_error_bad_indentation2,
85 IndentationError)
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070086 self.assertEqual(len(err), 3)
Florent Xicluna758fa5e2014-01-22 01:11:43 +010087 self.assertEqual(err[1].strip(), "print(2)")
Florent Xicluna758fa5e2014-01-22 01:11:43 +010088
Thomas Wouters477c8d52006-05-27 19:21:47 +000089 def test_base_exception(self):
90 # Test that exceptions derived from BaseException are formatted right
91 e = KeyboardInterrupt()
92 lst = traceback.format_exception_only(e.__class__, e)
93 self.assertEqual(lst, ['KeyboardInterrupt\n'])
94
Thomas Wouters0e3f5912006-08-11 14:57:12 +000095 def test_format_exception_only_bad__str__(self):
96 class X(Exception):
97 def __str__(self):
98 1/0
99 err = traceback.format_exception_only(X, X())
100 self.assertEqual(len(err), 1)
101 str_value = '<unprintable %s object>' % X.__name__
Georg Brandl1a3284e2007-12-02 09:40:06 +0000102 if X.__module__ in ('__main__', 'builtins'):
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300103 str_name = X.__qualname__
Brett Cannon44c52612007-02-27 00:12:43 +0000104 else:
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300105 str_name = '.'.join([X.__module__, X.__qualname__])
Brett Cannon44c52612007-02-27 00:12:43 +0000106 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000107
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000108 def test_encoded_file(self):
109 # Test that tracebacks are correctly printed for encoded source files:
110 # - correct line number (Issue2384)
111 # - respect file encoding (Issue3975)
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)90d0cfb2018-11-16 21:02:58 +0530112 import sys, subprocess
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000113
114 # The spawned subprocess has its stdout redirected to a PIPE, and its
115 # encoding may be different from the current interpreter, on Windows
116 # at least.
117 process = subprocess.Popen([sys.executable, "-c",
118 "import sys; print(sys.stdout.encoding)"],
119 stdout=subprocess.PIPE,
120 stderr=subprocess.STDOUT)
121 stdout, stderr = process.communicate()
122 output_encoding = str(stdout, 'ascii').splitlines()[0]
123
124 def do_test(firstlines, message, charset, lineno):
125 # Raise the message in a subprocess, and catch the output
126 try:
Victor Stinner51d8c522016-02-08 17:57:02 +0100127 with open(TESTFN, "w", encoding=charset) as output:
128 output.write("""{0}if 1:
129 import traceback;
130 raise RuntimeError('{1}')
131 """.format(firstlines, message))
132
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000133 process = subprocess.Popen([sys.executable, TESTFN],
134 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
135 stdout, stderr = process.communicate()
136 stdout = stdout.decode(output_encoding).splitlines()
137 finally:
138 unlink(TESTFN)
139
140 # The source lines are encoded with the 'backslashreplace' handler
141 encoded_message = message.encode(output_encoding,
142 'backslashreplace')
143 # and we just decoded them with the output_encoding.
144 message_ascii = encoded_message.decode(output_encoding)
145
146 err_line = "raise RuntimeError('{0}')".format(message_ascii)
147 err_msg = "RuntimeError: {0}".format(message_ascii)
148
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000149 self.assertIn(("line %s" % lineno), stdout[1],
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000150 "Invalid line number: {0!r} instead of {1}".format(
151 stdout[1], lineno))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000152 self.assertTrue(stdout[2].endswith(err_line),
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000153 "Invalid traceback line: {0!r} instead of {1!r}".format(
154 stdout[2], err_line))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000155 self.assertTrue(stdout[3] == err_msg,
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000156 "Invalid error message: {0!r} instead of {1!r}".format(
157 stdout[3], err_msg))
158
159 do_test("", "foo", "ascii", 3)
160 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
161 if charset == "ascii":
162 text = "foo"
163 elif charset == "GBK":
164 text = "\u4E02\u5100"
165 else:
166 text = "h\xe9 ho"
167 do_test("# coding: {0}\n".format(charset),
168 text, charset, 4)
169 do_test("#!shebang\n# coding: {0}\n".format(charset),
170 text, charset, 5)
Serhiy Storchaka1064a132014-01-09 20:12:49 +0200171 do_test(" \t\f\n# coding: {0}\n".format(charset),
172 text, charset, 5)
Martin Panter614827c2016-04-19 04:05:59 +0000173 # Issue #18960: coding spec should have no effect
Victor Stinner51d8c522016-02-08 17:57:02 +0100174 do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000175
Victor Stinner9d279b82014-12-05 10:18:30 +0100176 def test_print_traceback_at_exit(self):
177 # Issue #22599: Ensure that it is possible to use the traceback module
178 # to display an exception at Python exit
179 code = textwrap.dedent("""
180 import sys
181 import traceback
182
183 class PrintExceptionAtExit(object):
184 def __init__(self):
185 try:
186 x = 1 / 0
187 except Exception:
188 self.exc_info = sys.exc_info()
189 # self.exc_info[1] (traceback) contains frames:
190 # explicitly clear the reference to self in the current
191 # frame to break a reference cycle
192 self = None
193
194 def __del__(self):
195 traceback.print_exception(*self.exc_info)
196
197 # Keep a reference in the module namespace to call the destructor
198 # when the module is unloaded
199 obj = PrintExceptionAtExit()
200 """)
201 rc, stdout, stderr = assert_python_ok('-c', code)
202 expected = [b'Traceback (most recent call last):',
203 b' File "<string>", line 8, in __init__',
204 b'ZeroDivisionError: division by zero']
205 self.assertEqual(stderr.splitlines(), expected)
206
Berker Peksagc3f417d2015-07-24 17:36:21 +0300207 def test_print_exception(self):
208 output = StringIO()
209 traceback.print_exception(
210 Exception, Exception("projector"), None, file=output
211 )
212 self.assertEqual(output.getvalue(), "Exception: projector\n")
213
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000214
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000215class TracebackFormatTests(unittest.TestCase):
216
Antoine Pitrou58720d62013-08-05 23:26:40 +0200217 def some_exception(self):
218 raise KeyError('blah')
219
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200220 @cpython_only
Antoine Pitrou58720d62013-08-05 23:26:40 +0200221 def check_traceback_format(self, cleanup_func=None):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200222 from _testcapi import traceback_print
Georg Brandl236f7972009-04-05 14:28:42 +0000223 try:
Antoine Pitrou58720d62013-08-05 23:26:40 +0200224 self.some_exception()
Georg Brandl236f7972009-04-05 14:28:42 +0000225 except KeyError:
226 type_, value, tb = sys.exc_info()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200227 if cleanup_func is not None:
228 # Clear the inner frames, not this one
229 cleanup_func(tb.tb_next)
Georg Brandl236f7972009-04-05 14:28:42 +0000230 traceback_fmt = 'Traceback (most recent call last):\n' + \
231 ''.join(traceback.format_tb(tb))
232 file_ = StringIO()
233 traceback_print(tb, file_)
234 python_fmt = file_.getvalue()
Benjamin Petersond9fec152013-04-29 16:09:39 -0400235 # Call all _tb and _exc functions
236 with captured_output("stderr") as tbstderr:
237 traceback.print_tb(tb)
238 tbfile = StringIO()
239 traceback.print_tb(tb, file=tbfile)
240 with captured_output("stderr") as excstderr:
241 traceback.print_exc()
242 excfmt = traceback.format_exc()
243 excfile = StringIO()
244 traceback.print_exc(file=excfile)
Georg Brandl236f7972009-04-05 14:28:42 +0000245 else:
246 raise Error("unable to create test traceback string")
247
248 # Make sure that Python and the traceback module format the same thing
Ezio Melottib3aedd42010-11-20 19:04:17 +0000249 self.assertEqual(traceback_fmt, python_fmt)
Benjamin Petersond9fec152013-04-29 16:09:39 -0400250 # Now verify the _tb func output
251 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
252 # Now verify the _exc func output
253 self.assertEqual(excstderr.getvalue(), excfile.getvalue())
254 self.assertEqual(excfmt, excfile.getvalue())
Georg Brandl236f7972009-04-05 14:28:42 +0000255
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000256 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000257 tb_lines = python_fmt.splitlines()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200258 self.assertEqual(len(tb_lines), 5)
259 banner = tb_lines[0]
260 location, source_line = tb_lines[-2:]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000261 self.assertTrue(banner.startswith('Traceback'))
262 self.assertTrue(location.startswith(' File'))
263 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000264
Antoine Pitrou58720d62013-08-05 23:26:40 +0200265 def test_traceback_format(self):
266 self.check_traceback_format()
267
268 def test_traceback_format_with_cleared_frames(self):
269 # Check that traceback formatting also works with a clear()ed frame
270 def cleanup_tb(tb):
271 tb.tb_frame.clear()
272 self.check_traceback_format(cleanup_tb)
273
Benjamin Petersond9fec152013-04-29 16:09:39 -0400274 def test_stack_format(self):
275 # Verify _stack functions. Note we have to use _getframe(1) to
276 # compare them without this frame appearing in the output
277 with captured_output("stderr") as ststderr:
278 traceback.print_stack(sys._getframe(1))
279 stfile = StringIO()
280 traceback.print_stack(sys._getframe(1), file=stfile)
281 self.assertEqual(ststderr.getvalue(), stfile.getvalue())
282
283 stfmt = traceback.format_stack(sys._getframe(1))
284
285 self.assertEqual(ststderr.getvalue(), "".join(stfmt))
286
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300287 def test_print_stack(self):
288 def prn():
289 traceback.print_stack()
290 with captured_output("stderr") as stderr:
291 prn()
292 lineno = prn.__code__.co_firstlineno
293 self.assertEqual(stderr.getvalue().splitlines()[-4:], [
294 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3),
295 ' prn()',
296 ' File "%s", line %d, in prn' % (__file__, lineno+1),
297 ' traceback.print_stack()',
298 ])
299
Nick Coghland0034232016-08-15 13:11:34 +1000300 # issue 26823 - Shrink recursive tracebacks
301 def _check_recursive_traceback_display(self, render_exc):
302 # Always show full diffs when this test fails
303 # Note that rearranging things may require adjusting
304 # the relative line numbers in the expected tracebacks
305 self.maxDiff = None
306
307 # Check hitting the recursion limit
308 def f():
309 f()
310
311 with captured_output("stderr") as stderr_f:
312 try:
313 f()
Pablo Galindo293dd232019-11-19 21:34:03 +0000314 except RecursionError:
Nick Coghland0034232016-08-15 13:11:34 +1000315 render_exc()
316 else:
317 self.fail("no recursion occurred")
318
319 lineno_f = f.__code__.co_firstlineno
320 result_f = (
321 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400322 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000323 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400324 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000325 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400326 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000327 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400328 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000329 ' f()\n'
330 # XXX: The following line changes depending on whether the tests
331 # are run through the interactive interpreter or with -m
332 # It also varies depending on the platform (stack size)
333 # Fortunately, we don't care about exactness here, so we use regex
334 r' \[Previous line repeated (\d+) more times\]' '\n'
335 'RecursionError: maximum recursion depth exceeded\n'
336 )
337
338 expected = result_f.splitlines()
339 actual = stderr_f.getvalue().splitlines()
340
341 # Check the output text matches expectations
342 # 2nd last line contains the repetition count
343 self.assertEqual(actual[:-2], expected[:-2])
344 self.assertRegex(actual[-2], expected[-2])
codedragon3480ef92017-05-24 14:23:46 -0700345 # last line can have additional text appended
346 self.assertIn(expected[-1], actual[-1])
Nick Coghland0034232016-08-15 13:11:34 +1000347
348 # Check the recursion count is roughly as expected
349 rec_limit = sys.getrecursionlimit()
Serhiy Storchakab7281052016-09-12 00:52:40 +0300350 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
Nick Coghland0034232016-08-15 13:11:34 +1000351
352 # Check a known (limited) number of recursive invocations
353 def g(count=10):
354 if count:
355 return g(count-1)
356 raise ValueError
357
358 with captured_output("stderr") as stderr_g:
359 try:
360 g()
Pablo Galindo293dd232019-11-19 21:34:03 +0000361 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000362 render_exc()
363 else:
364 self.fail("no value error was raised")
365
366 lineno_g = g.__code__.co_firstlineno
367 result_g = (
Eric V. Smith451d0e32016-09-09 21:56:20 -0400368 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000369 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400370 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000371 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400372 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000373 ' return g(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700374 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400375 f' File "{__file__}", line {lineno_g+3}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000376 ' raise ValueError\n'
377 'ValueError\n'
378 )
379 tb_line = (
380 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400381 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000382 ' g()\n'
383 )
384 expected = (tb_line + result_g).splitlines()
385 actual = stderr_g.getvalue().splitlines()
386 self.assertEqual(actual, expected)
387
388 # Check 2 different repetitive sections
389 def h(count=10):
390 if count:
391 return h(count-1)
392 g()
393
394 with captured_output("stderr") as stderr_h:
395 try:
396 h()
Pablo Galindo293dd232019-11-19 21:34:03 +0000397 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000398 render_exc()
399 else:
400 self.fail("no value error was raised")
401
402 lineno_h = h.__code__.co_firstlineno
403 result_h = (
404 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400405 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000406 ' h()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400407 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000408 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400409 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000410 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400411 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000412 ' return h(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700413 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400414 f' File "{__file__}", line {lineno_h+3}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000415 ' g()\n'
416 )
417 expected = (result_h + result_g).splitlines()
418 actual = stderr_h.getvalue().splitlines()
419 self.assertEqual(actual, expected)
420
Benjamin Petersond5458692018-09-10 08:43:10 -0700421 # Check the boundary conditions. First, test just below the cutoff.
422 with captured_output("stderr") as stderr_g:
423 try:
424 g(traceback._RECURSIVE_CUTOFF)
Pablo Galindo293dd232019-11-19 21:34:03 +0000425 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700426 render_exc()
427 else:
428 self.fail("no error raised")
429 result_g = (
430 f' File "{__file__}", line {lineno_g+2}, in g\n'
431 ' return g(count-1)\n'
432 f' File "{__file__}", line {lineno_g+2}, in g\n'
433 ' return g(count-1)\n'
434 f' File "{__file__}", line {lineno_g+2}, in g\n'
435 ' return g(count-1)\n'
436 f' File "{__file__}", line {lineno_g+3}, in g\n'
437 ' raise ValueError\n'
438 'ValueError\n'
439 )
440 tb_line = (
441 'Traceback (most recent call last):\n'
442 f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n'
443 ' g(traceback._RECURSIVE_CUTOFF)\n'
444 )
445 expected = (tb_line + result_g).splitlines()
446 actual = stderr_g.getvalue().splitlines()
447 self.assertEqual(actual, expected)
448
449 # Second, test just above the cutoff.
450 with captured_output("stderr") as stderr_g:
451 try:
452 g(traceback._RECURSIVE_CUTOFF + 1)
Pablo Galindo293dd232019-11-19 21:34:03 +0000453 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700454 render_exc()
455 else:
456 self.fail("no error raised")
457 result_g = (
458 f' File "{__file__}", line {lineno_g+2}, in g\n'
459 ' return g(count-1)\n'
460 f' File "{__file__}", line {lineno_g+2}, in g\n'
461 ' return g(count-1)\n'
462 f' File "{__file__}", line {lineno_g+2}, in g\n'
463 ' return g(count-1)\n'
464 ' [Previous line repeated 1 more time]\n'
465 f' File "{__file__}", line {lineno_g+3}, in g\n'
466 ' raise ValueError\n'
467 'ValueError\n'
468 )
469 tb_line = (
470 'Traceback (most recent call last):\n'
471 f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n'
472 ' g(traceback._RECURSIVE_CUTOFF + 1)\n'
473 )
474 expected = (tb_line + result_g).splitlines()
475 actual = stderr_g.getvalue().splitlines()
476 self.assertEqual(actual, expected)
477
Nick Coghland0034232016-08-15 13:11:34 +1000478 def test_recursive_traceback_python(self):
479 self._check_recursive_traceback_display(traceback.print_exc)
480
481 @cpython_only
482 def test_recursive_traceback_cpython_internal(self):
483 from _testcapi import exception_print
484 def render_exc():
485 exc_type, exc_value, exc_tb = sys.exc_info()
486 exception_print(exc_value)
487 self._check_recursive_traceback_display(render_exc)
488
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300489 def test_format_stack(self):
490 def fmt():
491 return traceback.format_stack()
492 result = fmt()
493 lineno = fmt.__code__.co_firstlineno
494 self.assertEqual(result[-2:], [
495 ' File "%s", line %d, in test_format_stack\n'
496 ' result = fmt()\n' % (__file__, lineno+2),
497 ' File "%s", line %d, in fmt\n'
498 ' return traceback.format_stack()\n' % (__file__, lineno+1),
499 ])
500
Zane Bitterde860732017-10-17 17:29:39 -0400501 @cpython_only
502 def test_unhashable(self):
503 from _testcapi import exception_print
504
505 class UnhashableException(Exception):
506 def __eq__(self, other):
507 return True
508
509 ex1 = UnhashableException('ex1')
510 ex2 = UnhashableException('ex2')
511 try:
512 raise ex2 from ex1
513 except UnhashableException:
514 try:
515 raise ex1
516 except UnhashableException:
517 exc_type, exc_val, exc_tb = sys.exc_info()
518
519 with captured_output("stderr") as stderr_f:
520 exception_print(exc_val)
521
522 tb = stderr_f.getvalue().strip().splitlines()
523 self.assertEqual(11, len(tb))
524 self.assertEqual(context_message.strip(), tb[5])
525 self.assertIn('UnhashableException: ex2', tb[3])
526 self.assertIn('UnhashableException: ex1', tb[10])
527
Benjamin Petersone6528212008-07-15 15:32:09 +0000528
529cause_message = (
530 "\nThe above exception was the direct cause "
531 "of the following exception:\n\n")
532
533context_message = (
534 "\nDuring handling of the above exception, "
535 "another exception occurred:\n\n")
536
537boundaries = re.compile(
538 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
539
540
541class BaseExceptionReportingTests:
542
543 def get_exception(self, exception_or_callable):
544 if isinstance(exception_or_callable, Exception):
545 return exception_or_callable
546 try:
547 exception_or_callable()
548 except Exception as e:
549 return e
550
551 def zero_div(self):
552 1/0 # In zero_div
553
554 def check_zero_div(self, msg):
555 lines = msg.splitlines()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000556 self.assertTrue(lines[-3].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000557 self.assertIn('1/0 # In zero_div', lines[-2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000558 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000559
560 def test_simple(self):
561 try:
562 1/0 # Marker
563 except ZeroDivisionError as _:
564 e = _
565 lines = self.get_report(e).splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000566 self.assertEqual(len(lines), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000567 self.assertTrue(lines[0].startswith('Traceback'))
568 self.assertTrue(lines[1].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000569 self.assertIn('1/0 # Marker', lines[2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000570 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000571
572 def test_cause(self):
573 def inner_raise():
574 try:
575 self.zero_div()
576 except ZeroDivisionError as e:
577 raise KeyError from e
578 def outer_raise():
579 inner_raise() # Marker
580 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000581 self.assertEqual(len(blocks), 3)
582 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000583 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000584 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000585
586 def test_context(self):
587 def inner_raise():
588 try:
589 self.zero_div()
590 except ZeroDivisionError:
591 raise KeyError
592 def outer_raise():
593 inner_raise() # Marker
594 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000595 self.assertEqual(len(blocks), 3)
596 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000597 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000598 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000599
Nick Coghlanab7bf212012-02-26 17:49:52 +1000600 def test_context_suppression(self):
601 try:
602 try:
603 raise Exception
604 except:
605 raise ZeroDivisionError from None
606 except ZeroDivisionError as _:
607 e = _
608 lines = self.get_report(e).splitlines()
609 self.assertEqual(len(lines), 4)
610 self.assertTrue(lines[0].startswith('Traceback'))
611 self.assertTrue(lines[1].startswith(' File'))
612 self.assertIn('ZeroDivisionError from None', lines[2])
613 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
614
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000615 def test_cause_and_context(self):
616 # When both a cause and a context are set, only the cause should be
617 # displayed and the context should be muted.
618 def inner_raise():
619 try:
620 self.zero_div()
621 except ZeroDivisionError as _e:
622 e = _e
623 try:
624 xyzzy
625 except NameError:
626 raise KeyError from e
627 def outer_raise():
628 inner_raise() # Marker
629 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000630 self.assertEqual(len(blocks), 3)
631 self.assertEqual(blocks[1], cause_message)
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000632 self.check_zero_div(blocks[0])
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000633 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000634
Benjamin Petersone6528212008-07-15 15:32:09 +0000635 def test_cause_recursive(self):
636 def inner_raise():
637 try:
638 try:
639 self.zero_div()
640 except ZeroDivisionError as e:
641 z = e
642 raise KeyError from e
643 except KeyError as e:
644 raise z from e
645 def outer_raise():
646 inner_raise() # Marker
647 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000648 self.assertEqual(len(blocks), 3)
649 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000650 # The first block is the KeyError raised from the ZeroDivisionError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000651 self.assertIn('raise KeyError from e', blocks[0])
652 self.assertNotIn('1/0', blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000653 # The second block (apart from the boundary) is the ZeroDivisionError
654 # re-raised from the KeyError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000655 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000656 self.check_zero_div(blocks[2])
657
Benjamin Peterson503d6c52010-10-24 02:52:05 +0000658 def test_syntax_error_offset_at_eol(self):
659 # See #10186.
660 def e():
661 raise SyntaxError('', ('', 0, 5, 'hello'))
662 msg = self.get_report(e).splitlines()
663 self.assertEqual(msg[-2], " ^")
Benjamin Petersona95e9772010-10-29 03:28:14 +0000664 def e():
665 exec("x = 5 | 4 |")
666 msg = self.get_report(e).splitlines()
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700667 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000668
Martin Panterbb8b1cb2016-09-22 09:37:39 +0000669 def test_message_none(self):
670 # A message that looks like "None" should not be treated specially
671 err = self.get_report(Exception(None))
672 self.assertIn('Exception: None\n', err)
673 err = self.get_report(Exception('None'))
674 self.assertIn('Exception: None\n', err)
675 err = self.get_report(Exception())
676 self.assertIn('Exception\n', err)
677 err = self.get_report(Exception(''))
678 self.assertIn('Exception\n', err)
679
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700680 def test_syntax_error_various_offsets(self):
681 for offset in range(-5, 10):
682 for add in [0, 2]:
683 text = " "*add + "text%d" % offset
684 expected = [' File "file.py", line 1']
685 if offset < 1:
686 expected.append(" %s" % text.lstrip())
687 elif offset <= 6:
688 expected.append(" %s" % text.lstrip())
689 expected.append(" %s^" % (" "*(offset-1)))
690 else:
691 expected.append(" %s" % text.lstrip())
692 expected.append(" %s^" % (" "*5))
693 expected.append("SyntaxError: msg")
694 expected.append("")
695 err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text)))
696 exp = "\n".join(expected)
697 self.assertEqual(exp, err)
698
Benjamin Petersone6528212008-07-15 15:32:09 +0000699
700class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
701 #
702 # This checks reporting through the 'traceback' module, with both
703 # format_exception() and print_exception().
704 #
705
706 def get_report(self, e):
707 e = self.get_exception(e)
708 s = ''.join(
709 traceback.format_exception(type(e), e, e.__traceback__))
710 with captured_output("stderr") as sio:
711 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000712 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000713 return s
714
715
716class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
717 #
718 # This checks built-in reporting by the interpreter.
719 #
720
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200721 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000722 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200723 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000724 e = self.get_exception(e)
725 with captured_output("stderr") as s:
726 exception_print(e)
727 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000728
729
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300730class LimitTests(unittest.TestCase):
731
732 ''' Tests for limit argument.
733 It's enough to test extact_tb, extract_stack and format_exception '''
734
735 def last_raises1(self):
736 raise Exception('Last raised')
737
738 def last_raises2(self):
739 self.last_raises1()
740
741 def last_raises3(self):
742 self.last_raises2()
743
744 def last_raises4(self):
745 self.last_raises3()
746
747 def last_raises5(self):
748 self.last_raises4()
749
750 def last_returns_frame1(self):
751 return sys._getframe()
752
753 def last_returns_frame2(self):
754 return self.last_returns_frame1()
755
756 def last_returns_frame3(self):
757 return self.last_returns_frame2()
758
759 def last_returns_frame4(self):
760 return self.last_returns_frame3()
761
762 def last_returns_frame5(self):
763 return self.last_returns_frame4()
764
765 def test_extract_stack(self):
766 frame = self.last_returns_frame5()
767 def extract(**kwargs):
768 return traceback.extract_stack(frame, **kwargs)
769 def assertEqualExcept(actual, expected, ignore):
770 self.assertEqual(actual[:ignore], expected[:ignore])
771 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
772 self.assertEqual(len(actual), len(expected))
773
774 with support.swap_attr(sys, 'tracebacklimit', 1000):
775 nolim = extract()
776 self.assertGreater(len(nolim), 5)
777 self.assertEqual(extract(limit=2), nolim[-2:])
778 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
779 self.assertEqual(extract(limit=-2), nolim[:2])
780 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
781 self.assertEqual(extract(limit=0), [])
782 del sys.tracebacklimit
783 assertEqualExcept(extract(), nolim, -5-1)
784 sys.tracebacklimit = 2
785 self.assertEqual(extract(), nolim[-2:])
786 self.assertEqual(extract(limit=3), nolim[-3:])
787 self.assertEqual(extract(limit=-3), nolim[:3])
788 sys.tracebacklimit = 0
789 self.assertEqual(extract(), [])
790 sys.tracebacklimit = -1
791 self.assertEqual(extract(), [])
792
793 def test_extract_tb(self):
794 try:
795 self.last_raises5()
796 except Exception:
797 exc_type, exc_value, tb = sys.exc_info()
798 def extract(**kwargs):
799 return traceback.extract_tb(tb, **kwargs)
800
801 with support.swap_attr(sys, 'tracebacklimit', 1000):
802 nolim = extract()
803 self.assertEqual(len(nolim), 5+1)
804 self.assertEqual(extract(limit=2), nolim[:2])
805 self.assertEqual(extract(limit=10), nolim)
806 self.assertEqual(extract(limit=-2), nolim[-2:])
807 self.assertEqual(extract(limit=-10), nolim)
808 self.assertEqual(extract(limit=0), [])
809 del sys.tracebacklimit
810 self.assertEqual(extract(), nolim)
811 sys.tracebacklimit = 2
812 self.assertEqual(extract(), nolim[:2])
813 self.assertEqual(extract(limit=3), nolim[:3])
814 self.assertEqual(extract(limit=-3), nolim[-3:])
815 sys.tracebacklimit = 0
816 self.assertEqual(extract(), [])
817 sys.tracebacklimit = -1
818 self.assertEqual(extract(), [])
819
820 def test_format_exception(self):
821 try:
822 self.last_raises5()
823 except Exception:
824 exc_type, exc_value, tb = sys.exc_info()
825 # [1:-1] to exclude "Traceback (...)" header and
826 # exception type and value
827 def extract(**kwargs):
828 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
829
830 with support.swap_attr(sys, 'tracebacklimit', 1000):
831 nolim = extract()
832 self.assertEqual(len(nolim), 5+1)
833 self.assertEqual(extract(limit=2), nolim[:2])
834 self.assertEqual(extract(limit=10), nolim)
835 self.assertEqual(extract(limit=-2), nolim[-2:])
836 self.assertEqual(extract(limit=-10), nolim)
837 self.assertEqual(extract(limit=0), [])
838 del sys.tracebacklimit
839 self.assertEqual(extract(), nolim)
840 sys.tracebacklimit = 2
841 self.assertEqual(extract(), nolim[:2])
842 self.assertEqual(extract(limit=3), nolim[:3])
843 self.assertEqual(extract(limit=-3), nolim[-3:])
844 sys.tracebacklimit = 0
845 self.assertEqual(extract(), [])
846 sys.tracebacklimit = -1
847 self.assertEqual(extract(), [])
848
849
Andrew Kuchling173a1572013-09-15 18:15:56 -0400850class MiscTracebackCases(unittest.TestCase):
851 #
852 # Check non-printing functions in traceback module
853 #
854
855 def test_clear(self):
856 def outer():
857 middle()
858 def middle():
859 inner()
860 def inner():
861 i = 1
862 1/0
863
864 try:
865 outer()
866 except:
867 type_, value, tb = sys.exc_info()
868
869 # Initial assertion: there's one local in the inner frame.
870 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
871 self.assertEqual(len(inner_frame.f_locals), 1)
872
873 # Clear traceback frames
874 traceback.clear_frames(tb)
875
876 # Local variable dict should now be empty.
877 self.assertEqual(len(inner_frame.f_locals), 0)
878
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300879 def test_extract_stack(self):
880 def extract():
881 return traceback.extract_stack()
882 result = extract()
883 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300884 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300885 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
886 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
887 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300888 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300889
Andrew Kuchling173a1572013-09-15 18:15:56 -0400890
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300891class TestFrame(unittest.TestCase):
892
893 def test_basics(self):
894 linecache.clearcache()
895 linecache.lazycache("f", globals())
896 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300897 self.assertEqual(f,
898 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
899 self.assertEqual(tuple(f),
900 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
901 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
902 self.assertEqual(f, tuple(f))
903 # Since tuple.__eq__ doesn't support FrameSummary, the equality
904 # operator fallbacks to FrameSummary.__eq__.
905 self.assertEqual(tuple(f), f)
906 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +0300907 self.assertNotEqual(f, object())
908 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300909
910 def test_lazy_lines(self):
911 linecache.clearcache()
912 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
913 self.assertEqual(None, f._line)
914 linecache.lazycache("f", globals())
915 self.assertEqual(
916 '"""Test cases for traceback module"""',
917 f.line)
918
919 def test_explicit_line(self):
920 f = traceback.FrameSummary("f", 1, "dummy", line="line")
921 self.assertEqual("line", f.line)
922
Berker Peksag9797b7a2018-09-10 20:02:33 +0300923 def test_len(self):
924 f = traceback.FrameSummary("f", 1, "dummy", line="line")
925 self.assertEqual(len(f), 4)
926
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300927
928class TestStack(unittest.TestCase):
929
930 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +0300931 def deeper():
932 return list(traceback.walk_stack(None))
933 s1 = list(traceback.walk_stack(None))
934 s2 = deeper()
935 self.assertEqual(len(s2) - len(s1), 1)
936 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300937
938 def test_walk_tb(self):
939 try:
940 1/0
941 except Exception:
942 _, _, tb = sys.exc_info()
943 s = list(traceback.walk_tb(tb))
944 self.assertEqual(len(s), 1)
945
946 def test_extract_stack(self):
947 s = traceback.StackSummary.extract(traceback.walk_stack(None))
948 self.assertIsInstance(s, traceback.StackSummary)
949
950 def test_extract_stack_limit(self):
951 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
952 self.assertEqual(len(s), 5)
953
954 def test_extract_stack_lookup_lines(self):
955 linecache.clearcache()
956 linecache.updatecache('/foo.py', globals())
957 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300958 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300959 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
960 linecache.clearcache()
961 self.assertEqual(s[0].line, "import sys")
962
963 def test_extract_stackup_deferred_lookup_lines(self):
964 linecache.clearcache()
965 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300966 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300967 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
968 self.assertEqual({}, linecache.cache)
969 linecache.updatecache('/foo.py', globals())
970 self.assertEqual(s[0].line, "import sys")
971
972 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300973 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300974 self.assertEqual(
975 [' File "foo.py", line 1, in fred\n line\n'],
976 s.format())
977
Robert Collinsbbb8ade2015-03-16 15:27:16 +1300978 def test_from_list_edited_stack(self):
979 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
980 s[0] = ('foo.py', 2, 'fred', 'line')
981 s2 = traceback.StackSummary.from_list(s)
982 self.assertEqual(
983 [' File "foo.py", line 2, in fred\n line\n'],
984 s2.format())
985
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300986 def test_format_smoke(self):
987 # For detailed tests see the format_list tests, which consume the same
988 # code.
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300989 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300990 self.assertEqual(
991 [' File "foo.py", line 1, in fred\n line\n'],
992 s.format())
993
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300994 def test_locals(self):
995 linecache.updatecache('/foo.py', globals())
996 c = test_code('/foo.py', 'method')
997 f = test_frame(c, globals(), {'something': 1})
998 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
999 self.assertEqual(s[0].locals, {'something': '1'})
1000
1001 def test_no_locals(self):
1002 linecache.updatecache('/foo.py', globals())
1003 c = test_code('/foo.py', 'method')
1004 f = test_frame(c, globals(), {'something': 1})
1005 s = traceback.StackSummary.extract(iter([(f, 6)]))
1006 self.assertEqual(s[0].locals, None)
1007
1008 def test_format_locals(self):
1009 def some_inner(k, v):
1010 a = 1
1011 b = 2
1012 return traceback.StackSummary.extract(
1013 traceback.walk_stack(None), capture_locals=True, limit=1)
1014 s = some_inner(3, 4)
1015 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +03001016 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001017 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001018 ' a = 1\n'
1019 ' b = 2\n'
1020 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001021 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001022 ], s.format())
1023
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001024class TestTracebackException(unittest.TestCase):
1025
1026 def test_smoke(self):
1027 try:
1028 1/0
1029 except Exception:
1030 exc_info = sys.exc_info()
1031 exc = traceback.TracebackException(*exc_info)
1032 expected_stack = traceback.StackSummary.extract(
1033 traceback.walk_tb(exc_info[2]))
1034 self.assertEqual(None, exc.__cause__)
1035 self.assertEqual(None, exc.__context__)
1036 self.assertEqual(False, exc.__suppress_context__)
1037 self.assertEqual(expected_stack, exc.stack)
1038 self.assertEqual(exc_info[0], exc.exc_type)
1039 self.assertEqual(str(exc_info[1]), str(exc))
1040
1041 def test_from_exception(self):
1042 # Check all the parameters are accepted.
1043 def foo():
1044 1/0
1045 try:
1046 foo()
1047 except Exception as e:
1048 exc_info = sys.exc_info()
1049 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001050 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1051 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001052 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001053 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001054 expected_stack = self.expected_stack
1055 exc = self.exc
1056 self.assertEqual(None, exc.__cause__)
1057 self.assertEqual(None, exc.__context__)
1058 self.assertEqual(False, exc.__suppress_context__)
1059 self.assertEqual(expected_stack, exc.stack)
1060 self.assertEqual(exc_info[0], exc.exc_type)
1061 self.assertEqual(str(exc_info[1]), str(exc))
1062
1063 def test_cause(self):
1064 try:
1065 try:
1066 1/0
1067 finally:
1068 exc_info_context = sys.exc_info()
1069 exc_context = traceback.TracebackException(*exc_info_context)
1070 cause = Exception("cause")
1071 raise Exception("uh oh") from cause
1072 except Exception:
1073 exc_info = sys.exc_info()
1074 exc = traceback.TracebackException(*exc_info)
1075 expected_stack = traceback.StackSummary.extract(
1076 traceback.walk_tb(exc_info[2]))
1077 exc_cause = traceback.TracebackException(Exception, cause, None)
1078 self.assertEqual(exc_cause, exc.__cause__)
1079 self.assertEqual(exc_context, exc.__context__)
1080 self.assertEqual(True, exc.__suppress_context__)
1081 self.assertEqual(expected_stack, exc.stack)
1082 self.assertEqual(exc_info[0], exc.exc_type)
1083 self.assertEqual(str(exc_info[1]), str(exc))
1084
1085 def test_context(self):
1086 try:
1087 try:
1088 1/0
1089 finally:
1090 exc_info_context = sys.exc_info()
1091 exc_context = traceback.TracebackException(*exc_info_context)
1092 raise Exception("uh oh")
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 self.assertEqual(None, exc.__cause__)
1099 self.assertEqual(exc_context, exc.__context__)
1100 self.assertEqual(False, exc.__suppress_context__)
1101 self.assertEqual(expected_stack, exc.stack)
1102 self.assertEqual(exc_info[0], exc.exc_type)
1103 self.assertEqual(str(exc_info[1]), str(exc))
1104
Serhiy Storchaka662db122019-08-08 08:42:54 +03001105 def test_comparison(self):
1106 try:
1107 1/0
1108 except Exception:
1109 exc_info = sys.exc_info()
1110 exc = traceback.TracebackException(*exc_info)
1111 exc2 = traceback.TracebackException(*exc_info)
1112 self.assertIsNot(exc, exc2)
1113 self.assertEqual(exc, exc2)
1114 self.assertNotEqual(exc, object())
1115 self.assertEqual(exc, ALWAYS_EQ)
1116
Zane Bitterde860732017-10-17 17:29:39 -04001117 def test_unhashable(self):
1118 class UnhashableException(Exception):
1119 def __eq__(self, other):
1120 return True
1121
1122 ex1 = UnhashableException('ex1')
1123 ex2 = UnhashableException('ex2')
1124 try:
1125 raise ex2 from ex1
1126 except UnhashableException:
1127 try:
1128 raise ex1
1129 except UnhashableException:
1130 exc_info = sys.exc_info()
1131 exc = traceback.TracebackException(*exc_info)
1132 formatted = list(exc.format())
1133 self.assertIn('UnhashableException: ex2\n', formatted[2])
1134 self.assertIn('UnhashableException: ex1\n', formatted[6])
1135
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001136 def test_limit(self):
1137 def recurse(n):
1138 if n:
1139 recurse(n-1)
1140 else:
1141 1/0
1142 try:
1143 recurse(10)
1144 except Exception:
1145 exc_info = sys.exc_info()
1146 exc = traceback.TracebackException(*exc_info, limit=5)
1147 expected_stack = traceback.StackSummary.extract(
1148 traceback.walk_tb(exc_info[2]), limit=5)
1149 self.assertEqual(expected_stack, exc.stack)
1150
1151 def test_lookup_lines(self):
1152 linecache.clearcache()
1153 e = Exception("uh oh")
1154 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001155 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001156 tb = test_tb(f, 6, None)
1157 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
1158 self.assertEqual({}, linecache.cache)
1159 linecache.updatecache('/foo.py', globals())
1160 self.assertEqual(exc.stack[0].line, "import sys")
1161
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001162 def test_locals(self):
1163 linecache.updatecache('/foo.py', globals())
1164 e = Exception("uh oh")
1165 c = test_code('/foo.py', 'method')
1166 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1167 tb = test_tb(f, 6, None)
1168 exc = traceback.TracebackException(
1169 Exception, e, tb, capture_locals=True)
1170 self.assertEqual(
1171 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1172
1173 def test_no_locals(self):
1174 linecache.updatecache('/foo.py', globals())
1175 e = Exception("uh oh")
1176 c = test_code('/foo.py', 'method')
1177 f = test_frame(c, globals(), {'something': 1})
1178 tb = test_tb(f, 6, None)
1179 exc = traceback.TracebackException(Exception, e, tb)
1180 self.assertEqual(exc.stack[0].locals, None)
1181
Berker Peksagc3f417d2015-07-24 17:36:21 +03001182 def test_traceback_header(self):
1183 # do not print a traceback header if exc_traceback is None
1184 # see issue #24695
1185 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1186 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1187
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001188
Berker Peksag716b3d32015-04-08 09:47:14 +03001189class MiscTest(unittest.TestCase):
1190
1191 def test_all(self):
1192 expected = set()
1193 blacklist = {'print_list'}
1194 for name in dir(traceback):
1195 if name.startswith('_') or name in blacklist:
1196 continue
1197 module_object = getattr(traceback, name)
1198 if getattr(module_object, '__module__', None) == 'traceback':
1199 expected.add(name)
1200 self.assertCountEqual(traceback.__all__, expected)
1201
Fred Drake2e2be372001-09-20 21:33:42 +00001202
1203if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001204 unittest.main()