blob: 2261ea994209ff4644eb88102b076b2cea8491f5 [file] [log] [blame]
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00001"""Test cases for traceback module"""
2
Robert Collins6bc2c1e2015-03-05 12:07:57 +13003from collections import namedtuple
Christian Heimes81ee3ef2008-05-04 22:42:01 +00004from io import StringIO
Robert Collins6bc2c1e2015-03-05 12:07:57 +13005import linecache
Christian Heimes81ee3ef2008-05-04 22:42:01 +00006import sys
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00007import unittest
Benjamin Petersone6528212008-07-15 15:32:09 +00008import re
Serhiy Storchaka24559e42015-05-03 13:19:46 +03009from test import support
Hai Shideb01622020-07-06 20:29:49 +080010from test.support import Error, captured_output, cpython_only, ALWAYS_EQ
11from test.support.os_helper import TESTFN, unlink
Berker Peksagce643912015-05-06 06:33:17 +030012from test.support.script_helper import assert_python_ok
Victor Stinner9d279b82014-12-05 10:18:30 +010013import textwrap
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000014
15import traceback
16
Christian Heimes81ee3ef2008-05-04 22:42:01 +000017
Robert Collins6bc2c1e2015-03-05 12:07:57 +130018test_code = namedtuple('code', ['co_filename', 'co_name'])
Robert Collinsd7c7e0e2015-03-05 20:28:52 +130019test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals'])
Robert Collins6bc2c1e2015-03-05 12:07:57 +130020test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next'])
21
22
Martin Panterbb8b1cb2016-09-22 09:37:39 +000023class TracebackCases(unittest.TestCase):
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000024 # For now, a very minimal set of tests. I want to be sure that
25 # formatting of SyntaxErrors works based on changes for 2.1.
26
27 def get_exception_format(self, func, exc):
28 try:
29 func()
Guido van Rossumb940e112007-01-10 16:19:56 +000030 except exc as value:
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000031 return traceback.format_exception_only(exc, value)
32 else:
Collin Winter3add4d72007-08-29 23:37:32 +000033 raise ValueError("call did not raise exception")
Tim Peters7e01e282001-04-08 07:44:07 +000034
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000035 def syntax_error_with_caret(self):
36 compile("def fact(x):\n\treturn x!\n", "?", "exec")
37
Georg Brandl751899a2009-06-04 19:41:00 +000038 def syntax_error_with_caret_2(self):
39 compile("1 +\n", "?", "exec")
40
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000041 def syntax_error_bad_indentation(self):
Georg Brandl88fc6642007-02-09 21:28:07 +000042 compile("def spam():\n print(1)\n print(2)", "?", "exec")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000043
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020044 def syntax_error_with_caret_non_ascii(self):
45 compile('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', "?", "exec")
46
Florent Xicluna758fa5e2014-01-22 01:11:43 +010047 def syntax_error_bad_indentation2(self):
48 compile(" print(2)", "?", "exec")
49
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000050 def test_caret(self):
51 err = self.get_exception_format(self.syntax_error_with_caret,
52 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000053 self.assertEqual(len(err), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000054 self.assertTrue(err[1].strip() == "return x!")
Benjamin Peterson577473f2010-01-19 00:09:57 +000055 self.assertIn("^", err[2]) # third line has caret
Guido van Rossume61fd5b2007-07-11 12:20:59 +000056 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place
Tim Peters7e01e282001-04-08 07:44:07 +000057
Georg Brandl751899a2009-06-04 19:41:00 +000058 err = self.get_exception_format(self.syntax_error_with_caret_2,
59 SyntaxError)
Benjamin Peterson577473f2010-01-19 00:09:57 +000060 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010061 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070062 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Georg Brandl751899a2009-06-04 19:41:00 +000063
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020064 err = self.get_exception_format(self.syntax_error_with_caret_non_ascii,
65 SyntaxError)
66 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010067 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070068 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020069
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000070 def test_nocaret(self):
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000071 exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
72 err = traceback.format_exception_only(SyntaxError, exc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000073 self.assertEqual(len(err), 3)
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000074 self.assertEqual(err[1].strip(), "bad syntax")
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000075
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000076 def test_bad_indentation(self):
77 err = self.get_exception_format(self.syntax_error_bad_indentation,
78 IndentationError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000079 self.assertEqual(len(err), 4)
80 self.assertEqual(err[1].strip(), "print(2)")
Benjamin Peterson577473f2010-01-19 00:09:57 +000081 self.assertIn("^", err[2])
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070082 self.assertEqual(err[1].find(")") + 1, err[2].find("^"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000083
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070084 # No caret for "unexpected indent"
Florent Xicluna758fa5e2014-01-22 01:11:43 +010085 err = self.get_exception_format(self.syntax_error_bad_indentation2,
86 IndentationError)
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070087 self.assertEqual(len(err), 3)
Florent Xicluna758fa5e2014-01-22 01:11:43 +010088 self.assertEqual(err[1].strip(), "print(2)")
Florent Xicluna758fa5e2014-01-22 01:11:43 +010089
Thomas Wouters477c8d52006-05-27 19:21:47 +000090 def test_base_exception(self):
91 # Test that exceptions derived from BaseException are formatted right
92 e = KeyboardInterrupt()
93 lst = traceback.format_exception_only(e.__class__, e)
94 self.assertEqual(lst, ['KeyboardInterrupt\n'])
95
Thomas Wouters0e3f5912006-08-11 14:57:12 +000096 def test_format_exception_only_bad__str__(self):
97 class X(Exception):
98 def __str__(self):
99 1/0
100 err = traceback.format_exception_only(X, X())
101 self.assertEqual(len(err), 1)
102 str_value = '<unprintable %s object>' % X.__name__
Georg Brandl1a3284e2007-12-02 09:40:06 +0000103 if X.__module__ in ('__main__', 'builtins'):
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300104 str_name = X.__qualname__
Brett Cannon44c52612007-02-27 00:12:43 +0000105 else:
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300106 str_name = '.'.join([X.__module__, X.__qualname__])
Brett Cannon44c52612007-02-27 00:12:43 +0000107 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000108
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000109 def test_encoded_file(self):
110 # Test that tracebacks are correctly printed for encoded source files:
111 # - correct line number (Issue2384)
112 # - respect file encoding (Issue3975)
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)90d0cfb2018-11-16 21:02:58 +0530113 import sys, subprocess
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000114
115 # The spawned subprocess has its stdout redirected to a PIPE, and its
116 # encoding may be different from the current interpreter, on Windows
117 # at least.
118 process = subprocess.Popen([sys.executable, "-c",
119 "import sys; print(sys.stdout.encoding)"],
120 stdout=subprocess.PIPE,
121 stderr=subprocess.STDOUT)
122 stdout, stderr = process.communicate()
123 output_encoding = str(stdout, 'ascii').splitlines()[0]
124
125 def do_test(firstlines, message, charset, lineno):
126 # Raise the message in a subprocess, and catch the output
127 try:
Victor Stinner51d8c522016-02-08 17:57:02 +0100128 with open(TESTFN, "w", encoding=charset) as output:
129 output.write("""{0}if 1:
130 import traceback;
131 raise RuntimeError('{1}')
132 """.format(firstlines, message))
133
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000134 process = subprocess.Popen([sys.executable, TESTFN],
135 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
136 stdout, stderr = process.communicate()
137 stdout = stdout.decode(output_encoding).splitlines()
138 finally:
139 unlink(TESTFN)
140
141 # The source lines are encoded with the 'backslashreplace' handler
142 encoded_message = message.encode(output_encoding,
143 'backslashreplace')
144 # and we just decoded them with the output_encoding.
145 message_ascii = encoded_message.decode(output_encoding)
146
147 err_line = "raise RuntimeError('{0}')".format(message_ascii)
148 err_msg = "RuntimeError: {0}".format(message_ascii)
149
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000150 self.assertIn(("line %s" % lineno), stdout[1],
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000151 "Invalid line number: {0!r} instead of {1}".format(
152 stdout[1], lineno))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000153 self.assertTrue(stdout[2].endswith(err_line),
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000154 "Invalid traceback line: {0!r} instead of {1!r}".format(
155 stdout[2], err_line))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000156 self.assertTrue(stdout[3] == err_msg,
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000157 "Invalid error message: {0!r} instead of {1!r}".format(
158 stdout[3], err_msg))
159
160 do_test("", "foo", "ascii", 3)
161 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
162 if charset == "ascii":
163 text = "foo"
164 elif charset == "GBK":
165 text = "\u4E02\u5100"
166 else:
167 text = "h\xe9 ho"
168 do_test("# coding: {0}\n".format(charset),
169 text, charset, 4)
170 do_test("#!shebang\n# coding: {0}\n".format(charset),
171 text, charset, 5)
Serhiy Storchaka1064a132014-01-09 20:12:49 +0200172 do_test(" \t\f\n# coding: {0}\n".format(charset),
173 text, charset, 5)
Martin Panter614827c2016-04-19 04:05:59 +0000174 # Issue #18960: coding spec should have no effect
Victor Stinner51d8c522016-02-08 17:57:02 +0100175 do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000176
Victor Stinner9d279b82014-12-05 10:18:30 +0100177 def test_print_traceback_at_exit(self):
178 # Issue #22599: Ensure that it is possible to use the traceback module
179 # to display an exception at Python exit
180 code = textwrap.dedent("""
181 import sys
182 import traceback
183
184 class PrintExceptionAtExit(object):
185 def __init__(self):
186 try:
187 x = 1 / 0
188 except Exception:
189 self.exc_info = sys.exc_info()
190 # self.exc_info[1] (traceback) contains frames:
191 # explicitly clear the reference to self in the current
192 # frame to break a reference cycle
193 self = None
194
195 def __del__(self):
196 traceback.print_exception(*self.exc_info)
197
198 # Keep a reference in the module namespace to call the destructor
199 # when the module is unloaded
200 obj = PrintExceptionAtExit()
201 """)
202 rc, stdout, stderr = assert_python_ok('-c', code)
203 expected = [b'Traceback (most recent call last):',
204 b' File "<string>", line 8, in __init__',
205 b'ZeroDivisionError: division by zero']
206 self.assertEqual(stderr.splitlines(), expected)
207
Berker Peksagc3f417d2015-07-24 17:36:21 +0300208 def test_print_exception(self):
209 output = StringIO()
210 traceback.print_exception(
211 Exception, Exception("projector"), None, file=output
212 )
213 self.assertEqual(output.getvalue(), "Exception: projector\n")
214
Zackery Spytz91e93792020-11-05 15:18:44 -0700215 def test_print_exception_exc(self):
216 output = StringIO()
217 traceback.print_exception(Exception("projector"), file=output)
218 self.assertEqual(output.getvalue(), "Exception: projector\n")
219
220 def test_format_exception_exc(self):
221 e = Exception("projector")
222 output = traceback.format_exception(e)
223 self.assertEqual(output, ["Exception: projector\n"])
224 with self.assertRaisesRegex(ValueError, 'Both or neither'):
225 traceback.format_exception(e.__class__, e)
226 with self.assertRaisesRegex(ValueError, 'Both or neither'):
227 traceback.format_exception(e.__class__, tb=e.__traceback__)
228 with self.assertRaisesRegex(TypeError, 'positional-only'):
229 traceback.format_exception(exc=e)
230
231 def test_format_exception_only_exc(self):
232 output = traceback.format_exception_only(Exception("projector"))
233 self.assertEqual(output, ["Exception: projector\n"])
234
Irit Katriel26f18b82021-02-23 14:58:47 +0000235 def test_exception_is_None(self):
236 NONE_EXC_STRING = 'NoneType: None\n'
237 excfile = StringIO()
238 traceback.print_exception(None, None, None, file=excfile)
239 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
240
241 excfile = StringIO()
242 traceback.print_exc(None, file=excfile)
243 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
244
245 self.assertEqual(traceback.format_exc(None), NONE_EXC_STRING)
246 self.assertEqual(
247 traceback.format_exception(None, None, None), [NONE_EXC_STRING])
248 self.assertEqual(
249 traceback.format_exception_only(None), [NONE_EXC_STRING])
250 self.assertEqual(
251 traceback.format_exception_only(None, None), [NONE_EXC_STRING])
252
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000253
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000254class TracebackFormatTests(unittest.TestCase):
255
Antoine Pitrou58720d62013-08-05 23:26:40 +0200256 def some_exception(self):
257 raise KeyError('blah')
258
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200259 @cpython_only
Antoine Pitrou58720d62013-08-05 23:26:40 +0200260 def check_traceback_format(self, cleanup_func=None):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200261 from _testcapi import traceback_print
Georg Brandl236f7972009-04-05 14:28:42 +0000262 try:
Antoine Pitrou58720d62013-08-05 23:26:40 +0200263 self.some_exception()
Georg Brandl236f7972009-04-05 14:28:42 +0000264 except KeyError:
265 type_, value, tb = sys.exc_info()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200266 if cleanup_func is not None:
267 # Clear the inner frames, not this one
268 cleanup_func(tb.tb_next)
Georg Brandl236f7972009-04-05 14:28:42 +0000269 traceback_fmt = 'Traceback (most recent call last):\n' + \
270 ''.join(traceback.format_tb(tb))
271 file_ = StringIO()
272 traceback_print(tb, file_)
273 python_fmt = file_.getvalue()
Benjamin Petersond9fec152013-04-29 16:09:39 -0400274 # Call all _tb and _exc functions
275 with captured_output("stderr") as tbstderr:
276 traceback.print_tb(tb)
277 tbfile = StringIO()
278 traceback.print_tb(tb, file=tbfile)
279 with captured_output("stderr") as excstderr:
280 traceback.print_exc()
281 excfmt = traceback.format_exc()
282 excfile = StringIO()
283 traceback.print_exc(file=excfile)
Georg Brandl236f7972009-04-05 14:28:42 +0000284 else:
285 raise Error("unable to create test traceback string")
286
287 # Make sure that Python and the traceback module format the same thing
Ezio Melottib3aedd42010-11-20 19:04:17 +0000288 self.assertEqual(traceback_fmt, python_fmt)
Benjamin Petersond9fec152013-04-29 16:09:39 -0400289 # Now verify the _tb func output
290 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
291 # Now verify the _exc func output
292 self.assertEqual(excstderr.getvalue(), excfile.getvalue())
293 self.assertEqual(excfmt, excfile.getvalue())
Georg Brandl236f7972009-04-05 14:28:42 +0000294
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000295 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000296 tb_lines = python_fmt.splitlines()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200297 self.assertEqual(len(tb_lines), 5)
298 banner = tb_lines[0]
299 location, source_line = tb_lines[-2:]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000300 self.assertTrue(banner.startswith('Traceback'))
301 self.assertTrue(location.startswith(' File'))
302 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000303
Antoine Pitrou58720d62013-08-05 23:26:40 +0200304 def test_traceback_format(self):
305 self.check_traceback_format()
306
307 def test_traceback_format_with_cleared_frames(self):
308 # Check that traceback formatting also works with a clear()ed frame
309 def cleanup_tb(tb):
310 tb.tb_frame.clear()
311 self.check_traceback_format(cleanup_tb)
312
Benjamin Petersond9fec152013-04-29 16:09:39 -0400313 def test_stack_format(self):
314 # Verify _stack functions. Note we have to use _getframe(1) to
315 # compare them without this frame appearing in the output
316 with captured_output("stderr") as ststderr:
317 traceback.print_stack(sys._getframe(1))
318 stfile = StringIO()
319 traceback.print_stack(sys._getframe(1), file=stfile)
320 self.assertEqual(ststderr.getvalue(), stfile.getvalue())
321
322 stfmt = traceback.format_stack(sys._getframe(1))
323
324 self.assertEqual(ststderr.getvalue(), "".join(stfmt))
325
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300326 def test_print_stack(self):
327 def prn():
328 traceback.print_stack()
329 with captured_output("stderr") as stderr:
330 prn()
331 lineno = prn.__code__.co_firstlineno
332 self.assertEqual(stderr.getvalue().splitlines()[-4:], [
333 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3),
334 ' prn()',
335 ' File "%s", line %d, in prn' % (__file__, lineno+1),
336 ' traceback.print_stack()',
337 ])
338
Nick Coghland0034232016-08-15 13:11:34 +1000339 # issue 26823 - Shrink recursive tracebacks
340 def _check_recursive_traceback_display(self, render_exc):
341 # Always show full diffs when this test fails
342 # Note that rearranging things may require adjusting
343 # the relative line numbers in the expected tracebacks
344 self.maxDiff = None
345
346 # Check hitting the recursion limit
347 def f():
348 f()
349
350 with captured_output("stderr") as stderr_f:
351 try:
352 f()
Pablo Galindo293dd232019-11-19 21:34:03 +0000353 except RecursionError:
Nick Coghland0034232016-08-15 13:11:34 +1000354 render_exc()
355 else:
356 self.fail("no recursion occurred")
357
358 lineno_f = f.__code__.co_firstlineno
359 result_f = (
360 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400361 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000362 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400363 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000364 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400365 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000366 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400367 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000368 ' f()\n'
369 # XXX: The following line changes depending on whether the tests
370 # are run through the interactive interpreter or with -m
371 # It also varies depending on the platform (stack size)
372 # Fortunately, we don't care about exactness here, so we use regex
373 r' \[Previous line repeated (\d+) more times\]' '\n'
374 'RecursionError: maximum recursion depth exceeded\n'
375 )
376
377 expected = result_f.splitlines()
378 actual = stderr_f.getvalue().splitlines()
379
380 # Check the output text matches expectations
381 # 2nd last line contains the repetition count
382 self.assertEqual(actual[:-2], expected[:-2])
383 self.assertRegex(actual[-2], expected[-2])
codedragon3480ef92017-05-24 14:23:46 -0700384 # last line can have additional text appended
385 self.assertIn(expected[-1], actual[-1])
Nick Coghland0034232016-08-15 13:11:34 +1000386
387 # Check the recursion count is roughly as expected
388 rec_limit = sys.getrecursionlimit()
Serhiy Storchakab7281052016-09-12 00:52:40 +0300389 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
Nick Coghland0034232016-08-15 13:11:34 +1000390
391 # Check a known (limited) number of recursive invocations
392 def g(count=10):
393 if count:
394 return g(count-1)
395 raise ValueError
396
397 with captured_output("stderr") as stderr_g:
398 try:
399 g()
Pablo Galindo293dd232019-11-19 21:34:03 +0000400 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000401 render_exc()
402 else:
403 self.fail("no value error was raised")
404
405 lineno_g = g.__code__.co_firstlineno
406 result_g = (
Eric V. Smith451d0e32016-09-09 21:56:20 -0400407 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000408 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400409 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000410 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400411 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000412 ' return g(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700413 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400414 f' File "{__file__}", line {lineno_g+3}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000415 ' raise ValueError\n'
416 'ValueError\n'
417 )
418 tb_line = (
419 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400420 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000421 ' g()\n'
422 )
423 expected = (tb_line + result_g).splitlines()
424 actual = stderr_g.getvalue().splitlines()
425 self.assertEqual(actual, expected)
426
427 # Check 2 different repetitive sections
428 def h(count=10):
429 if count:
430 return h(count-1)
431 g()
432
433 with captured_output("stderr") as stderr_h:
434 try:
435 h()
Pablo Galindo293dd232019-11-19 21:34:03 +0000436 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000437 render_exc()
438 else:
439 self.fail("no value error was raised")
440
441 lineno_h = h.__code__.co_firstlineno
442 result_h = (
443 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400444 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000445 ' h()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400446 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000447 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400448 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000449 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400450 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000451 ' return h(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700452 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400453 f' File "{__file__}", line {lineno_h+3}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000454 ' g()\n'
455 )
456 expected = (result_h + result_g).splitlines()
457 actual = stderr_h.getvalue().splitlines()
458 self.assertEqual(actual, expected)
459
Benjamin Petersond5458692018-09-10 08:43:10 -0700460 # Check the boundary conditions. First, test just below the cutoff.
461 with captured_output("stderr") as stderr_g:
462 try:
463 g(traceback._RECURSIVE_CUTOFF)
Pablo Galindo293dd232019-11-19 21:34:03 +0000464 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700465 render_exc()
466 else:
467 self.fail("no error raised")
468 result_g = (
469 f' File "{__file__}", line {lineno_g+2}, in g\n'
470 ' return g(count-1)\n'
471 f' File "{__file__}", line {lineno_g+2}, in g\n'
472 ' return g(count-1)\n'
473 f' File "{__file__}", line {lineno_g+2}, in g\n'
474 ' return g(count-1)\n'
475 f' File "{__file__}", line {lineno_g+3}, in g\n'
476 ' raise ValueError\n'
477 'ValueError\n'
478 )
479 tb_line = (
480 'Traceback (most recent call last):\n'
481 f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n'
482 ' g(traceback._RECURSIVE_CUTOFF)\n'
483 )
484 expected = (tb_line + result_g).splitlines()
485 actual = stderr_g.getvalue().splitlines()
486 self.assertEqual(actual, expected)
487
488 # Second, test just above the cutoff.
489 with captured_output("stderr") as stderr_g:
490 try:
491 g(traceback._RECURSIVE_CUTOFF + 1)
Pablo Galindo293dd232019-11-19 21:34:03 +0000492 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700493 render_exc()
494 else:
495 self.fail("no error raised")
496 result_g = (
497 f' File "{__file__}", line {lineno_g+2}, in g\n'
498 ' return g(count-1)\n'
499 f' File "{__file__}", line {lineno_g+2}, in g\n'
500 ' return g(count-1)\n'
501 f' File "{__file__}", line {lineno_g+2}, in g\n'
502 ' return g(count-1)\n'
503 ' [Previous line repeated 1 more time]\n'
504 f' File "{__file__}", line {lineno_g+3}, in g\n'
505 ' raise ValueError\n'
506 'ValueError\n'
507 )
508 tb_line = (
509 'Traceback (most recent call last):\n'
510 f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n'
511 ' g(traceback._RECURSIVE_CUTOFF + 1)\n'
512 )
513 expected = (tb_line + result_g).splitlines()
514 actual = stderr_g.getvalue().splitlines()
515 self.assertEqual(actual, expected)
516
Nick Coghland0034232016-08-15 13:11:34 +1000517 def test_recursive_traceback_python(self):
518 self._check_recursive_traceback_display(traceback.print_exc)
519
520 @cpython_only
521 def test_recursive_traceback_cpython_internal(self):
522 from _testcapi import exception_print
523 def render_exc():
524 exc_type, exc_value, exc_tb = sys.exc_info()
525 exception_print(exc_value)
526 self._check_recursive_traceback_display(render_exc)
527
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300528 def test_format_stack(self):
529 def fmt():
530 return traceback.format_stack()
531 result = fmt()
532 lineno = fmt.__code__.co_firstlineno
533 self.assertEqual(result[-2:], [
534 ' File "%s", line %d, in test_format_stack\n'
535 ' result = fmt()\n' % (__file__, lineno+2),
536 ' File "%s", line %d, in fmt\n'
537 ' return traceback.format_stack()\n' % (__file__, lineno+1),
538 ])
539
Zane Bitterde860732017-10-17 17:29:39 -0400540 @cpython_only
541 def test_unhashable(self):
542 from _testcapi import exception_print
543
544 class UnhashableException(Exception):
545 def __eq__(self, other):
546 return True
547
548 ex1 = UnhashableException('ex1')
549 ex2 = UnhashableException('ex2')
550 try:
551 raise ex2 from ex1
552 except UnhashableException:
553 try:
554 raise ex1
555 except UnhashableException:
556 exc_type, exc_val, exc_tb = sys.exc_info()
557
558 with captured_output("stderr") as stderr_f:
559 exception_print(exc_val)
560
561 tb = stderr_f.getvalue().strip().splitlines()
562 self.assertEqual(11, len(tb))
563 self.assertEqual(context_message.strip(), tb[5])
564 self.assertIn('UnhashableException: ex2', tb[3])
565 self.assertIn('UnhashableException: ex1', tb[10])
566
Benjamin Petersone6528212008-07-15 15:32:09 +0000567
568cause_message = (
569 "\nThe above exception was the direct cause "
570 "of the following exception:\n\n")
571
572context_message = (
573 "\nDuring handling of the above exception, "
574 "another exception occurred:\n\n")
575
576boundaries = re.compile(
577 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
578
579
580class BaseExceptionReportingTests:
581
582 def get_exception(self, exception_or_callable):
583 if isinstance(exception_or_callable, Exception):
584 return exception_or_callable
585 try:
586 exception_or_callable()
587 except Exception as e:
588 return e
589
590 def zero_div(self):
591 1/0 # In zero_div
592
593 def check_zero_div(self, msg):
594 lines = msg.splitlines()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000595 self.assertTrue(lines[-3].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000596 self.assertIn('1/0 # In zero_div', lines[-2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000597 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000598
599 def test_simple(self):
600 try:
601 1/0 # Marker
602 except ZeroDivisionError as _:
603 e = _
604 lines = self.get_report(e).splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000605 self.assertEqual(len(lines), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000606 self.assertTrue(lines[0].startswith('Traceback'))
607 self.assertTrue(lines[1].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000608 self.assertIn('1/0 # Marker', lines[2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000609 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000610
611 def test_cause(self):
612 def inner_raise():
613 try:
614 self.zero_div()
615 except ZeroDivisionError as e:
616 raise KeyError from e
617 def outer_raise():
618 inner_raise() # Marker
619 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000620 self.assertEqual(len(blocks), 3)
621 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000622 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000623 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000624
625 def test_context(self):
626 def inner_raise():
627 try:
628 self.zero_div()
629 except ZeroDivisionError:
630 raise KeyError
631 def outer_raise():
632 inner_raise() # Marker
633 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000634 self.assertEqual(len(blocks), 3)
635 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000636 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000637 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000638
Nick Coghlanab7bf212012-02-26 17:49:52 +1000639 def test_context_suppression(self):
640 try:
641 try:
642 raise Exception
643 except:
644 raise ZeroDivisionError from None
645 except ZeroDivisionError as _:
646 e = _
647 lines = self.get_report(e).splitlines()
648 self.assertEqual(len(lines), 4)
649 self.assertTrue(lines[0].startswith('Traceback'))
650 self.assertTrue(lines[1].startswith(' File'))
651 self.assertIn('ZeroDivisionError from None', lines[2])
652 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
653
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000654 def test_cause_and_context(self):
655 # When both a cause and a context are set, only the cause should be
656 # displayed and the context should be muted.
657 def inner_raise():
658 try:
659 self.zero_div()
660 except ZeroDivisionError as _e:
661 e = _e
662 try:
663 xyzzy
664 except NameError:
665 raise KeyError from e
666 def outer_raise():
667 inner_raise() # Marker
668 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000669 self.assertEqual(len(blocks), 3)
670 self.assertEqual(blocks[1], cause_message)
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000671 self.check_zero_div(blocks[0])
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000672 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000673
Benjamin Petersone6528212008-07-15 15:32:09 +0000674 def test_cause_recursive(self):
675 def inner_raise():
676 try:
677 try:
678 self.zero_div()
679 except ZeroDivisionError as e:
680 z = e
681 raise KeyError from e
682 except KeyError as e:
683 raise z from e
684 def outer_raise():
685 inner_raise() # Marker
686 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000687 self.assertEqual(len(blocks), 3)
688 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000689 # The first block is the KeyError raised from the ZeroDivisionError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000690 self.assertIn('raise KeyError from e', blocks[0])
691 self.assertNotIn('1/0', blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000692 # The second block (apart from the boundary) is the ZeroDivisionError
693 # re-raised from the KeyError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000694 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000695 self.check_zero_div(blocks[2])
696
Benjamin Peterson503d6c52010-10-24 02:52:05 +0000697 def test_syntax_error_offset_at_eol(self):
698 # See #10186.
699 def e():
700 raise SyntaxError('', ('', 0, 5, 'hello'))
701 msg = self.get_report(e).splitlines()
702 self.assertEqual(msg[-2], " ^")
Benjamin Petersona95e9772010-10-29 03:28:14 +0000703 def e():
704 exec("x = 5 | 4 |")
705 msg = self.get_report(e).splitlines()
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700706 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000707
Irit Katriel069560b2020-12-22 19:53:09 +0000708 def test_syntax_error_no_lineno(self):
709 # See #34463.
710
711 # Without filename
712 e = SyntaxError('bad syntax')
713 msg = self.get_report(e).splitlines()
714 self.assertEqual(msg,
715 ['SyntaxError: bad syntax'])
716 e.lineno = 100
717 msg = self.get_report(e).splitlines()
718 self.assertEqual(msg,
719 [' File "<string>", line 100', 'SyntaxError: bad syntax'])
720
721 # With filename
722 e = SyntaxError('bad syntax')
723 e.filename = 'myfile.py'
724
725 msg = self.get_report(e).splitlines()
726 self.assertEqual(msg,
727 ['SyntaxError: bad syntax (myfile.py)'])
728 e.lineno = 100
729 msg = self.get_report(e).splitlines()
730 self.assertEqual(msg,
731 [' File "myfile.py", line 100', 'SyntaxError: bad syntax'])
732
Martin Panterbb8b1cb2016-09-22 09:37:39 +0000733 def test_message_none(self):
734 # A message that looks like "None" should not be treated specially
735 err = self.get_report(Exception(None))
736 self.assertIn('Exception: None\n', err)
737 err = self.get_report(Exception('None'))
738 self.assertIn('Exception: None\n', err)
739 err = self.get_report(Exception())
740 self.assertIn('Exception\n', err)
741 err = self.get_report(Exception(''))
742 self.assertIn('Exception\n', err)
743
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700744 def test_syntax_error_various_offsets(self):
745 for offset in range(-5, 10):
746 for add in [0, 2]:
747 text = " "*add + "text%d" % offset
748 expected = [' File "file.py", line 1']
749 if offset < 1:
750 expected.append(" %s" % text.lstrip())
751 elif offset <= 6:
752 expected.append(" %s" % text.lstrip())
753 expected.append(" %s^" % (" "*(offset-1)))
754 else:
755 expected.append(" %s" % text.lstrip())
756 expected.append(" %s^" % (" "*5))
757 expected.append("SyntaxError: msg")
758 expected.append("")
759 err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text)))
760 exp = "\n".join(expected)
761 self.assertEqual(exp, err)
762
Benjamin Petersone6528212008-07-15 15:32:09 +0000763
764class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
765 #
766 # This checks reporting through the 'traceback' module, with both
767 # format_exception() and print_exception().
768 #
769
770 def get_report(self, e):
771 e = self.get_exception(e)
772 s = ''.join(
773 traceback.format_exception(type(e), e, e.__traceback__))
774 with captured_output("stderr") as sio:
775 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000776 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000777 return s
778
779
780class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
781 #
782 # This checks built-in reporting by the interpreter.
783 #
784
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200785 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000786 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200787 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000788 e = self.get_exception(e)
789 with captured_output("stderr") as s:
790 exception_print(e)
791 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000792
793
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300794class LimitTests(unittest.TestCase):
795
796 ''' Tests for limit argument.
797 It's enough to test extact_tb, extract_stack and format_exception '''
798
799 def last_raises1(self):
800 raise Exception('Last raised')
801
802 def last_raises2(self):
803 self.last_raises1()
804
805 def last_raises3(self):
806 self.last_raises2()
807
808 def last_raises4(self):
809 self.last_raises3()
810
811 def last_raises5(self):
812 self.last_raises4()
813
814 def last_returns_frame1(self):
815 return sys._getframe()
816
817 def last_returns_frame2(self):
818 return self.last_returns_frame1()
819
820 def last_returns_frame3(self):
821 return self.last_returns_frame2()
822
823 def last_returns_frame4(self):
824 return self.last_returns_frame3()
825
826 def last_returns_frame5(self):
827 return self.last_returns_frame4()
828
829 def test_extract_stack(self):
830 frame = self.last_returns_frame5()
831 def extract(**kwargs):
832 return traceback.extract_stack(frame, **kwargs)
833 def assertEqualExcept(actual, expected, ignore):
834 self.assertEqual(actual[:ignore], expected[:ignore])
835 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
836 self.assertEqual(len(actual), len(expected))
837
838 with support.swap_attr(sys, 'tracebacklimit', 1000):
839 nolim = extract()
840 self.assertGreater(len(nolim), 5)
841 self.assertEqual(extract(limit=2), nolim[-2:])
842 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
843 self.assertEqual(extract(limit=-2), nolim[:2])
844 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
845 self.assertEqual(extract(limit=0), [])
846 del sys.tracebacklimit
847 assertEqualExcept(extract(), nolim, -5-1)
848 sys.tracebacklimit = 2
849 self.assertEqual(extract(), nolim[-2:])
850 self.assertEqual(extract(limit=3), nolim[-3:])
851 self.assertEqual(extract(limit=-3), nolim[:3])
852 sys.tracebacklimit = 0
853 self.assertEqual(extract(), [])
854 sys.tracebacklimit = -1
855 self.assertEqual(extract(), [])
856
857 def test_extract_tb(self):
858 try:
859 self.last_raises5()
860 except Exception:
861 exc_type, exc_value, tb = sys.exc_info()
862 def extract(**kwargs):
863 return traceback.extract_tb(tb, **kwargs)
864
865 with support.swap_attr(sys, 'tracebacklimit', 1000):
866 nolim = extract()
867 self.assertEqual(len(nolim), 5+1)
868 self.assertEqual(extract(limit=2), nolim[:2])
869 self.assertEqual(extract(limit=10), nolim)
870 self.assertEqual(extract(limit=-2), nolim[-2:])
871 self.assertEqual(extract(limit=-10), nolim)
872 self.assertEqual(extract(limit=0), [])
873 del sys.tracebacklimit
874 self.assertEqual(extract(), nolim)
875 sys.tracebacklimit = 2
876 self.assertEqual(extract(), nolim[:2])
877 self.assertEqual(extract(limit=3), nolim[:3])
878 self.assertEqual(extract(limit=-3), nolim[-3:])
879 sys.tracebacklimit = 0
880 self.assertEqual(extract(), [])
881 sys.tracebacklimit = -1
882 self.assertEqual(extract(), [])
883
884 def test_format_exception(self):
885 try:
886 self.last_raises5()
887 except Exception:
888 exc_type, exc_value, tb = sys.exc_info()
889 # [1:-1] to exclude "Traceback (...)" header and
890 # exception type and value
891 def extract(**kwargs):
892 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
893
894 with support.swap_attr(sys, 'tracebacklimit', 1000):
895 nolim = extract()
896 self.assertEqual(len(nolim), 5+1)
897 self.assertEqual(extract(limit=2), nolim[:2])
898 self.assertEqual(extract(limit=10), nolim)
899 self.assertEqual(extract(limit=-2), nolim[-2:])
900 self.assertEqual(extract(limit=-10), nolim)
901 self.assertEqual(extract(limit=0), [])
902 del sys.tracebacklimit
903 self.assertEqual(extract(), nolim)
904 sys.tracebacklimit = 2
905 self.assertEqual(extract(), nolim[:2])
906 self.assertEqual(extract(limit=3), nolim[:3])
907 self.assertEqual(extract(limit=-3), nolim[-3:])
908 sys.tracebacklimit = 0
909 self.assertEqual(extract(), [])
910 sys.tracebacklimit = -1
911 self.assertEqual(extract(), [])
912
913
Andrew Kuchling173a1572013-09-15 18:15:56 -0400914class MiscTracebackCases(unittest.TestCase):
915 #
916 # Check non-printing functions in traceback module
917 #
918
919 def test_clear(self):
920 def outer():
921 middle()
922 def middle():
923 inner()
924 def inner():
925 i = 1
926 1/0
927
928 try:
929 outer()
930 except:
931 type_, value, tb = sys.exc_info()
932
933 # Initial assertion: there's one local in the inner frame.
934 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
935 self.assertEqual(len(inner_frame.f_locals), 1)
936
937 # Clear traceback frames
938 traceback.clear_frames(tb)
939
940 # Local variable dict should now be empty.
941 self.assertEqual(len(inner_frame.f_locals), 0)
942
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300943 def test_extract_stack(self):
944 def extract():
945 return traceback.extract_stack()
946 result = extract()
947 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300948 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300949 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
950 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
951 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300952 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300953
Andrew Kuchling173a1572013-09-15 18:15:56 -0400954
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300955class TestFrame(unittest.TestCase):
956
957 def test_basics(self):
958 linecache.clearcache()
959 linecache.lazycache("f", globals())
960 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300961 self.assertEqual(f,
962 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
963 self.assertEqual(tuple(f),
964 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
965 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
966 self.assertEqual(f, tuple(f))
967 # Since tuple.__eq__ doesn't support FrameSummary, the equality
968 # operator fallbacks to FrameSummary.__eq__.
969 self.assertEqual(tuple(f), f)
970 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +0300971 self.assertNotEqual(f, object())
972 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300973
974 def test_lazy_lines(self):
975 linecache.clearcache()
976 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
977 self.assertEqual(None, f._line)
978 linecache.lazycache("f", globals())
979 self.assertEqual(
980 '"""Test cases for traceback module"""',
981 f.line)
982
983 def test_explicit_line(self):
984 f = traceback.FrameSummary("f", 1, "dummy", line="line")
985 self.assertEqual("line", f.line)
986
Berker Peksag9797b7a2018-09-10 20:02:33 +0300987 def test_len(self):
988 f = traceback.FrameSummary("f", 1, "dummy", line="line")
989 self.assertEqual(len(f), 4)
990
Robert Collins6bc2c1e2015-03-05 12:07:57 +1300991
992class TestStack(unittest.TestCase):
993
994 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +0300995 def deeper():
996 return list(traceback.walk_stack(None))
997 s1 = list(traceback.walk_stack(None))
998 s2 = deeper()
999 self.assertEqual(len(s2) - len(s1), 1)
1000 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001001
1002 def test_walk_tb(self):
1003 try:
1004 1/0
1005 except Exception:
1006 _, _, tb = sys.exc_info()
1007 s = list(traceback.walk_tb(tb))
1008 self.assertEqual(len(s), 1)
1009
1010 def test_extract_stack(self):
1011 s = traceback.StackSummary.extract(traceback.walk_stack(None))
1012 self.assertIsInstance(s, traceback.StackSummary)
1013
1014 def test_extract_stack_limit(self):
1015 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
1016 self.assertEqual(len(s), 5)
1017
1018 def test_extract_stack_lookup_lines(self):
1019 linecache.clearcache()
1020 linecache.updatecache('/foo.py', globals())
1021 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001022 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001023 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
1024 linecache.clearcache()
1025 self.assertEqual(s[0].line, "import sys")
1026
1027 def test_extract_stackup_deferred_lookup_lines(self):
1028 linecache.clearcache()
1029 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001030 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001031 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
1032 self.assertEqual({}, linecache.cache)
1033 linecache.updatecache('/foo.py', globals())
1034 self.assertEqual(s[0].line, "import sys")
1035
1036 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001037 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001038 self.assertEqual(
1039 [' File "foo.py", line 1, in fred\n line\n'],
1040 s.format())
1041
Robert Collinsbbb8ade2015-03-16 15:27:16 +13001042 def test_from_list_edited_stack(self):
1043 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
1044 s[0] = ('foo.py', 2, 'fred', 'line')
1045 s2 = traceback.StackSummary.from_list(s)
1046 self.assertEqual(
1047 [' File "foo.py", line 2, in fred\n line\n'],
1048 s2.format())
1049
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001050 def test_format_smoke(self):
1051 # For detailed tests see the format_list tests, which consume the same
1052 # code.
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001053 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001054 self.assertEqual(
1055 [' File "foo.py", line 1, in fred\n line\n'],
1056 s.format())
1057
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001058 def test_locals(self):
1059 linecache.updatecache('/foo.py', globals())
1060 c = test_code('/foo.py', 'method')
1061 f = test_frame(c, globals(), {'something': 1})
1062 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
1063 self.assertEqual(s[0].locals, {'something': '1'})
1064
1065 def test_no_locals(self):
1066 linecache.updatecache('/foo.py', globals())
1067 c = test_code('/foo.py', 'method')
1068 f = test_frame(c, globals(), {'something': 1})
1069 s = traceback.StackSummary.extract(iter([(f, 6)]))
1070 self.assertEqual(s[0].locals, None)
1071
1072 def test_format_locals(self):
1073 def some_inner(k, v):
1074 a = 1
1075 b = 2
1076 return traceback.StackSummary.extract(
1077 traceback.walk_stack(None), capture_locals=True, limit=1)
1078 s = some_inner(3, 4)
1079 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +03001080 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001081 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001082 ' a = 1\n'
1083 ' b = 2\n'
1084 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001085 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001086 ], s.format())
1087
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001088class TestTracebackException(unittest.TestCase):
1089
1090 def test_smoke(self):
1091 try:
1092 1/0
1093 except Exception:
1094 exc_info = sys.exc_info()
1095 exc = traceback.TracebackException(*exc_info)
1096 expected_stack = traceback.StackSummary.extract(
1097 traceback.walk_tb(exc_info[2]))
1098 self.assertEqual(None, exc.__cause__)
1099 self.assertEqual(None, exc.__context__)
1100 self.assertEqual(False, exc.__suppress_context__)
1101 self.assertEqual(expected_stack, exc.stack)
1102 self.assertEqual(exc_info[0], exc.exc_type)
1103 self.assertEqual(str(exc_info[1]), str(exc))
1104
1105 def test_from_exception(self):
1106 # Check all the parameters are accepted.
1107 def foo():
1108 1/0
1109 try:
1110 foo()
1111 except Exception as e:
1112 exc_info = sys.exc_info()
1113 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001114 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1115 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001116 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001117 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001118 expected_stack = self.expected_stack
1119 exc = self.exc
1120 self.assertEqual(None, exc.__cause__)
1121 self.assertEqual(None, exc.__context__)
1122 self.assertEqual(False, exc.__suppress_context__)
1123 self.assertEqual(expected_stack, exc.stack)
1124 self.assertEqual(exc_info[0], exc.exc_type)
1125 self.assertEqual(str(exc_info[1]), str(exc))
1126
1127 def test_cause(self):
1128 try:
1129 try:
1130 1/0
1131 finally:
1132 exc_info_context = sys.exc_info()
1133 exc_context = traceback.TracebackException(*exc_info_context)
1134 cause = Exception("cause")
1135 raise Exception("uh oh") from cause
1136 except Exception:
1137 exc_info = sys.exc_info()
1138 exc = traceback.TracebackException(*exc_info)
1139 expected_stack = traceback.StackSummary.extract(
1140 traceback.walk_tb(exc_info[2]))
1141 exc_cause = traceback.TracebackException(Exception, cause, None)
1142 self.assertEqual(exc_cause, exc.__cause__)
1143 self.assertEqual(exc_context, exc.__context__)
1144 self.assertEqual(True, exc.__suppress_context__)
1145 self.assertEqual(expected_stack, exc.stack)
1146 self.assertEqual(exc_info[0], exc.exc_type)
1147 self.assertEqual(str(exc_info[1]), str(exc))
1148
1149 def test_context(self):
1150 try:
1151 try:
1152 1/0
1153 finally:
1154 exc_info_context = sys.exc_info()
1155 exc_context = traceback.TracebackException(*exc_info_context)
1156 raise Exception("uh oh")
1157 except Exception:
1158 exc_info = sys.exc_info()
1159 exc = traceback.TracebackException(*exc_info)
1160 expected_stack = traceback.StackSummary.extract(
1161 traceback.walk_tb(exc_info[2]))
1162 self.assertEqual(None, exc.__cause__)
1163 self.assertEqual(exc_context, exc.__context__)
1164 self.assertEqual(False, exc.__suppress_context__)
1165 self.assertEqual(expected_stack, exc.stack)
1166 self.assertEqual(exc_info[0], exc.exc_type)
1167 self.assertEqual(str(exc_info[1]), str(exc))
1168
Irit Katriel6dfd1732021-01-12 22:14:27 +00001169 def test_long_context_chain(self):
1170 def f():
1171 try:
1172 1/0
1173 except:
1174 f()
1175
1176 try:
1177 f()
1178 except RecursionError:
1179 exc_info = sys.exc_info()
1180 else:
1181 self.fail("Exception not raised")
1182
1183 te = traceback.TracebackException(*exc_info)
1184 res = list(te.format())
1185
1186 # many ZeroDiv errors followed by the RecursionError
1187 self.assertGreater(len(res), sys.getrecursionlimit())
1188 self.assertGreater(
1189 len([l for l in res if 'ZeroDivisionError:' in l]),
1190 sys.getrecursionlimit() * 0.5)
1191 self.assertIn(
1192 "RecursionError: maximum recursion depth exceeded", res[-1])
1193
Irit Katriel4c94d742021-01-15 02:45:02 +00001194 def test_compact_with_cause(self):
1195 try:
1196 try:
1197 1/0
1198 finally:
1199 cause = Exception("cause")
1200 raise Exception("uh oh") from cause
1201 except Exception:
1202 exc_info = sys.exc_info()
1203 exc = traceback.TracebackException(*exc_info, compact=True)
1204 expected_stack = traceback.StackSummary.extract(
1205 traceback.walk_tb(exc_info[2]))
1206 exc_cause = traceback.TracebackException(Exception, cause, None)
1207 self.assertEqual(exc_cause, exc.__cause__)
1208 self.assertEqual(None, exc.__context__)
1209 self.assertEqual(True, exc.__suppress_context__)
1210 self.assertEqual(expected_stack, exc.stack)
1211 self.assertEqual(exc_info[0], exc.exc_type)
1212 self.assertEqual(str(exc_info[1]), str(exc))
1213
1214 def test_compact_no_cause(self):
1215 try:
1216 try:
1217 1/0
1218 finally:
1219 exc_info_context = sys.exc_info()
1220 exc_context = traceback.TracebackException(*exc_info_context)
1221 raise Exception("uh oh")
1222 except Exception:
1223 exc_info = sys.exc_info()
1224 exc = traceback.TracebackException(*exc_info, compact=True)
1225 expected_stack = traceback.StackSummary.extract(
1226 traceback.walk_tb(exc_info[2]))
1227 self.assertEqual(None, exc.__cause__)
1228 self.assertEqual(exc_context, exc.__context__)
1229 self.assertEqual(False, exc.__suppress_context__)
1230 self.assertEqual(expected_stack, exc.stack)
1231 self.assertEqual(exc_info[0], exc.exc_type)
1232 self.assertEqual(str(exc_info[1]), str(exc))
1233
Irit Katriel427613f2020-12-01 01:35:25 +00001234 def test_no_refs_to_exception_and_traceback_objects(self):
1235 try:
1236 1/0
1237 except Exception:
1238 exc_info = sys.exc_info()
1239
1240 refcnt1 = sys.getrefcount(exc_info[1])
1241 refcnt2 = sys.getrefcount(exc_info[2])
1242 exc = traceback.TracebackException(*exc_info)
1243 self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
1244 self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
1245
Irit Katriel44ca05a2020-11-27 16:38:54 +00001246 def test_comparison_basic(self):
Serhiy Storchaka662db122019-08-08 08:42:54 +03001247 try:
1248 1/0
1249 except Exception:
1250 exc_info = sys.exc_info()
1251 exc = traceback.TracebackException(*exc_info)
1252 exc2 = traceback.TracebackException(*exc_info)
1253 self.assertIsNot(exc, exc2)
1254 self.assertEqual(exc, exc2)
1255 self.assertNotEqual(exc, object())
1256 self.assertEqual(exc, ALWAYS_EQ)
1257
Irit Katriel44ca05a2020-11-27 16:38:54 +00001258 def test_comparison_params_variations(self):
1259 def raise_exc():
1260 try:
1261 raise ValueError('bad value')
1262 except:
1263 raise
1264
1265 def raise_with_locals():
1266 x, y = 1, 2
1267 raise_exc()
1268
1269 try:
1270 raise_with_locals()
1271 except Exception:
1272 exc_info = sys.exc_info()
1273
1274 exc = traceback.TracebackException(*exc_info)
1275 exc1 = traceback.TracebackException(*exc_info, limit=10)
1276 exc2 = traceback.TracebackException(*exc_info, limit=2)
1277
1278 self.assertEqual(exc, exc1) # limit=10 gets all frames
1279 self.assertNotEqual(exc, exc2) # limit=2 truncates the output
1280
1281 # locals change the output
1282 exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
1283 self.assertNotEqual(exc, exc3)
1284
1285 # there are no locals in the innermost frame
1286 exc4 = traceback.TracebackException(*exc_info, limit=-1)
1287 exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
1288 self.assertEqual(exc4, exc5)
1289
1290 # there are locals in the next-to-innermost frame
1291 exc6 = traceback.TracebackException(*exc_info, limit=-2)
1292 exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
1293 self.assertNotEqual(exc6, exc7)
1294
Irit Katriel427613f2020-12-01 01:35:25 +00001295 def test_comparison_equivalent_exceptions_are_equal(self):
1296 excs = []
1297 for _ in range(2):
1298 try:
1299 1/0
1300 except:
1301 excs.append(traceback.TracebackException(*sys.exc_info()))
1302 self.assertEqual(excs[0], excs[1])
1303 self.assertEqual(list(excs[0].format()), list(excs[1].format()))
1304
Zane Bitterde860732017-10-17 17:29:39 -04001305 def test_unhashable(self):
1306 class UnhashableException(Exception):
1307 def __eq__(self, other):
1308 return True
1309
1310 ex1 = UnhashableException('ex1')
1311 ex2 = UnhashableException('ex2')
1312 try:
1313 raise ex2 from ex1
1314 except UnhashableException:
1315 try:
1316 raise ex1
1317 except UnhashableException:
1318 exc_info = sys.exc_info()
1319 exc = traceback.TracebackException(*exc_info)
1320 formatted = list(exc.format())
1321 self.assertIn('UnhashableException: ex2\n', formatted[2])
1322 self.assertIn('UnhashableException: ex1\n', formatted[6])
1323
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001324 def test_limit(self):
1325 def recurse(n):
1326 if n:
1327 recurse(n-1)
1328 else:
1329 1/0
1330 try:
1331 recurse(10)
1332 except Exception:
1333 exc_info = sys.exc_info()
1334 exc = traceback.TracebackException(*exc_info, limit=5)
1335 expected_stack = traceback.StackSummary.extract(
1336 traceback.walk_tb(exc_info[2]), limit=5)
1337 self.assertEqual(expected_stack, exc.stack)
1338
1339 def test_lookup_lines(self):
1340 linecache.clearcache()
1341 e = Exception("uh oh")
1342 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001343 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001344 tb = test_tb(f, 6, None)
1345 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
Irit Katriel44ca05a2020-11-27 16:38:54 +00001346 self.assertEqual(linecache.cache, {})
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001347 linecache.updatecache('/foo.py', globals())
1348 self.assertEqual(exc.stack[0].line, "import sys")
1349
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001350 def test_locals(self):
1351 linecache.updatecache('/foo.py', globals())
1352 e = Exception("uh oh")
1353 c = test_code('/foo.py', 'method')
1354 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1355 tb = test_tb(f, 6, None)
1356 exc = traceback.TracebackException(
1357 Exception, e, tb, capture_locals=True)
1358 self.assertEqual(
1359 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1360
1361 def test_no_locals(self):
1362 linecache.updatecache('/foo.py', globals())
1363 e = Exception("uh oh")
1364 c = test_code('/foo.py', 'method')
1365 f = test_frame(c, globals(), {'something': 1})
1366 tb = test_tb(f, 6, None)
1367 exc = traceback.TracebackException(Exception, e, tb)
1368 self.assertEqual(exc.stack[0].locals, None)
1369
Berker Peksagc3f417d2015-07-24 17:36:21 +03001370 def test_traceback_header(self):
1371 # do not print a traceback header if exc_traceback is None
1372 # see issue #24695
1373 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1374 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1375
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001376
Berker Peksag716b3d32015-04-08 09:47:14 +03001377class MiscTest(unittest.TestCase):
1378
1379 def test_all(self):
1380 expected = set()
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001381 denylist = {'print_list'}
Berker Peksag716b3d32015-04-08 09:47:14 +03001382 for name in dir(traceback):
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001383 if name.startswith('_') or name in denylist:
Berker Peksag716b3d32015-04-08 09:47:14 +03001384 continue
1385 module_object = getattr(traceback, name)
1386 if getattr(module_object, '__module__', None) == 'traceback':
1387 expected.add(name)
1388 self.assertCountEqual(traceback.__all__, expected)
1389
Fred Drake2e2be372001-09-20 21:33:42 +00001390
1391if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001392 unittest.main()