blob: 7135d997d54ab5c429e6cf6c9916e1bebd761dc0 [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
61 self.assertEqual(err[1].find("+"), 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
67 self.assertEqual(err[1].find("+"), 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 Rossume61fd5b2007-07-11 12:20:59 +000081 self.assertEqual(err[1].find(")"), err[2].find("^"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000082
Florent Xicluna758fa5e2014-01-22 01:11:43 +010083 err = self.get_exception_format(self.syntax_error_bad_indentation2,
84 IndentationError)
85 self.assertEqual(len(err), 4)
86 self.assertEqual(err[1].strip(), "print(2)")
87 self.assertIn("^", err[2])
88 self.assertEqual(err[1].find("p"), err[2].find("^"))
89
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
Serhiy Storchakaa7930372016-07-03 22:27:26 +0300177 @support.requires_type_collecting
Victor Stinner9d279b82014-12-05 10:18:30 +0100178 def test_print_traceback_at_exit(self):
179 # Issue #22599: Ensure that it is possible to use the traceback module
180 # to display an exception at Python exit
181 code = textwrap.dedent("""
182 import sys
183 import traceback
184
185 class PrintExceptionAtExit(object):
186 def __init__(self):
187 try:
188 x = 1 / 0
189 except Exception:
190 self.exc_info = sys.exc_info()
191 # self.exc_info[1] (traceback) contains frames:
192 # explicitly clear the reference to self in the current
193 # frame to break a reference cycle
194 self = None
195
196 def __del__(self):
197 traceback.print_exception(*self.exc_info)
198
199 # Keep a reference in the module namespace to call the destructor
200 # when the module is unloaded
201 obj = PrintExceptionAtExit()
202 """)
203 rc, stdout, stderr = assert_python_ok('-c', code)
204 expected = [b'Traceback (most recent call last):',
205 b' File "<string>", line 8, in __init__',
206 b'ZeroDivisionError: division by zero']
207 self.assertEqual(stderr.splitlines(), expected)
208
Berker Peksagc3f417d2015-07-24 17:36:21 +0300209 def test_print_exception(self):
210 output = StringIO()
211 traceback.print_exception(
212 Exception, Exception("projector"), None, file=output
213 )
214 self.assertEqual(output.getvalue(), "Exception: projector\n")
215
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000216
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000217class TracebackFormatTests(unittest.TestCase):
218
Antoine Pitrou58720d62013-08-05 23:26:40 +0200219 def some_exception(self):
220 raise KeyError('blah')
221
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200222 @cpython_only
Antoine Pitrou58720d62013-08-05 23:26:40 +0200223 def check_traceback_format(self, cleanup_func=None):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200224 from _testcapi import traceback_print
Georg Brandl236f7972009-04-05 14:28:42 +0000225 try:
Antoine Pitrou58720d62013-08-05 23:26:40 +0200226 self.some_exception()
Georg Brandl236f7972009-04-05 14:28:42 +0000227 except KeyError:
228 type_, value, tb = sys.exc_info()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200229 if cleanup_func is not None:
230 # Clear the inner frames, not this one
231 cleanup_func(tb.tb_next)
Georg Brandl236f7972009-04-05 14:28:42 +0000232 traceback_fmt = 'Traceback (most recent call last):\n' + \
233 ''.join(traceback.format_tb(tb))
234 file_ = StringIO()
235 traceback_print(tb, file_)
236 python_fmt = file_.getvalue()
Benjamin Petersond9fec152013-04-29 16:09:39 -0400237 # Call all _tb and _exc functions
238 with captured_output("stderr") as tbstderr:
239 traceback.print_tb(tb)
240 tbfile = StringIO()
241 traceback.print_tb(tb, file=tbfile)
242 with captured_output("stderr") as excstderr:
243 traceback.print_exc()
244 excfmt = traceback.format_exc()
245 excfile = StringIO()
246 traceback.print_exc(file=excfile)
Georg Brandl236f7972009-04-05 14:28:42 +0000247 else:
248 raise Error("unable to create test traceback string")
249
250 # Make sure that Python and the traceback module format the same thing
Ezio Melottib3aedd42010-11-20 19:04:17 +0000251 self.assertEqual(traceback_fmt, python_fmt)
Benjamin Petersond9fec152013-04-29 16:09:39 -0400252 # Now verify the _tb func output
253 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
254 # Now verify the _exc func output
255 self.assertEqual(excstderr.getvalue(), excfile.getvalue())
256 self.assertEqual(excfmt, excfile.getvalue())
Georg Brandl236f7972009-04-05 14:28:42 +0000257
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000258 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000259 tb_lines = python_fmt.splitlines()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200260 self.assertEqual(len(tb_lines), 5)
261 banner = tb_lines[0]
262 location, source_line = tb_lines[-2:]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000263 self.assertTrue(banner.startswith('Traceback'))
264 self.assertTrue(location.startswith(' File'))
265 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000266
Antoine Pitrou58720d62013-08-05 23:26:40 +0200267 def test_traceback_format(self):
268 self.check_traceback_format()
269
270 def test_traceback_format_with_cleared_frames(self):
271 # Check that traceback formatting also works with a clear()ed frame
272 def cleanup_tb(tb):
273 tb.tb_frame.clear()
274 self.check_traceback_format(cleanup_tb)
275
Benjamin Petersond9fec152013-04-29 16:09:39 -0400276 def test_stack_format(self):
277 # Verify _stack functions. Note we have to use _getframe(1) to
278 # compare them without this frame appearing in the output
279 with captured_output("stderr") as ststderr:
280 traceback.print_stack(sys._getframe(1))
281 stfile = StringIO()
282 traceback.print_stack(sys._getframe(1), file=stfile)
283 self.assertEqual(ststderr.getvalue(), stfile.getvalue())
284
285 stfmt = traceback.format_stack(sys._getframe(1))
286
287 self.assertEqual(ststderr.getvalue(), "".join(stfmt))
288
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300289 def test_print_stack(self):
290 def prn():
291 traceback.print_stack()
292 with captured_output("stderr") as stderr:
293 prn()
294 lineno = prn.__code__.co_firstlineno
295 self.assertEqual(stderr.getvalue().splitlines()[-4:], [
296 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3),
297 ' prn()',
298 ' File "%s", line %d, in prn' % (__file__, lineno+1),
299 ' traceback.print_stack()',
300 ])
301
Nick Coghland0034232016-08-15 13:11:34 +1000302 # issue 26823 - Shrink recursive tracebacks
303 def _check_recursive_traceback_display(self, render_exc):
304 # Always show full diffs when this test fails
305 # Note that rearranging things may require adjusting
306 # the relative line numbers in the expected tracebacks
307 self.maxDiff = None
308
309 # Check hitting the recursion limit
310 def f():
311 f()
312
313 with captured_output("stderr") as stderr_f:
314 try:
315 f()
Pablo Galindo293dd232019-11-19 21:34:03 +0000316 except RecursionError:
Nick Coghland0034232016-08-15 13:11:34 +1000317 render_exc()
318 else:
319 self.fail("no recursion occurred")
320
321 lineno_f = f.__code__.co_firstlineno
322 result_f = (
323 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400324 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\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'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400330 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000331 ' f()\n'
332 # XXX: The following line changes depending on whether the tests
333 # are run through the interactive interpreter or with -m
334 # It also varies depending on the platform (stack size)
335 # Fortunately, we don't care about exactness here, so we use regex
336 r' \[Previous line repeated (\d+) more times\]' '\n'
337 'RecursionError: maximum recursion depth exceeded\n'
338 )
339
340 expected = result_f.splitlines()
341 actual = stderr_f.getvalue().splitlines()
342
343 # Check the output text matches expectations
344 # 2nd last line contains the repetition count
345 self.assertEqual(actual[:-2], expected[:-2])
346 self.assertRegex(actual[-2], expected[-2])
codedragon3480ef92017-05-24 14:23:46 -0700347 # last line can have additional text appended
348 self.assertIn(expected[-1], actual[-1])
Nick Coghland0034232016-08-15 13:11:34 +1000349
350 # Check the recursion count is roughly as expected
351 rec_limit = sys.getrecursionlimit()
Serhiy Storchakab7281052016-09-12 00:52:40 +0300352 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
Nick Coghland0034232016-08-15 13:11:34 +1000353
354 # Check a known (limited) number of recursive invocations
355 def g(count=10):
356 if count:
357 return g(count-1)
358 raise ValueError
359
360 with captured_output("stderr") as stderr_g:
361 try:
362 g()
Pablo Galindo293dd232019-11-19 21:34:03 +0000363 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000364 render_exc()
365 else:
366 self.fail("no value error was raised")
367
368 lineno_g = g.__code__.co_firstlineno
369 result_g = (
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'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400374 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000375 ' return g(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700376 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400377 f' File "{__file__}", line {lineno_g+3}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000378 ' raise ValueError\n'
379 'ValueError\n'
380 )
381 tb_line = (
382 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400383 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000384 ' g()\n'
385 )
386 expected = (tb_line + result_g).splitlines()
387 actual = stderr_g.getvalue().splitlines()
388 self.assertEqual(actual, expected)
389
390 # Check 2 different repetitive sections
391 def h(count=10):
392 if count:
393 return h(count-1)
394 g()
395
396 with captured_output("stderr") as stderr_h:
397 try:
398 h()
Pablo Galindo293dd232019-11-19 21:34:03 +0000399 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000400 render_exc()
401 else:
402 self.fail("no value error was raised")
403
404 lineno_h = h.__code__.co_firstlineno
405 result_h = (
406 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400407 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000408 ' h()\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'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400413 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000414 ' return h(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700415 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400416 f' File "{__file__}", line {lineno_h+3}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000417 ' g()\n'
418 )
419 expected = (result_h + result_g).splitlines()
420 actual = stderr_h.getvalue().splitlines()
421 self.assertEqual(actual, expected)
422
Benjamin Petersond5458692018-09-10 08:43:10 -0700423 # Check the boundary conditions. First, test just below the cutoff.
424 with captured_output("stderr") as stderr_g:
425 try:
426 g(traceback._RECURSIVE_CUTOFF)
Pablo Galindo293dd232019-11-19 21:34:03 +0000427 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700428 render_exc()
429 else:
430 self.fail("no error raised")
431 result_g = (
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+2}, in g\n'
437 ' return g(count-1)\n'
438 f' File "{__file__}", line {lineno_g+3}, in g\n'
439 ' raise ValueError\n'
440 'ValueError\n'
441 )
442 tb_line = (
443 'Traceback (most recent call last):\n'
444 f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n'
445 ' g(traceback._RECURSIVE_CUTOFF)\n'
446 )
447 expected = (tb_line + result_g).splitlines()
448 actual = stderr_g.getvalue().splitlines()
449 self.assertEqual(actual, expected)
450
451 # Second, test just above the cutoff.
452 with captured_output("stderr") as stderr_g:
453 try:
454 g(traceback._RECURSIVE_CUTOFF + 1)
Pablo Galindo293dd232019-11-19 21:34:03 +0000455 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700456 render_exc()
457 else:
458 self.fail("no error raised")
459 result_g = (
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 f' File "{__file__}", line {lineno_g+2}, in g\n'
465 ' return g(count-1)\n'
466 ' [Previous line repeated 1 more time]\n'
467 f' File "{__file__}", line {lineno_g+3}, in g\n'
468 ' raise ValueError\n'
469 'ValueError\n'
470 )
471 tb_line = (
472 'Traceback (most recent call last):\n'
473 f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n'
474 ' g(traceback._RECURSIVE_CUTOFF + 1)\n'
475 )
476 expected = (tb_line + result_g).splitlines()
477 actual = stderr_g.getvalue().splitlines()
478 self.assertEqual(actual, expected)
479
Nick Coghland0034232016-08-15 13:11:34 +1000480 def test_recursive_traceback_python(self):
481 self._check_recursive_traceback_display(traceback.print_exc)
482
483 @cpython_only
484 def test_recursive_traceback_cpython_internal(self):
485 from _testcapi import exception_print
486 def render_exc():
487 exc_type, exc_value, exc_tb = sys.exc_info()
488 exception_print(exc_value)
489 self._check_recursive_traceback_display(render_exc)
490
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300491 def test_format_stack(self):
492 def fmt():
493 return traceback.format_stack()
494 result = fmt()
495 lineno = fmt.__code__.co_firstlineno
496 self.assertEqual(result[-2:], [
497 ' File "%s", line %d, in test_format_stack\n'
498 ' result = fmt()\n' % (__file__, lineno+2),
499 ' File "%s", line %d, in fmt\n'
500 ' return traceback.format_stack()\n' % (__file__, lineno+1),
501 ])
502
Zane Bitterde860732017-10-17 17:29:39 -0400503 @cpython_only
504 def test_unhashable(self):
505 from _testcapi import exception_print
506
507 class UnhashableException(Exception):
508 def __eq__(self, other):
509 return True
510
511 ex1 = UnhashableException('ex1')
512 ex2 = UnhashableException('ex2')
513 try:
514 raise ex2 from ex1
515 except UnhashableException:
516 try:
517 raise ex1
518 except UnhashableException:
519 exc_type, exc_val, exc_tb = sys.exc_info()
520
521 with captured_output("stderr") as stderr_f:
522 exception_print(exc_val)
523
524 tb = stderr_f.getvalue().strip().splitlines()
525 self.assertEqual(11, len(tb))
526 self.assertEqual(context_message.strip(), tb[5])
527 self.assertIn('UnhashableException: ex2', tb[3])
528 self.assertIn('UnhashableException: ex1', tb[10])
529
Benjamin Petersone6528212008-07-15 15:32:09 +0000530
531cause_message = (
532 "\nThe above exception was the direct cause "
533 "of the following exception:\n\n")
534
535context_message = (
536 "\nDuring handling of the above exception, "
537 "another exception occurred:\n\n")
538
539boundaries = re.compile(
540 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
541
542
543class BaseExceptionReportingTests:
544
545 def get_exception(self, exception_or_callable):
546 if isinstance(exception_or_callable, Exception):
547 return exception_or_callable
548 try:
549 exception_or_callable()
550 except Exception as e:
551 return e
552
553 def zero_div(self):
554 1/0 # In zero_div
555
556 def check_zero_div(self, msg):
557 lines = msg.splitlines()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000558 self.assertTrue(lines[-3].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000559 self.assertIn('1/0 # In zero_div', lines[-2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000560 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000561
562 def test_simple(self):
563 try:
564 1/0 # Marker
565 except ZeroDivisionError as _:
566 e = _
567 lines = self.get_report(e).splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000568 self.assertEqual(len(lines), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000569 self.assertTrue(lines[0].startswith('Traceback'))
570 self.assertTrue(lines[1].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000571 self.assertIn('1/0 # Marker', lines[2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000572 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000573
574 def test_cause(self):
575 def inner_raise():
576 try:
577 self.zero_div()
578 except ZeroDivisionError as e:
579 raise KeyError from e
580 def outer_raise():
581 inner_raise() # Marker
582 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000583 self.assertEqual(len(blocks), 3)
584 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000585 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000586 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000587
588 def test_context(self):
589 def inner_raise():
590 try:
591 self.zero_div()
592 except ZeroDivisionError:
593 raise KeyError
594 def outer_raise():
595 inner_raise() # Marker
596 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000597 self.assertEqual(len(blocks), 3)
598 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000599 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000600 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000601
Nick Coghlanab7bf212012-02-26 17:49:52 +1000602 def test_context_suppression(self):
603 try:
604 try:
605 raise Exception
606 except:
607 raise ZeroDivisionError from None
608 except ZeroDivisionError as _:
609 e = _
610 lines = self.get_report(e).splitlines()
611 self.assertEqual(len(lines), 4)
612 self.assertTrue(lines[0].startswith('Traceback'))
613 self.assertTrue(lines[1].startswith(' File'))
614 self.assertIn('ZeroDivisionError from None', lines[2])
615 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
616
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000617 def test_cause_and_context(self):
618 # When both a cause and a context are set, only the cause should be
619 # displayed and the context should be muted.
620 def inner_raise():
621 try:
622 self.zero_div()
623 except ZeroDivisionError as _e:
624 e = _e
625 try:
626 xyzzy
627 except NameError:
628 raise KeyError from e
629 def outer_raise():
630 inner_raise() # Marker
631 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000632 self.assertEqual(len(blocks), 3)
633 self.assertEqual(blocks[1], cause_message)
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000634 self.check_zero_div(blocks[0])
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000635 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000636
Benjamin Petersone6528212008-07-15 15:32:09 +0000637 def test_cause_recursive(self):
638 def inner_raise():
639 try:
640 try:
641 self.zero_div()
642 except ZeroDivisionError as e:
643 z = e
644 raise KeyError from e
645 except KeyError as e:
646 raise z from e
647 def outer_raise():
648 inner_raise() # Marker
649 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000650 self.assertEqual(len(blocks), 3)
651 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000652 # The first block is the KeyError raised from the ZeroDivisionError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000653 self.assertIn('raise KeyError from e', blocks[0])
654 self.assertNotIn('1/0', blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000655 # The second block (apart from the boundary) is the ZeroDivisionError
656 # re-raised from the KeyError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000657 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000658 self.check_zero_div(blocks[2])
659
Benjamin Peterson503d6c52010-10-24 02:52:05 +0000660 def test_syntax_error_offset_at_eol(self):
661 # See #10186.
662 def e():
663 raise SyntaxError('', ('', 0, 5, 'hello'))
664 msg = self.get_report(e).splitlines()
665 self.assertEqual(msg[-2], " ^")
Benjamin Petersona95e9772010-10-29 03:28:14 +0000666 def e():
667 exec("x = 5 | 4 |")
668 msg = self.get_report(e).splitlines()
669 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000670
Martin Panterbb8b1cb2016-09-22 09:37:39 +0000671 def test_message_none(self):
672 # A message that looks like "None" should not be treated specially
673 err = self.get_report(Exception(None))
674 self.assertIn('Exception: None\n', err)
675 err = self.get_report(Exception('None'))
676 self.assertIn('Exception: None\n', err)
677 err = self.get_report(Exception())
678 self.assertIn('Exception\n', err)
679 err = self.get_report(Exception(''))
680 self.assertIn('Exception\n', err)
681
Benjamin Petersone6528212008-07-15 15:32:09 +0000682
683class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
684 #
685 # This checks reporting through the 'traceback' module, with both
686 # format_exception() and print_exception().
687 #
688
689 def get_report(self, e):
690 e = self.get_exception(e)
691 s = ''.join(
692 traceback.format_exception(type(e), e, e.__traceback__))
693 with captured_output("stderr") as sio:
694 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000695 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000696 return s
697
698
699class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
700 #
701 # This checks built-in reporting by the interpreter.
702 #
703
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200704 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000705 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200706 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000707 e = self.get_exception(e)
708 with captured_output("stderr") as s:
709 exception_print(e)
710 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000711
712
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300713class LimitTests(unittest.TestCase):
714
715 ''' Tests for limit argument.
716 It's enough to test extact_tb, extract_stack and format_exception '''
717
718 def last_raises1(self):
719 raise Exception('Last raised')
720
721 def last_raises2(self):
722 self.last_raises1()
723
724 def last_raises3(self):
725 self.last_raises2()
726
727 def last_raises4(self):
728 self.last_raises3()
729
730 def last_raises5(self):
731 self.last_raises4()
732
733 def last_returns_frame1(self):
734 return sys._getframe()
735
736 def last_returns_frame2(self):
737 return self.last_returns_frame1()
738
739 def last_returns_frame3(self):
740 return self.last_returns_frame2()
741
742 def last_returns_frame4(self):
743 return self.last_returns_frame3()
744
745 def last_returns_frame5(self):
746 return self.last_returns_frame4()
747
748 def test_extract_stack(self):
749 frame = self.last_returns_frame5()
750 def extract(**kwargs):
751 return traceback.extract_stack(frame, **kwargs)
752 def assertEqualExcept(actual, expected, ignore):
753 self.assertEqual(actual[:ignore], expected[:ignore])
754 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
755 self.assertEqual(len(actual), len(expected))
756
757 with support.swap_attr(sys, 'tracebacklimit', 1000):
758 nolim = extract()
759 self.assertGreater(len(nolim), 5)
760 self.assertEqual(extract(limit=2), nolim[-2:])
761 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
762 self.assertEqual(extract(limit=-2), nolim[:2])
763 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
764 self.assertEqual(extract(limit=0), [])
765 del sys.tracebacklimit
766 assertEqualExcept(extract(), nolim, -5-1)
767 sys.tracebacklimit = 2
768 self.assertEqual(extract(), nolim[-2:])
769 self.assertEqual(extract(limit=3), nolim[-3:])
770 self.assertEqual(extract(limit=-3), nolim[:3])
771 sys.tracebacklimit = 0
772 self.assertEqual(extract(), [])
773 sys.tracebacklimit = -1
774 self.assertEqual(extract(), [])
775
776 def test_extract_tb(self):
777 try:
778 self.last_raises5()
779 except Exception:
780 exc_type, exc_value, tb = sys.exc_info()
781 def extract(**kwargs):
782 return traceback.extract_tb(tb, **kwargs)
783
784 with support.swap_attr(sys, 'tracebacklimit', 1000):
785 nolim = extract()
786 self.assertEqual(len(nolim), 5+1)
787 self.assertEqual(extract(limit=2), nolim[:2])
788 self.assertEqual(extract(limit=10), nolim)
789 self.assertEqual(extract(limit=-2), nolim[-2:])
790 self.assertEqual(extract(limit=-10), nolim)
791 self.assertEqual(extract(limit=0), [])
792 del sys.tracebacklimit
793 self.assertEqual(extract(), nolim)
794 sys.tracebacklimit = 2
795 self.assertEqual(extract(), nolim[:2])
796 self.assertEqual(extract(limit=3), nolim[:3])
797 self.assertEqual(extract(limit=-3), nolim[-3:])
798 sys.tracebacklimit = 0
799 self.assertEqual(extract(), [])
800 sys.tracebacklimit = -1
801 self.assertEqual(extract(), [])
802
803 def test_format_exception(self):
804 try:
805 self.last_raises5()
806 except Exception:
807 exc_type, exc_value, tb = sys.exc_info()
808 # [1:-1] to exclude "Traceback (...)" header and
809 # exception type and value
810 def extract(**kwargs):
811 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
812
813 with support.swap_attr(sys, 'tracebacklimit', 1000):
814 nolim = extract()
815 self.assertEqual(len(nolim), 5+1)
816 self.assertEqual(extract(limit=2), nolim[:2])
817 self.assertEqual(extract(limit=10), nolim)
818 self.assertEqual(extract(limit=-2), nolim[-2:])
819 self.assertEqual(extract(limit=-10), nolim)
820 self.assertEqual(extract(limit=0), [])
821 del sys.tracebacklimit
822 self.assertEqual(extract(), nolim)
823 sys.tracebacklimit = 2
824 self.assertEqual(extract(), nolim[:2])
825 self.assertEqual(extract(limit=3), nolim[:3])
826 self.assertEqual(extract(limit=-3), nolim[-3:])
827 sys.tracebacklimit = 0
828 self.assertEqual(extract(), [])
829 sys.tracebacklimit = -1
830 self.assertEqual(extract(), [])
831
832
Andrew Kuchling173a1572013-09-15 18:15:56 -0400833class MiscTracebackCases(unittest.TestCase):
834 #
835 # Check non-printing functions in traceback module
836 #
837
838 def test_clear(self):
839 def outer():
840 middle()
841 def middle():
842 inner()
843 def inner():
844 i = 1
845 1/0
846
847 try:
848 outer()
849 except:
850 type_, value, tb = sys.exc_info()
851
852 # Initial assertion: there's one local in the inner frame.
853 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
854 self.assertEqual(len(inner_frame.f_locals), 1)
855
856 # Clear traceback frames
857 traceback.clear_frames(tb)
858
859 # Local variable dict should now be empty.
860 self.assertEqual(len(inner_frame.f_locals), 0)
861
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300862 def test_extract_stack(self):
863 def extract():
864 return traceback.extract_stack()
865 result = extract()
866 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300867 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300868 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
869 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
870 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300871 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300872
Andrew Kuchling173a1572013-09-15 18:15:56 -0400873
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300874class TestFrame(unittest.TestCase):
875
876 def test_basics(self):
877 linecache.clearcache()
878 linecache.lazycache("f", globals())
879 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300880 self.assertEqual(f,
881 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
882 self.assertEqual(tuple(f),
883 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
884 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
885 self.assertEqual(f, tuple(f))
886 # Since tuple.__eq__ doesn't support FrameSummary, the equality
887 # operator fallbacks to FrameSummary.__eq__.
888 self.assertEqual(tuple(f), f)
889 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +0300890 self.assertNotEqual(f, object())
891 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300892
893 def test_lazy_lines(self):
894 linecache.clearcache()
895 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
896 self.assertEqual(None, f._line)
897 linecache.lazycache("f", globals())
898 self.assertEqual(
899 '"""Test cases for traceback module"""',
900 f.line)
901
902 def test_explicit_line(self):
903 f = traceback.FrameSummary("f", 1, "dummy", line="line")
904 self.assertEqual("line", f.line)
905
Berker Peksag9797b7a2018-09-10 20:02:33 +0300906 def test_len(self):
907 f = traceback.FrameSummary("f", 1, "dummy", line="line")
908 self.assertEqual(len(f), 4)
909
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300910
911class TestStack(unittest.TestCase):
912
913 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +0300914 def deeper():
915 return list(traceback.walk_stack(None))
916 s1 = list(traceback.walk_stack(None))
917 s2 = deeper()
918 self.assertEqual(len(s2) - len(s1), 1)
919 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300920
921 def test_walk_tb(self):
922 try:
923 1/0
924 except Exception:
925 _, _, tb = sys.exc_info()
926 s = list(traceback.walk_tb(tb))
927 self.assertEqual(len(s), 1)
928
929 def test_extract_stack(self):
930 s = traceback.StackSummary.extract(traceback.walk_stack(None))
931 self.assertIsInstance(s, traceback.StackSummary)
932
933 def test_extract_stack_limit(self):
934 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
935 self.assertEqual(len(s), 5)
936
937 def test_extract_stack_lookup_lines(self):
938 linecache.clearcache()
939 linecache.updatecache('/foo.py', globals())
940 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300941 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300942 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
943 linecache.clearcache()
944 self.assertEqual(s[0].line, "import sys")
945
946 def test_extract_stackup_deferred_lookup_lines(self):
947 linecache.clearcache()
948 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300949 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300950 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
951 self.assertEqual({}, linecache.cache)
952 linecache.updatecache('/foo.py', globals())
953 self.assertEqual(s[0].line, "import sys")
954
955 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300956 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300957 self.assertEqual(
958 [' File "foo.py", line 1, in fred\n line\n'],
959 s.format())
960
Robert Collinsbbb8ade2015-03-16 15:27:16 +1300961 def test_from_list_edited_stack(self):
962 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
963 s[0] = ('foo.py', 2, 'fred', 'line')
964 s2 = traceback.StackSummary.from_list(s)
965 self.assertEqual(
966 [' File "foo.py", line 2, in fred\n line\n'],
967 s2.format())
968
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300969 def test_format_smoke(self):
970 # For detailed tests see the format_list tests, which consume the same
971 # code.
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300972 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300973 self.assertEqual(
974 [' File "foo.py", line 1, in fred\n line\n'],
975 s.format())
976
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300977 def test_locals(self):
978 linecache.updatecache('/foo.py', globals())
979 c = test_code('/foo.py', 'method')
980 f = test_frame(c, globals(), {'something': 1})
981 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
982 self.assertEqual(s[0].locals, {'something': '1'})
983
984 def test_no_locals(self):
985 linecache.updatecache('/foo.py', globals())
986 c = test_code('/foo.py', 'method')
987 f = test_frame(c, globals(), {'something': 1})
988 s = traceback.StackSummary.extract(iter([(f, 6)]))
989 self.assertEqual(s[0].locals, None)
990
991 def test_format_locals(self):
992 def some_inner(k, v):
993 a = 1
994 b = 2
995 return traceback.StackSummary.extract(
996 traceback.walk_stack(None), capture_locals=True, limit=1)
997 s = some_inner(3, 4)
998 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300999 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001000 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001001 ' a = 1\n'
1002 ' b = 2\n'
1003 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001004 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001005 ], s.format())
1006
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001007class TestTracebackException(unittest.TestCase):
1008
1009 def test_smoke(self):
1010 try:
1011 1/0
1012 except Exception:
1013 exc_info = sys.exc_info()
1014 exc = traceback.TracebackException(*exc_info)
1015 expected_stack = traceback.StackSummary.extract(
1016 traceback.walk_tb(exc_info[2]))
1017 self.assertEqual(None, exc.__cause__)
1018 self.assertEqual(None, exc.__context__)
1019 self.assertEqual(False, exc.__suppress_context__)
1020 self.assertEqual(expected_stack, exc.stack)
1021 self.assertEqual(exc_info[0], exc.exc_type)
1022 self.assertEqual(str(exc_info[1]), str(exc))
1023
1024 def test_from_exception(self):
1025 # Check all the parameters are accepted.
1026 def foo():
1027 1/0
1028 try:
1029 foo()
1030 except Exception as e:
1031 exc_info = sys.exc_info()
1032 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001033 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1034 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001035 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001036 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001037 expected_stack = self.expected_stack
1038 exc = self.exc
1039 self.assertEqual(None, exc.__cause__)
1040 self.assertEqual(None, exc.__context__)
1041 self.assertEqual(False, exc.__suppress_context__)
1042 self.assertEqual(expected_stack, exc.stack)
1043 self.assertEqual(exc_info[0], exc.exc_type)
1044 self.assertEqual(str(exc_info[1]), str(exc))
1045
1046 def test_cause(self):
1047 try:
1048 try:
1049 1/0
1050 finally:
1051 exc_info_context = sys.exc_info()
1052 exc_context = traceback.TracebackException(*exc_info_context)
1053 cause = Exception("cause")
1054 raise Exception("uh oh") from cause
1055 except Exception:
1056 exc_info = sys.exc_info()
1057 exc = traceback.TracebackException(*exc_info)
1058 expected_stack = traceback.StackSummary.extract(
1059 traceback.walk_tb(exc_info[2]))
1060 exc_cause = traceback.TracebackException(Exception, cause, None)
1061 self.assertEqual(exc_cause, exc.__cause__)
1062 self.assertEqual(exc_context, exc.__context__)
1063 self.assertEqual(True, exc.__suppress_context__)
1064 self.assertEqual(expected_stack, exc.stack)
1065 self.assertEqual(exc_info[0], exc.exc_type)
1066 self.assertEqual(str(exc_info[1]), str(exc))
1067
1068 def test_context(self):
1069 try:
1070 try:
1071 1/0
1072 finally:
1073 exc_info_context = sys.exc_info()
1074 exc_context = traceback.TracebackException(*exc_info_context)
1075 raise Exception("uh oh")
1076 except Exception:
1077 exc_info = sys.exc_info()
1078 exc = traceback.TracebackException(*exc_info)
1079 expected_stack = traceback.StackSummary.extract(
1080 traceback.walk_tb(exc_info[2]))
1081 self.assertEqual(None, exc.__cause__)
1082 self.assertEqual(exc_context, exc.__context__)
1083 self.assertEqual(False, exc.__suppress_context__)
1084 self.assertEqual(expected_stack, exc.stack)
1085 self.assertEqual(exc_info[0], exc.exc_type)
1086 self.assertEqual(str(exc_info[1]), str(exc))
1087
Serhiy Storchaka662db122019-08-08 08:42:54 +03001088 def test_comparison(self):
1089 try:
1090 1/0
1091 except Exception:
1092 exc_info = sys.exc_info()
1093 exc = traceback.TracebackException(*exc_info)
1094 exc2 = traceback.TracebackException(*exc_info)
1095 self.assertIsNot(exc, exc2)
1096 self.assertEqual(exc, exc2)
1097 self.assertNotEqual(exc, object())
1098 self.assertEqual(exc, ALWAYS_EQ)
1099
Zane Bitterde860732017-10-17 17:29:39 -04001100 def test_unhashable(self):
1101 class UnhashableException(Exception):
1102 def __eq__(self, other):
1103 return True
1104
1105 ex1 = UnhashableException('ex1')
1106 ex2 = UnhashableException('ex2')
1107 try:
1108 raise ex2 from ex1
1109 except UnhashableException:
1110 try:
1111 raise ex1
1112 except UnhashableException:
1113 exc_info = sys.exc_info()
1114 exc = traceback.TracebackException(*exc_info)
1115 formatted = list(exc.format())
1116 self.assertIn('UnhashableException: ex2\n', formatted[2])
1117 self.assertIn('UnhashableException: ex1\n', formatted[6])
1118
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001119 def test_limit(self):
1120 def recurse(n):
1121 if n:
1122 recurse(n-1)
1123 else:
1124 1/0
1125 try:
1126 recurse(10)
1127 except Exception:
1128 exc_info = sys.exc_info()
1129 exc = traceback.TracebackException(*exc_info, limit=5)
1130 expected_stack = traceback.StackSummary.extract(
1131 traceback.walk_tb(exc_info[2]), limit=5)
1132 self.assertEqual(expected_stack, exc.stack)
1133
1134 def test_lookup_lines(self):
1135 linecache.clearcache()
1136 e = Exception("uh oh")
1137 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001138 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001139 tb = test_tb(f, 6, None)
1140 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
1141 self.assertEqual({}, linecache.cache)
1142 linecache.updatecache('/foo.py', globals())
1143 self.assertEqual(exc.stack[0].line, "import sys")
1144
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001145 def test_locals(self):
1146 linecache.updatecache('/foo.py', globals())
1147 e = Exception("uh oh")
1148 c = test_code('/foo.py', 'method')
1149 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1150 tb = test_tb(f, 6, None)
1151 exc = traceback.TracebackException(
1152 Exception, e, tb, capture_locals=True)
1153 self.assertEqual(
1154 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1155
1156 def test_no_locals(self):
1157 linecache.updatecache('/foo.py', globals())
1158 e = Exception("uh oh")
1159 c = test_code('/foo.py', 'method')
1160 f = test_frame(c, globals(), {'something': 1})
1161 tb = test_tb(f, 6, None)
1162 exc = traceback.TracebackException(Exception, e, tb)
1163 self.assertEqual(exc.stack[0].locals, None)
1164
Berker Peksagc3f417d2015-07-24 17:36:21 +03001165 def test_traceback_header(self):
1166 # do not print a traceback header if exc_traceback is None
1167 # see issue #24695
1168 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1169 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1170
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001171
Berker Peksag716b3d32015-04-08 09:47:14 +03001172class MiscTest(unittest.TestCase):
1173
1174 def test_all(self):
1175 expected = set()
1176 blacklist = {'print_list'}
1177 for name in dir(traceback):
1178 if name.startswith('_') or name in blacklist:
1179 continue
1180 module_object = getattr(traceback, name)
1181 if getattr(module_object, '__module__', None) == 'traceback':
1182 expected.add(name)
1183 self.assertCountEqual(traceback.__all__, expected)
1184
Fred Drake2e2be372001-09-20 21:33:42 +00001185
1186if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001187 unittest.main()