blob: 45f55e1f8ab6c5a4fccd2d75781e6fe5fb02cfe2 [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
Victor Stinner9d279b82014-12-05 10:18:30 +0100177 def test_print_traceback_at_exit(self):
178 # Issue #22599: Ensure that it is possible to use the traceback module
179 # to display an exception at Python exit
180 code = textwrap.dedent("""
181 import sys
182 import traceback
183
184 class PrintExceptionAtExit(object):
185 def __init__(self):
186 try:
187 x = 1 / 0
188 except Exception:
189 self.exc_info = sys.exc_info()
190 # self.exc_info[1] (traceback) contains frames:
191 # explicitly clear the reference to self in the current
192 # frame to break a reference cycle
193 self = None
194
195 def __del__(self):
196 traceback.print_exception(*self.exc_info)
197
198 # Keep a reference in the module namespace to call the destructor
199 # when the module is unloaded
200 obj = PrintExceptionAtExit()
201 """)
202 rc, stdout, stderr = assert_python_ok('-c', code)
203 expected = [b'Traceback (most recent call last):',
204 b' File "<string>", line 8, in __init__',
205 b'ZeroDivisionError: division by zero']
206 self.assertEqual(stderr.splitlines(), expected)
207
Berker Peksagc3f417d2015-07-24 17:36:21 +0300208 def test_print_exception(self):
209 output = StringIO()
210 traceback.print_exception(
211 Exception, Exception("projector"), None, file=output
212 )
213 self.assertEqual(output.getvalue(), "Exception: projector\n")
214
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000215
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000216class TracebackFormatTests(unittest.TestCase):
217
Antoine Pitrou58720d62013-08-05 23:26:40 +0200218 def some_exception(self):
219 raise KeyError('blah')
220
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200221 @cpython_only
Antoine Pitrou58720d62013-08-05 23:26:40 +0200222 def check_traceback_format(self, cleanup_func=None):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200223 from _testcapi import traceback_print
Georg Brandl236f7972009-04-05 14:28:42 +0000224 try:
Antoine Pitrou58720d62013-08-05 23:26:40 +0200225 self.some_exception()
Georg Brandl236f7972009-04-05 14:28:42 +0000226 except KeyError:
227 type_, value, tb = sys.exc_info()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200228 if cleanup_func is not None:
229 # Clear the inner frames, not this one
230 cleanup_func(tb.tb_next)
Georg Brandl236f7972009-04-05 14:28:42 +0000231 traceback_fmt = 'Traceback (most recent call last):\n' + \
232 ''.join(traceback.format_tb(tb))
233 file_ = StringIO()
234 traceback_print(tb, file_)
235 python_fmt = file_.getvalue()
Benjamin Petersond9fec152013-04-29 16:09:39 -0400236 # Call all _tb and _exc functions
237 with captured_output("stderr") as tbstderr:
238 traceback.print_tb(tb)
239 tbfile = StringIO()
240 traceback.print_tb(tb, file=tbfile)
241 with captured_output("stderr") as excstderr:
242 traceback.print_exc()
243 excfmt = traceback.format_exc()
244 excfile = StringIO()
245 traceback.print_exc(file=excfile)
Georg Brandl236f7972009-04-05 14:28:42 +0000246 else:
247 raise Error("unable to create test traceback string")
248
249 # Make sure that Python and the traceback module format the same thing
Ezio Melottib3aedd42010-11-20 19:04:17 +0000250 self.assertEqual(traceback_fmt, python_fmt)
Benjamin Petersond9fec152013-04-29 16:09:39 -0400251 # Now verify the _tb func output
252 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
253 # Now verify the _exc func output
254 self.assertEqual(excstderr.getvalue(), excfile.getvalue())
255 self.assertEqual(excfmt, excfile.getvalue())
Georg Brandl236f7972009-04-05 14:28:42 +0000256
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000257 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000258 tb_lines = python_fmt.splitlines()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200259 self.assertEqual(len(tb_lines), 5)
260 banner = tb_lines[0]
261 location, source_line = tb_lines[-2:]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000262 self.assertTrue(banner.startswith('Traceback'))
263 self.assertTrue(location.startswith(' File'))
264 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000265
Antoine Pitrou58720d62013-08-05 23:26:40 +0200266 def test_traceback_format(self):
267 self.check_traceback_format()
268
269 def test_traceback_format_with_cleared_frames(self):
270 # Check that traceback formatting also works with a clear()ed frame
271 def cleanup_tb(tb):
272 tb.tb_frame.clear()
273 self.check_traceback_format(cleanup_tb)
274
Benjamin Petersond9fec152013-04-29 16:09:39 -0400275 def test_stack_format(self):
276 # Verify _stack functions. Note we have to use _getframe(1) to
277 # compare them without this frame appearing in the output
278 with captured_output("stderr") as ststderr:
279 traceback.print_stack(sys._getframe(1))
280 stfile = StringIO()
281 traceback.print_stack(sys._getframe(1), file=stfile)
282 self.assertEqual(ststderr.getvalue(), stfile.getvalue())
283
284 stfmt = traceback.format_stack(sys._getframe(1))
285
286 self.assertEqual(ststderr.getvalue(), "".join(stfmt))
287
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300288 def test_print_stack(self):
289 def prn():
290 traceback.print_stack()
291 with captured_output("stderr") as stderr:
292 prn()
293 lineno = prn.__code__.co_firstlineno
294 self.assertEqual(stderr.getvalue().splitlines()[-4:], [
295 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3),
296 ' prn()',
297 ' File "%s", line %d, in prn' % (__file__, lineno+1),
298 ' traceback.print_stack()',
299 ])
300
Nick Coghland0034232016-08-15 13:11:34 +1000301 # issue 26823 - Shrink recursive tracebacks
302 def _check_recursive_traceback_display(self, render_exc):
303 # Always show full diffs when this test fails
304 # Note that rearranging things may require adjusting
305 # the relative line numbers in the expected tracebacks
306 self.maxDiff = None
307
308 # Check hitting the recursion limit
309 def f():
310 f()
311
312 with captured_output("stderr") as stderr_f:
313 try:
314 f()
Pablo Galindo293dd232019-11-19 21:34:03 +0000315 except RecursionError:
Nick Coghland0034232016-08-15 13:11:34 +1000316 render_exc()
317 else:
318 self.fail("no recursion occurred")
319
320 lineno_f = f.__code__.co_firstlineno
321 result_f = (
322 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400323 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000324 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400325 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000326 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400327 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000328 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400329 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000330 ' f()\n'
331 # XXX: The following line changes depending on whether the tests
332 # are run through the interactive interpreter or with -m
333 # It also varies depending on the platform (stack size)
334 # Fortunately, we don't care about exactness here, so we use regex
335 r' \[Previous line repeated (\d+) more times\]' '\n'
336 'RecursionError: maximum recursion depth exceeded\n'
337 )
338
339 expected = result_f.splitlines()
340 actual = stderr_f.getvalue().splitlines()
341
342 # Check the output text matches expectations
343 # 2nd last line contains the repetition count
344 self.assertEqual(actual[:-2], expected[:-2])
345 self.assertRegex(actual[-2], expected[-2])
codedragon3480ef92017-05-24 14:23:46 -0700346 # last line can have additional text appended
347 self.assertIn(expected[-1], actual[-1])
Nick Coghland0034232016-08-15 13:11:34 +1000348
349 # Check the recursion count is roughly as expected
350 rec_limit = sys.getrecursionlimit()
Serhiy Storchakab7281052016-09-12 00:52:40 +0300351 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
Nick Coghland0034232016-08-15 13:11:34 +1000352
353 # Check a known (limited) number of recursive invocations
354 def g(count=10):
355 if count:
356 return g(count-1)
357 raise ValueError
358
359 with captured_output("stderr") as stderr_g:
360 try:
361 g()
Pablo Galindo293dd232019-11-19 21:34:03 +0000362 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000363 render_exc()
364 else:
365 self.fail("no value error was raised")
366
367 lineno_g = g.__code__.co_firstlineno
368 result_g = (
Eric V. Smith451d0e32016-09-09 21:56:20 -0400369 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000370 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400371 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000372 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400373 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000374 ' return g(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700375 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400376 f' File "{__file__}", line {lineno_g+3}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000377 ' raise ValueError\n'
378 'ValueError\n'
379 )
380 tb_line = (
381 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400382 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000383 ' g()\n'
384 )
385 expected = (tb_line + result_g).splitlines()
386 actual = stderr_g.getvalue().splitlines()
387 self.assertEqual(actual, expected)
388
389 # Check 2 different repetitive sections
390 def h(count=10):
391 if count:
392 return h(count-1)
393 g()
394
395 with captured_output("stderr") as stderr_h:
396 try:
397 h()
Pablo Galindo293dd232019-11-19 21:34:03 +0000398 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000399 render_exc()
400 else:
401 self.fail("no value error was raised")
402
403 lineno_h = h.__code__.co_firstlineno
404 result_h = (
405 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400406 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000407 ' h()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400408 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000409 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400410 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000411 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400412 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000413 ' return h(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700414 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400415 f' File "{__file__}", line {lineno_h+3}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000416 ' g()\n'
417 )
418 expected = (result_h + result_g).splitlines()
419 actual = stderr_h.getvalue().splitlines()
420 self.assertEqual(actual, expected)
421
Benjamin Petersond5458692018-09-10 08:43:10 -0700422 # Check the boundary conditions. First, test just below the cutoff.
423 with captured_output("stderr") as stderr_g:
424 try:
425 g(traceback._RECURSIVE_CUTOFF)
Pablo Galindo293dd232019-11-19 21:34:03 +0000426 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700427 render_exc()
428 else:
429 self.fail("no error raised")
430 result_g = (
431 f' File "{__file__}", line {lineno_g+2}, in g\n'
432 ' return g(count-1)\n'
433 f' File "{__file__}", line {lineno_g+2}, in g\n'
434 ' return g(count-1)\n'
435 f' File "{__file__}", line {lineno_g+2}, in g\n'
436 ' return g(count-1)\n'
437 f' File "{__file__}", line {lineno_g+3}, in g\n'
438 ' raise ValueError\n'
439 'ValueError\n'
440 )
441 tb_line = (
442 'Traceback (most recent call last):\n'
443 f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n'
444 ' g(traceback._RECURSIVE_CUTOFF)\n'
445 )
446 expected = (tb_line + result_g).splitlines()
447 actual = stderr_g.getvalue().splitlines()
448 self.assertEqual(actual, expected)
449
450 # Second, test just above the cutoff.
451 with captured_output("stderr") as stderr_g:
452 try:
453 g(traceback._RECURSIVE_CUTOFF + 1)
Pablo Galindo293dd232019-11-19 21:34:03 +0000454 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700455 render_exc()
456 else:
457 self.fail("no error raised")
458 result_g = (
459 f' File "{__file__}", line {lineno_g+2}, in g\n'
460 ' return g(count-1)\n'
461 f' File "{__file__}", line {lineno_g+2}, in g\n'
462 ' return g(count-1)\n'
463 f' File "{__file__}", line {lineno_g+2}, in g\n'
464 ' return g(count-1)\n'
465 ' [Previous line repeated 1 more time]\n'
466 f' File "{__file__}", line {lineno_g+3}, in g\n'
467 ' raise ValueError\n'
468 'ValueError\n'
469 )
470 tb_line = (
471 'Traceback (most recent call last):\n'
472 f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n'
473 ' g(traceback._RECURSIVE_CUTOFF + 1)\n'
474 )
475 expected = (tb_line + result_g).splitlines()
476 actual = stderr_g.getvalue().splitlines()
477 self.assertEqual(actual, expected)
478
Nick Coghland0034232016-08-15 13:11:34 +1000479 def test_recursive_traceback_python(self):
480 self._check_recursive_traceback_display(traceback.print_exc)
481
482 @cpython_only
483 def test_recursive_traceback_cpython_internal(self):
484 from _testcapi import exception_print
485 def render_exc():
486 exc_type, exc_value, exc_tb = sys.exc_info()
487 exception_print(exc_value)
488 self._check_recursive_traceback_display(render_exc)
489
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300490 def test_format_stack(self):
491 def fmt():
492 return traceback.format_stack()
493 result = fmt()
494 lineno = fmt.__code__.co_firstlineno
495 self.assertEqual(result[-2:], [
496 ' File "%s", line %d, in test_format_stack\n'
497 ' result = fmt()\n' % (__file__, lineno+2),
498 ' File "%s", line %d, in fmt\n'
499 ' return traceback.format_stack()\n' % (__file__, lineno+1),
500 ])
501
Zane Bitterde860732017-10-17 17:29:39 -0400502 @cpython_only
503 def test_unhashable(self):
504 from _testcapi import exception_print
505
506 class UnhashableException(Exception):
507 def __eq__(self, other):
508 return True
509
510 ex1 = UnhashableException('ex1')
511 ex2 = UnhashableException('ex2')
512 try:
513 raise ex2 from ex1
514 except UnhashableException:
515 try:
516 raise ex1
517 except UnhashableException:
518 exc_type, exc_val, exc_tb = sys.exc_info()
519
520 with captured_output("stderr") as stderr_f:
521 exception_print(exc_val)
522
523 tb = stderr_f.getvalue().strip().splitlines()
524 self.assertEqual(11, len(tb))
525 self.assertEqual(context_message.strip(), tb[5])
526 self.assertIn('UnhashableException: ex2', tb[3])
527 self.assertIn('UnhashableException: ex1', tb[10])
528
Benjamin Petersone6528212008-07-15 15:32:09 +0000529
530cause_message = (
531 "\nThe above exception was the direct cause "
532 "of the following exception:\n\n")
533
534context_message = (
535 "\nDuring handling of the above exception, "
536 "another exception occurred:\n\n")
537
538boundaries = re.compile(
539 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
540
541
542class BaseExceptionReportingTests:
543
544 def get_exception(self, exception_or_callable):
545 if isinstance(exception_or_callable, Exception):
546 return exception_or_callable
547 try:
548 exception_or_callable()
549 except Exception as e:
550 return e
551
552 def zero_div(self):
553 1/0 # In zero_div
554
555 def check_zero_div(self, msg):
556 lines = msg.splitlines()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000557 self.assertTrue(lines[-3].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000558 self.assertIn('1/0 # In zero_div', lines[-2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000559 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000560
561 def test_simple(self):
562 try:
563 1/0 # Marker
564 except ZeroDivisionError as _:
565 e = _
566 lines = self.get_report(e).splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000567 self.assertEqual(len(lines), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000568 self.assertTrue(lines[0].startswith('Traceback'))
569 self.assertTrue(lines[1].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000570 self.assertIn('1/0 # Marker', lines[2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000571 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000572
573 def test_cause(self):
574 def inner_raise():
575 try:
576 self.zero_div()
577 except ZeroDivisionError as e:
578 raise KeyError from e
579 def outer_raise():
580 inner_raise() # Marker
581 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000582 self.assertEqual(len(blocks), 3)
583 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000584 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000585 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000586
587 def test_context(self):
588 def inner_raise():
589 try:
590 self.zero_div()
591 except ZeroDivisionError:
592 raise KeyError
593 def outer_raise():
594 inner_raise() # Marker
595 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000596 self.assertEqual(len(blocks), 3)
597 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000598 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000599 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000600
Nick Coghlanab7bf212012-02-26 17:49:52 +1000601 def test_context_suppression(self):
602 try:
603 try:
604 raise Exception
605 except:
606 raise ZeroDivisionError from None
607 except ZeroDivisionError as _:
608 e = _
609 lines = self.get_report(e).splitlines()
610 self.assertEqual(len(lines), 4)
611 self.assertTrue(lines[0].startswith('Traceback'))
612 self.assertTrue(lines[1].startswith(' File'))
613 self.assertIn('ZeroDivisionError from None', lines[2])
614 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
615
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000616 def test_cause_and_context(self):
617 # When both a cause and a context are set, only the cause should be
618 # displayed and the context should be muted.
619 def inner_raise():
620 try:
621 self.zero_div()
622 except ZeroDivisionError as _e:
623 e = _e
624 try:
625 xyzzy
626 except NameError:
627 raise KeyError from e
628 def outer_raise():
629 inner_raise() # Marker
630 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000631 self.assertEqual(len(blocks), 3)
632 self.assertEqual(blocks[1], cause_message)
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000633 self.check_zero_div(blocks[0])
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000634 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000635
Benjamin Petersone6528212008-07-15 15:32:09 +0000636 def test_cause_recursive(self):
637 def inner_raise():
638 try:
639 try:
640 self.zero_div()
641 except ZeroDivisionError as e:
642 z = e
643 raise KeyError from e
644 except KeyError as e:
645 raise z from e
646 def outer_raise():
647 inner_raise() # Marker
648 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000649 self.assertEqual(len(blocks), 3)
650 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000651 # The first block is the KeyError raised from the ZeroDivisionError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000652 self.assertIn('raise KeyError from e', blocks[0])
653 self.assertNotIn('1/0', blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000654 # The second block (apart from the boundary) is the ZeroDivisionError
655 # re-raised from the KeyError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000656 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000657 self.check_zero_div(blocks[2])
658
Pablo Galindoc5fc1562020-04-22 23:29:27 +0100659 @unittest.skipIf(sys.flags.use_peg,
660 "Pegen is arguably better here, so no need to fix this")
Benjamin Peterson503d6c52010-10-24 02:52:05 +0000661 def test_syntax_error_offset_at_eol(self):
662 # See #10186.
663 def e():
664 raise SyntaxError('', ('', 0, 5, 'hello'))
665 msg = self.get_report(e).splitlines()
666 self.assertEqual(msg[-2], " ^")
Benjamin Petersona95e9772010-10-29 03:28:14 +0000667 def e():
668 exec("x = 5 | 4 |")
669 msg = self.get_report(e).splitlines()
670 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000671
Martin Panterbb8b1cb2016-09-22 09:37:39 +0000672 def test_message_none(self):
673 # A message that looks like "None" should not be treated specially
674 err = self.get_report(Exception(None))
675 self.assertIn('Exception: None\n', err)
676 err = self.get_report(Exception('None'))
677 self.assertIn('Exception: None\n', err)
678 err = self.get_report(Exception())
679 self.assertIn('Exception\n', err)
680 err = self.get_report(Exception(''))
681 self.assertIn('Exception\n', err)
682
Benjamin Petersone6528212008-07-15 15:32:09 +0000683
684class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
685 #
686 # This checks reporting through the 'traceback' module, with both
687 # format_exception() and print_exception().
688 #
689
690 def get_report(self, e):
691 e = self.get_exception(e)
692 s = ''.join(
693 traceback.format_exception(type(e), e, e.__traceback__))
694 with captured_output("stderr") as sio:
695 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000696 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000697 return s
698
699
700class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
701 #
702 # This checks built-in reporting by the interpreter.
703 #
704
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200705 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000706 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200707 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000708 e = self.get_exception(e)
709 with captured_output("stderr") as s:
710 exception_print(e)
711 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000712
713
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300714class LimitTests(unittest.TestCase):
715
716 ''' Tests for limit argument.
717 It's enough to test extact_tb, extract_stack and format_exception '''
718
719 def last_raises1(self):
720 raise Exception('Last raised')
721
722 def last_raises2(self):
723 self.last_raises1()
724
725 def last_raises3(self):
726 self.last_raises2()
727
728 def last_raises4(self):
729 self.last_raises3()
730
731 def last_raises5(self):
732 self.last_raises4()
733
734 def last_returns_frame1(self):
735 return sys._getframe()
736
737 def last_returns_frame2(self):
738 return self.last_returns_frame1()
739
740 def last_returns_frame3(self):
741 return self.last_returns_frame2()
742
743 def last_returns_frame4(self):
744 return self.last_returns_frame3()
745
746 def last_returns_frame5(self):
747 return self.last_returns_frame4()
748
749 def test_extract_stack(self):
750 frame = self.last_returns_frame5()
751 def extract(**kwargs):
752 return traceback.extract_stack(frame, **kwargs)
753 def assertEqualExcept(actual, expected, ignore):
754 self.assertEqual(actual[:ignore], expected[:ignore])
755 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
756 self.assertEqual(len(actual), len(expected))
757
758 with support.swap_attr(sys, 'tracebacklimit', 1000):
759 nolim = extract()
760 self.assertGreater(len(nolim), 5)
761 self.assertEqual(extract(limit=2), nolim[-2:])
762 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
763 self.assertEqual(extract(limit=-2), nolim[:2])
764 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
765 self.assertEqual(extract(limit=0), [])
766 del sys.tracebacklimit
767 assertEqualExcept(extract(), nolim, -5-1)
768 sys.tracebacklimit = 2
769 self.assertEqual(extract(), nolim[-2:])
770 self.assertEqual(extract(limit=3), nolim[-3:])
771 self.assertEqual(extract(limit=-3), nolim[:3])
772 sys.tracebacklimit = 0
773 self.assertEqual(extract(), [])
774 sys.tracebacklimit = -1
775 self.assertEqual(extract(), [])
776
777 def test_extract_tb(self):
778 try:
779 self.last_raises5()
780 except Exception:
781 exc_type, exc_value, tb = sys.exc_info()
782 def extract(**kwargs):
783 return traceback.extract_tb(tb, **kwargs)
784
785 with support.swap_attr(sys, 'tracebacklimit', 1000):
786 nolim = extract()
787 self.assertEqual(len(nolim), 5+1)
788 self.assertEqual(extract(limit=2), nolim[:2])
789 self.assertEqual(extract(limit=10), nolim)
790 self.assertEqual(extract(limit=-2), nolim[-2:])
791 self.assertEqual(extract(limit=-10), nolim)
792 self.assertEqual(extract(limit=0), [])
793 del sys.tracebacklimit
794 self.assertEqual(extract(), nolim)
795 sys.tracebacklimit = 2
796 self.assertEqual(extract(), nolim[:2])
797 self.assertEqual(extract(limit=3), nolim[:3])
798 self.assertEqual(extract(limit=-3), nolim[-3:])
799 sys.tracebacklimit = 0
800 self.assertEqual(extract(), [])
801 sys.tracebacklimit = -1
802 self.assertEqual(extract(), [])
803
804 def test_format_exception(self):
805 try:
806 self.last_raises5()
807 except Exception:
808 exc_type, exc_value, tb = sys.exc_info()
809 # [1:-1] to exclude "Traceback (...)" header and
810 # exception type and value
811 def extract(**kwargs):
812 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
813
814 with support.swap_attr(sys, 'tracebacklimit', 1000):
815 nolim = extract()
816 self.assertEqual(len(nolim), 5+1)
817 self.assertEqual(extract(limit=2), nolim[:2])
818 self.assertEqual(extract(limit=10), nolim)
819 self.assertEqual(extract(limit=-2), nolim[-2:])
820 self.assertEqual(extract(limit=-10), nolim)
821 self.assertEqual(extract(limit=0), [])
822 del sys.tracebacklimit
823 self.assertEqual(extract(), nolim)
824 sys.tracebacklimit = 2
825 self.assertEqual(extract(), nolim[:2])
826 self.assertEqual(extract(limit=3), nolim[:3])
827 self.assertEqual(extract(limit=-3), nolim[-3:])
828 sys.tracebacklimit = 0
829 self.assertEqual(extract(), [])
830 sys.tracebacklimit = -1
831 self.assertEqual(extract(), [])
832
833
Andrew Kuchling173a1572013-09-15 18:15:56 -0400834class MiscTracebackCases(unittest.TestCase):
835 #
836 # Check non-printing functions in traceback module
837 #
838
839 def test_clear(self):
840 def outer():
841 middle()
842 def middle():
843 inner()
844 def inner():
845 i = 1
846 1/0
847
848 try:
849 outer()
850 except:
851 type_, value, tb = sys.exc_info()
852
853 # Initial assertion: there's one local in the inner frame.
854 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
855 self.assertEqual(len(inner_frame.f_locals), 1)
856
857 # Clear traceback frames
858 traceback.clear_frames(tb)
859
860 # Local variable dict should now be empty.
861 self.assertEqual(len(inner_frame.f_locals), 0)
862
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300863 def test_extract_stack(self):
864 def extract():
865 return traceback.extract_stack()
866 result = extract()
867 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300868 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300869 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
870 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
871 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300872 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300873
Andrew Kuchling173a1572013-09-15 18:15:56 -0400874
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300875class TestFrame(unittest.TestCase):
876
877 def test_basics(self):
878 linecache.clearcache()
879 linecache.lazycache("f", globals())
880 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300881 self.assertEqual(f,
882 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
883 self.assertEqual(tuple(f),
884 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
885 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
886 self.assertEqual(f, tuple(f))
887 # Since tuple.__eq__ doesn't support FrameSummary, the equality
888 # operator fallbacks to FrameSummary.__eq__.
889 self.assertEqual(tuple(f), f)
890 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +0300891 self.assertNotEqual(f, object())
892 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300893
894 def test_lazy_lines(self):
895 linecache.clearcache()
896 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
897 self.assertEqual(None, f._line)
898 linecache.lazycache("f", globals())
899 self.assertEqual(
900 '"""Test cases for traceback module"""',
901 f.line)
902
903 def test_explicit_line(self):
904 f = traceback.FrameSummary("f", 1, "dummy", line="line")
905 self.assertEqual("line", f.line)
906
Berker Peksag9797b7a2018-09-10 20:02:33 +0300907 def test_len(self):
908 f = traceback.FrameSummary("f", 1, "dummy", line="line")
909 self.assertEqual(len(f), 4)
910
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300911
912class TestStack(unittest.TestCase):
913
914 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +0300915 def deeper():
916 return list(traceback.walk_stack(None))
917 s1 = list(traceback.walk_stack(None))
918 s2 = deeper()
919 self.assertEqual(len(s2) - len(s1), 1)
920 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300921
922 def test_walk_tb(self):
923 try:
924 1/0
925 except Exception:
926 _, _, tb = sys.exc_info()
927 s = list(traceback.walk_tb(tb))
928 self.assertEqual(len(s), 1)
929
930 def test_extract_stack(self):
931 s = traceback.StackSummary.extract(traceback.walk_stack(None))
932 self.assertIsInstance(s, traceback.StackSummary)
933
934 def test_extract_stack_limit(self):
935 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
936 self.assertEqual(len(s), 5)
937
938 def test_extract_stack_lookup_lines(self):
939 linecache.clearcache()
940 linecache.updatecache('/foo.py', globals())
941 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300942 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300943 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
944 linecache.clearcache()
945 self.assertEqual(s[0].line, "import sys")
946
947 def test_extract_stackup_deferred_lookup_lines(self):
948 linecache.clearcache()
949 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300950 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300951 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
952 self.assertEqual({}, linecache.cache)
953 linecache.updatecache('/foo.py', globals())
954 self.assertEqual(s[0].line, "import sys")
955
956 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +1300957 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300958 self.assertEqual(
959 [' File "foo.py", line 1, in fred\n line\n'],
960 s.format())
961
Robert Collinsbbb8ade2015-03-16 15:27:16 +1300962 def test_from_list_edited_stack(self):
963 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
964 s[0] = ('foo.py', 2, 'fred', 'line')
965 s2 = traceback.StackSummary.from_list(s)
966 self.assertEqual(
967 [' File "foo.py", line 2, in fred\n line\n'],
968 s2.format())
969
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300970 def test_format_smoke(self):
971 # For detailed tests see the format_list tests, which consume the same
972 # code.
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 Collinsd7c7e0e2015-03-05 20:28:52 +1300978 def test_locals(self):
979 linecache.updatecache('/foo.py', globals())
980 c = test_code('/foo.py', 'method')
981 f = test_frame(c, globals(), {'something': 1})
982 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
983 self.assertEqual(s[0].locals, {'something': '1'})
984
985 def test_no_locals(self):
986 linecache.updatecache('/foo.py', globals())
987 c = test_code('/foo.py', 'method')
988 f = test_frame(c, globals(), {'something': 1})
989 s = traceback.StackSummary.extract(iter([(f, 6)]))
990 self.assertEqual(s[0].locals, None)
991
992 def test_format_locals(self):
993 def some_inner(k, v):
994 a = 1
995 b = 2
996 return traceback.StackSummary.extract(
997 traceback.walk_stack(None), capture_locals=True, limit=1)
998 s = some_inner(3, 4)
999 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +03001000 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001001 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001002 ' a = 1\n'
1003 ' b = 2\n'
1004 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001005 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001006 ], s.format())
1007
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001008class TestTracebackException(unittest.TestCase):
1009
1010 def test_smoke(self):
1011 try:
1012 1/0
1013 except Exception:
1014 exc_info = sys.exc_info()
1015 exc = traceback.TracebackException(*exc_info)
1016 expected_stack = traceback.StackSummary.extract(
1017 traceback.walk_tb(exc_info[2]))
1018 self.assertEqual(None, exc.__cause__)
1019 self.assertEqual(None, exc.__context__)
1020 self.assertEqual(False, exc.__suppress_context__)
1021 self.assertEqual(expected_stack, exc.stack)
1022 self.assertEqual(exc_info[0], exc.exc_type)
1023 self.assertEqual(str(exc_info[1]), str(exc))
1024
1025 def test_from_exception(self):
1026 # Check all the parameters are accepted.
1027 def foo():
1028 1/0
1029 try:
1030 foo()
1031 except Exception as e:
1032 exc_info = sys.exc_info()
1033 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001034 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1035 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001036 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001037 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001038 expected_stack = self.expected_stack
1039 exc = self.exc
1040 self.assertEqual(None, exc.__cause__)
1041 self.assertEqual(None, exc.__context__)
1042 self.assertEqual(False, exc.__suppress_context__)
1043 self.assertEqual(expected_stack, exc.stack)
1044 self.assertEqual(exc_info[0], exc.exc_type)
1045 self.assertEqual(str(exc_info[1]), str(exc))
1046
1047 def test_cause(self):
1048 try:
1049 try:
1050 1/0
1051 finally:
1052 exc_info_context = sys.exc_info()
1053 exc_context = traceback.TracebackException(*exc_info_context)
1054 cause = Exception("cause")
1055 raise Exception("uh oh") from cause
1056 except Exception:
1057 exc_info = sys.exc_info()
1058 exc = traceback.TracebackException(*exc_info)
1059 expected_stack = traceback.StackSummary.extract(
1060 traceback.walk_tb(exc_info[2]))
1061 exc_cause = traceback.TracebackException(Exception, cause, None)
1062 self.assertEqual(exc_cause, exc.__cause__)
1063 self.assertEqual(exc_context, exc.__context__)
1064 self.assertEqual(True, exc.__suppress_context__)
1065 self.assertEqual(expected_stack, exc.stack)
1066 self.assertEqual(exc_info[0], exc.exc_type)
1067 self.assertEqual(str(exc_info[1]), str(exc))
1068
1069 def test_context(self):
1070 try:
1071 try:
1072 1/0
1073 finally:
1074 exc_info_context = sys.exc_info()
1075 exc_context = traceback.TracebackException(*exc_info_context)
1076 raise Exception("uh oh")
1077 except Exception:
1078 exc_info = sys.exc_info()
1079 exc = traceback.TracebackException(*exc_info)
1080 expected_stack = traceback.StackSummary.extract(
1081 traceback.walk_tb(exc_info[2]))
1082 self.assertEqual(None, exc.__cause__)
1083 self.assertEqual(exc_context, exc.__context__)
1084 self.assertEqual(False, exc.__suppress_context__)
1085 self.assertEqual(expected_stack, exc.stack)
1086 self.assertEqual(exc_info[0], exc.exc_type)
1087 self.assertEqual(str(exc_info[1]), str(exc))
1088
Serhiy Storchaka662db122019-08-08 08:42:54 +03001089 def test_comparison(self):
1090 try:
1091 1/0
1092 except Exception:
1093 exc_info = sys.exc_info()
1094 exc = traceback.TracebackException(*exc_info)
1095 exc2 = traceback.TracebackException(*exc_info)
1096 self.assertIsNot(exc, exc2)
1097 self.assertEqual(exc, exc2)
1098 self.assertNotEqual(exc, object())
1099 self.assertEqual(exc, ALWAYS_EQ)
1100
Zane Bitterde860732017-10-17 17:29:39 -04001101 def test_unhashable(self):
1102 class UnhashableException(Exception):
1103 def __eq__(self, other):
1104 return True
1105
1106 ex1 = UnhashableException('ex1')
1107 ex2 = UnhashableException('ex2')
1108 try:
1109 raise ex2 from ex1
1110 except UnhashableException:
1111 try:
1112 raise ex1
1113 except UnhashableException:
1114 exc_info = sys.exc_info()
1115 exc = traceback.TracebackException(*exc_info)
1116 formatted = list(exc.format())
1117 self.assertIn('UnhashableException: ex2\n', formatted[2])
1118 self.assertIn('UnhashableException: ex1\n', formatted[6])
1119
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001120 def test_limit(self):
1121 def recurse(n):
1122 if n:
1123 recurse(n-1)
1124 else:
1125 1/0
1126 try:
1127 recurse(10)
1128 except Exception:
1129 exc_info = sys.exc_info()
1130 exc = traceback.TracebackException(*exc_info, limit=5)
1131 expected_stack = traceback.StackSummary.extract(
1132 traceback.walk_tb(exc_info[2]), limit=5)
1133 self.assertEqual(expected_stack, exc.stack)
1134
1135 def test_lookup_lines(self):
1136 linecache.clearcache()
1137 e = Exception("uh oh")
1138 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001139 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001140 tb = test_tb(f, 6, None)
1141 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
1142 self.assertEqual({}, linecache.cache)
1143 linecache.updatecache('/foo.py', globals())
1144 self.assertEqual(exc.stack[0].line, "import sys")
1145
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001146 def test_locals(self):
1147 linecache.updatecache('/foo.py', globals())
1148 e = Exception("uh oh")
1149 c = test_code('/foo.py', 'method')
1150 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1151 tb = test_tb(f, 6, None)
1152 exc = traceback.TracebackException(
1153 Exception, e, tb, capture_locals=True)
1154 self.assertEqual(
1155 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1156
1157 def test_no_locals(self):
1158 linecache.updatecache('/foo.py', globals())
1159 e = Exception("uh oh")
1160 c = test_code('/foo.py', 'method')
1161 f = test_frame(c, globals(), {'something': 1})
1162 tb = test_tb(f, 6, None)
1163 exc = traceback.TracebackException(Exception, e, tb)
1164 self.assertEqual(exc.stack[0].locals, None)
1165
Berker Peksagc3f417d2015-07-24 17:36:21 +03001166 def test_traceback_header(self):
1167 # do not print a traceback header if exc_traceback is None
1168 # see issue #24695
1169 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1170 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1171
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001172
Berker Peksag716b3d32015-04-08 09:47:14 +03001173class MiscTest(unittest.TestCase):
1174
1175 def test_all(self):
1176 expected = set()
1177 blacklist = {'print_list'}
1178 for name in dir(traceback):
1179 if name.startswith('_') or name in blacklist:
1180 continue
1181 module_object = getattr(traceback, name)
1182 if getattr(module_object, '__module__', None) == 'traceback':
1183 expected.add(name)
1184 self.assertCountEqual(traceback.__all__, expected)
1185
Fred Drake2e2be372001-09-20 21:33:42 +00001186
1187if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001188 unittest.main()