blob: fabe5d01a94db04d457ca937ade5e1426564b635 [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
Miss Islington (bot)eb0a6802021-06-17 09:41:46 -07007import inspect
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00008import unittest
Benjamin Petersone6528212008-07-15 15:32:09 +00009import re
Serhiy Storchaka24559e42015-05-03 13:19:46 +030010from test import support
Hai Shideb01622020-07-06 20:29:49 +080011from test.support import Error, captured_output, cpython_only, ALWAYS_EQ
12from test.support.os_helper import TESTFN, unlink
Berker Peksagce643912015-05-06 06:33:17 +030013from test.support.script_helper import assert_python_ok
Victor Stinner9d279b82014-12-05 10:18:30 +010014import textwrap
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000015
16import traceback
17
Christian Heimes81ee3ef2008-05-04 22:42:01 +000018
Robert Collins6bc2c1e2015-03-05 12:07:57 +130019test_code = namedtuple('code', ['co_filename', 'co_name'])
Robert Collinsd7c7e0e2015-03-05 20:28:52 +130020test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals'])
Robert Collins6bc2c1e2015-03-05 12:07:57 +130021test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next'])
22
23
Martin Panterbb8b1cb2016-09-22 09:37:39 +000024class TracebackCases(unittest.TestCase):
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000025 # For now, a very minimal set of tests. I want to be sure that
26 # formatting of SyntaxErrors works based on changes for 2.1.
27
28 def get_exception_format(self, func, exc):
29 try:
30 func()
Guido van Rossumb940e112007-01-10 16:19:56 +000031 except exc as value:
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000032 return traceback.format_exception_only(exc, value)
33 else:
Collin Winter3add4d72007-08-29 23:37:32 +000034 raise ValueError("call did not raise exception")
Tim Peters7e01e282001-04-08 07:44:07 +000035
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000036 def syntax_error_with_caret(self):
37 compile("def fact(x):\n\treturn x!\n", "?", "exec")
38
Georg Brandl751899a2009-06-04 19:41:00 +000039 def syntax_error_with_caret_2(self):
40 compile("1 +\n", "?", "exec")
41
Miss Islington (bot)c7fdd682021-09-27 14:26:39 -070042 def syntax_error_with_caret_range(self):
43 compile("f(x, y for y in range(30), z)", "?", "exec")
44
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000045 def syntax_error_bad_indentation(self):
Georg Brandl88fc6642007-02-09 21:28:07 +000046 compile("def spam():\n print(1)\n print(2)", "?", "exec")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000047
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020048 def syntax_error_with_caret_non_ascii(self):
49 compile('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', "?", "exec")
50
Florent Xicluna758fa5e2014-01-22 01:11:43 +010051 def syntax_error_bad_indentation2(self):
52 compile(" print(2)", "?", "exec")
53
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000054 def test_caret(self):
55 err = self.get_exception_format(self.syntax_error_with_caret,
56 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000057 self.assertEqual(len(err), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000058 self.assertTrue(err[1].strip() == "return x!")
Benjamin Peterson577473f2010-01-19 00:09:57 +000059 self.assertIn("^", err[2]) # third line has caret
Guido van Rossume61fd5b2007-07-11 12:20:59 +000060 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place
Miss Islington (bot)c7fdd682021-09-27 14:26:39 -070061 self.assertEqual(err[2].count("^"), 1)
Tim Peters7e01e282001-04-08 07:44:07 +000062
Georg Brandl751899a2009-06-04 19:41:00 +000063 err = self.get_exception_format(self.syntax_error_with_caret_2,
64 SyntaxError)
Benjamin Peterson577473f2010-01-19 00:09:57 +000065 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010066 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070067 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Miss Islington (bot)c7fdd682021-09-27 14:26:39 -070068 self.assertEqual(err[2].count("^"), 1)
Georg Brandl751899a2009-06-04 19:41:00 +000069
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020070 err = self.get_exception_format(self.syntax_error_with_caret_non_ascii,
71 SyntaxError)
72 self.assertIn("^", err[2]) # third line has caret
Florent Xicluna758fa5e2014-01-22 01:11:43 +010073 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070074 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
Miss Islington (bot)c7fdd682021-09-27 14:26:39 -070075 self.assertEqual(err[2].count("^"), 1)
76
77 err = self.get_exception_format(self.syntax_error_with_caret_range,
78 SyntaxError)
79 self.assertIn("^", err[2]) # third line has caret
80 self.assertEqual(err[2].count('\n'), 1) # and no additional newline
81 self.assertEqual(err[1].find("y"), err[2].find("^")) # in the right place
82 self.assertEqual(err[2].count("^"), len("y for y in range(30)"))
Serhiy Storchaka65fd0592014-01-21 22:26:52 +020083
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000084 def test_nocaret(self):
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000085 exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
86 err = traceback.format_exception_only(SyntaxError, exc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000087 self.assertEqual(len(err), 3)
Benjamin Peterson26d64ae2010-09-20 21:47:37 +000088 self.assertEqual(err[1].strip(), "bad syntax")
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000089
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000090 def test_bad_indentation(self):
91 err = self.get_exception_format(self.syntax_error_bad_indentation,
92 IndentationError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000093 self.assertEqual(len(err), 4)
94 self.assertEqual(err[1].strip(), "print(2)")
Benjamin Peterson577473f2010-01-19 00:09:57 +000095 self.assertIn("^", err[2])
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070096 self.assertEqual(err[1].find(")") + 1, err[2].find("^"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000097
Guido van Rossum15bc9ab2020-05-14 19:22:48 -070098 # No caret for "unexpected indent"
Florent Xicluna758fa5e2014-01-22 01:11:43 +010099 err = self.get_exception_format(self.syntax_error_bad_indentation2,
100 IndentationError)
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700101 self.assertEqual(len(err), 3)
Florent Xicluna758fa5e2014-01-22 01:11:43 +0100102 self.assertEqual(err[1].strip(), "print(2)")
Florent Xicluna758fa5e2014-01-22 01:11:43 +0100103
Thomas Wouters477c8d52006-05-27 19:21:47 +0000104 def test_base_exception(self):
105 # Test that exceptions derived from BaseException are formatted right
106 e = KeyboardInterrupt()
107 lst = traceback.format_exception_only(e.__class__, e)
108 self.assertEqual(lst, ['KeyboardInterrupt\n'])
109
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000110 def test_format_exception_only_bad__str__(self):
111 class X(Exception):
112 def __str__(self):
113 1/0
114 err = traceback.format_exception_only(X, X())
115 self.assertEqual(len(err), 1)
116 str_value = '<unprintable %s object>' % X.__name__
Georg Brandl1a3284e2007-12-02 09:40:06 +0000117 if X.__module__ in ('__main__', 'builtins'):
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300118 str_name = X.__qualname__
Brett Cannon44c52612007-02-27 00:12:43 +0000119 else:
Serhiy Storchaka521e5862014-07-22 15:00:37 +0300120 str_name = '.'.join([X.__module__, X.__qualname__])
Brett Cannon44c52612007-02-27 00:12:43 +0000121 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000122
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000123 def test_encoded_file(self):
124 # Test that tracebacks are correctly printed for encoded source files:
125 # - correct line number (Issue2384)
126 # - respect file encoding (Issue3975)
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)90d0cfb2018-11-16 21:02:58 +0530127 import sys, subprocess
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000128
129 # The spawned subprocess has its stdout redirected to a PIPE, and its
130 # encoding may be different from the current interpreter, on Windows
131 # at least.
132 process = subprocess.Popen([sys.executable, "-c",
133 "import sys; print(sys.stdout.encoding)"],
134 stdout=subprocess.PIPE,
135 stderr=subprocess.STDOUT)
136 stdout, stderr = process.communicate()
137 output_encoding = str(stdout, 'ascii').splitlines()[0]
138
139 def do_test(firstlines, message, charset, lineno):
140 # Raise the message in a subprocess, and catch the output
141 try:
Victor Stinner51d8c522016-02-08 17:57:02 +0100142 with open(TESTFN, "w", encoding=charset) as output:
143 output.write("""{0}if 1:
144 import traceback;
145 raise RuntimeError('{1}')
146 """.format(firstlines, message))
147
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000148 process = subprocess.Popen([sys.executable, TESTFN],
149 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
150 stdout, stderr = process.communicate()
151 stdout = stdout.decode(output_encoding).splitlines()
152 finally:
153 unlink(TESTFN)
154
155 # The source lines are encoded with the 'backslashreplace' handler
156 encoded_message = message.encode(output_encoding,
157 'backslashreplace')
158 # and we just decoded them with the output_encoding.
159 message_ascii = encoded_message.decode(output_encoding)
160
161 err_line = "raise RuntimeError('{0}')".format(message_ascii)
162 err_msg = "RuntimeError: {0}".format(message_ascii)
163
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000164 self.assertIn(("line %s" % lineno), stdout[1],
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000165 "Invalid line number: {0!r} instead of {1}".format(
166 stdout[1], lineno))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000167 self.assertTrue(stdout[2].endswith(err_line),
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000168 "Invalid traceback line: {0!r} instead of {1!r}".format(
169 stdout[2], err_line))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000170 self.assertTrue(stdout[3] == err_msg,
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000171 "Invalid error message: {0!r} instead of {1!r}".format(
172 stdout[3], err_msg))
173
174 do_test("", "foo", "ascii", 3)
175 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
176 if charset == "ascii":
177 text = "foo"
178 elif charset == "GBK":
179 text = "\u4E02\u5100"
180 else:
181 text = "h\xe9 ho"
182 do_test("# coding: {0}\n".format(charset),
183 text, charset, 4)
184 do_test("#!shebang\n# coding: {0}\n".format(charset),
185 text, charset, 5)
Serhiy Storchaka1064a132014-01-09 20:12:49 +0200186 do_test(" \t\f\n# coding: {0}\n".format(charset),
187 text, charset, 5)
Martin Panter614827c2016-04-19 04:05:59 +0000188 # Issue #18960: coding spec should have no effect
Victor Stinner51d8c522016-02-08 17:57:02 +0100189 do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000190
Victor Stinner9d279b82014-12-05 10:18:30 +0100191 def test_print_traceback_at_exit(self):
192 # Issue #22599: Ensure that it is possible to use the traceback module
193 # to display an exception at Python exit
194 code = textwrap.dedent("""
195 import sys
196 import traceback
197
198 class PrintExceptionAtExit(object):
199 def __init__(self):
200 try:
201 x = 1 / 0
202 except Exception:
203 self.exc_info = sys.exc_info()
204 # self.exc_info[1] (traceback) contains frames:
205 # explicitly clear the reference to self in the current
206 # frame to break a reference cycle
207 self = None
208
209 def __del__(self):
210 traceback.print_exception(*self.exc_info)
211
212 # Keep a reference in the module namespace to call the destructor
213 # when the module is unloaded
214 obj = PrintExceptionAtExit()
215 """)
216 rc, stdout, stderr = assert_python_ok('-c', code)
217 expected = [b'Traceback (most recent call last):',
218 b' File "<string>", line 8, in __init__',
219 b'ZeroDivisionError: division by zero']
220 self.assertEqual(stderr.splitlines(), expected)
221
Berker Peksagc3f417d2015-07-24 17:36:21 +0300222 def test_print_exception(self):
223 output = StringIO()
224 traceback.print_exception(
225 Exception, Exception("projector"), None, file=output
226 )
227 self.assertEqual(output.getvalue(), "Exception: projector\n")
228
Zackery Spytz91e93792020-11-05 15:18:44 -0700229 def test_print_exception_exc(self):
230 output = StringIO()
231 traceback.print_exception(Exception("projector"), file=output)
232 self.assertEqual(output.getvalue(), "Exception: projector\n")
233
234 def test_format_exception_exc(self):
235 e = Exception("projector")
236 output = traceback.format_exception(e)
237 self.assertEqual(output, ["Exception: projector\n"])
238 with self.assertRaisesRegex(ValueError, 'Both or neither'):
239 traceback.format_exception(e.__class__, e)
240 with self.assertRaisesRegex(ValueError, 'Both or neither'):
241 traceback.format_exception(e.__class__, tb=e.__traceback__)
242 with self.assertRaisesRegex(TypeError, 'positional-only'):
243 traceback.format_exception(exc=e)
244
245 def test_format_exception_only_exc(self):
246 output = traceback.format_exception_only(Exception("projector"))
247 self.assertEqual(output, ["Exception: projector\n"])
248
Irit Katriel26f18b82021-02-23 14:58:47 +0000249 def test_exception_is_None(self):
250 NONE_EXC_STRING = 'NoneType: None\n'
251 excfile = StringIO()
Irit Katrielb798ab02021-02-23 17:43:04 +0000252 traceback.print_exception(None, file=excfile)
253 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
254
255 excfile = StringIO()
Irit Katriel26f18b82021-02-23 14:58:47 +0000256 traceback.print_exception(None, None, None, file=excfile)
257 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
258
259 excfile = StringIO()
260 traceback.print_exc(None, file=excfile)
261 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING)
262
263 self.assertEqual(traceback.format_exc(None), NONE_EXC_STRING)
Irit Katrielb798ab02021-02-23 17:43:04 +0000264 self.assertEqual(traceback.format_exception(None), [NONE_EXC_STRING])
Irit Katriel26f18b82021-02-23 14:58:47 +0000265 self.assertEqual(
266 traceback.format_exception(None, None, None), [NONE_EXC_STRING])
267 self.assertEqual(
268 traceback.format_exception_only(None), [NONE_EXC_STRING])
269 self.assertEqual(
270 traceback.format_exception_only(None, None), [NONE_EXC_STRING])
271
Miss Islington (bot)eb0a6802021-06-17 09:41:46 -0700272 def test_signatures(self):
273 self.assertEqual(
274 str(inspect.signature(traceback.print_exception)),
275 ('(exc, /, value=<implicit>, tb=<implicit>, '
276 'limit=None, file=None, chain=True)'))
277
278 self.assertEqual(
279 str(inspect.signature(traceback.format_exception)),
280 ('(exc, /, value=<implicit>, tb=<implicit>, limit=None, '
281 'chain=True)'))
282
283 self.assertEqual(
284 str(inspect.signature(traceback.format_exception_only)),
285 '(exc, /, value=<implicit>)')
286
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000287
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000288class TracebackFormatTests(unittest.TestCase):
289
Antoine Pitrou58720d62013-08-05 23:26:40 +0200290 def some_exception(self):
291 raise KeyError('blah')
292
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200293 @cpython_only
Antoine Pitrou58720d62013-08-05 23:26:40 +0200294 def check_traceback_format(self, cleanup_func=None):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200295 from _testcapi import traceback_print
Georg Brandl236f7972009-04-05 14:28:42 +0000296 try:
Antoine Pitrou58720d62013-08-05 23:26:40 +0200297 self.some_exception()
Georg Brandl236f7972009-04-05 14:28:42 +0000298 except KeyError:
299 type_, value, tb = sys.exc_info()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200300 if cleanup_func is not None:
301 # Clear the inner frames, not this one
302 cleanup_func(tb.tb_next)
Georg Brandl236f7972009-04-05 14:28:42 +0000303 traceback_fmt = 'Traceback (most recent call last):\n' + \
304 ''.join(traceback.format_tb(tb))
305 file_ = StringIO()
306 traceback_print(tb, file_)
307 python_fmt = file_.getvalue()
Benjamin Petersond9fec152013-04-29 16:09:39 -0400308 # Call all _tb and _exc functions
309 with captured_output("stderr") as tbstderr:
310 traceback.print_tb(tb)
311 tbfile = StringIO()
312 traceback.print_tb(tb, file=tbfile)
313 with captured_output("stderr") as excstderr:
314 traceback.print_exc()
315 excfmt = traceback.format_exc()
316 excfile = StringIO()
317 traceback.print_exc(file=excfile)
Georg Brandl236f7972009-04-05 14:28:42 +0000318 else:
319 raise Error("unable to create test traceback string")
320
321 # Make sure that Python and the traceback module format the same thing
Ezio Melottib3aedd42010-11-20 19:04:17 +0000322 self.assertEqual(traceback_fmt, python_fmt)
Benjamin Petersond9fec152013-04-29 16:09:39 -0400323 # Now verify the _tb func output
324 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
325 # Now verify the _exc func output
326 self.assertEqual(excstderr.getvalue(), excfile.getvalue())
327 self.assertEqual(excfmt, excfile.getvalue())
Georg Brandl236f7972009-04-05 14:28:42 +0000328
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000329 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000330 tb_lines = python_fmt.splitlines()
Antoine Pitrou58720d62013-08-05 23:26:40 +0200331 self.assertEqual(len(tb_lines), 5)
332 banner = tb_lines[0]
333 location, source_line = tb_lines[-2:]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000334 self.assertTrue(banner.startswith('Traceback'))
335 self.assertTrue(location.startswith(' File'))
336 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000337
Antoine Pitrou58720d62013-08-05 23:26:40 +0200338 def test_traceback_format(self):
339 self.check_traceback_format()
340
341 def test_traceback_format_with_cleared_frames(self):
342 # Check that traceback formatting also works with a clear()ed frame
343 def cleanup_tb(tb):
344 tb.tb_frame.clear()
345 self.check_traceback_format(cleanup_tb)
346
Benjamin Petersond9fec152013-04-29 16:09:39 -0400347 def test_stack_format(self):
348 # Verify _stack functions. Note we have to use _getframe(1) to
349 # compare them without this frame appearing in the output
350 with captured_output("stderr") as ststderr:
351 traceback.print_stack(sys._getframe(1))
352 stfile = StringIO()
353 traceback.print_stack(sys._getframe(1), file=stfile)
354 self.assertEqual(ststderr.getvalue(), stfile.getvalue())
355
356 stfmt = traceback.format_stack(sys._getframe(1))
357
358 self.assertEqual(ststderr.getvalue(), "".join(stfmt))
359
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300360 def test_print_stack(self):
361 def prn():
362 traceback.print_stack()
363 with captured_output("stderr") as stderr:
364 prn()
365 lineno = prn.__code__.co_firstlineno
366 self.assertEqual(stderr.getvalue().splitlines()[-4:], [
367 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3),
368 ' prn()',
369 ' File "%s", line %d, in prn' % (__file__, lineno+1),
370 ' traceback.print_stack()',
371 ])
372
Nick Coghland0034232016-08-15 13:11:34 +1000373 # issue 26823 - Shrink recursive tracebacks
374 def _check_recursive_traceback_display(self, render_exc):
375 # Always show full diffs when this test fails
376 # Note that rearranging things may require adjusting
377 # the relative line numbers in the expected tracebacks
378 self.maxDiff = None
379
380 # Check hitting the recursion limit
381 def f():
382 f()
383
384 with captured_output("stderr") as stderr_f:
385 try:
386 f()
Pablo Galindo293dd232019-11-19 21:34:03 +0000387 except RecursionError:
Nick Coghland0034232016-08-15 13:11:34 +1000388 render_exc()
389 else:
390 self.fail("no recursion occurred")
391
392 lineno_f = f.__code__.co_firstlineno
393 result_f = (
394 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400395 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000396 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400397 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000398 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400399 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000400 ' f()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400401 f' File "{__file__}", line {lineno_f+1}, in f\n'
Nick Coghland0034232016-08-15 13:11:34 +1000402 ' f()\n'
403 # XXX: The following line changes depending on whether the tests
404 # are run through the interactive interpreter or with -m
405 # It also varies depending on the platform (stack size)
406 # Fortunately, we don't care about exactness here, so we use regex
407 r' \[Previous line repeated (\d+) more times\]' '\n'
408 'RecursionError: maximum recursion depth exceeded\n'
409 )
410
411 expected = result_f.splitlines()
412 actual = stderr_f.getvalue().splitlines()
413
414 # Check the output text matches expectations
415 # 2nd last line contains the repetition count
416 self.assertEqual(actual[:-2], expected[:-2])
417 self.assertRegex(actual[-2], expected[-2])
codedragon3480ef92017-05-24 14:23:46 -0700418 # last line can have additional text appended
419 self.assertIn(expected[-1], actual[-1])
Nick Coghland0034232016-08-15 13:11:34 +1000420
421 # Check the recursion count is roughly as expected
422 rec_limit = sys.getrecursionlimit()
Serhiy Storchakab7281052016-09-12 00:52:40 +0300423 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
Nick Coghland0034232016-08-15 13:11:34 +1000424
425 # Check a known (limited) number of recursive invocations
426 def g(count=10):
427 if count:
428 return g(count-1)
429 raise ValueError
430
431 with captured_output("stderr") as stderr_g:
432 try:
433 g()
Pablo Galindo293dd232019-11-19 21:34:03 +0000434 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000435 render_exc()
436 else:
437 self.fail("no value error was raised")
438
439 lineno_g = g.__code__.co_firstlineno
440 result_g = (
Eric V. Smith451d0e32016-09-09 21:56:20 -0400441 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000442 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400443 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000444 ' return g(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400445 f' File "{__file__}", line {lineno_g+2}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000446 ' return g(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700447 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400448 f' File "{__file__}", line {lineno_g+3}, in g\n'
Nick Coghland0034232016-08-15 13:11:34 +1000449 ' raise ValueError\n'
450 'ValueError\n'
451 )
452 tb_line = (
453 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400454 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000455 ' g()\n'
456 )
457 expected = (tb_line + result_g).splitlines()
458 actual = stderr_g.getvalue().splitlines()
459 self.assertEqual(actual, expected)
460
461 # Check 2 different repetitive sections
462 def h(count=10):
463 if count:
464 return h(count-1)
465 g()
466
467 with captured_output("stderr") as stderr_h:
468 try:
469 h()
Pablo Galindo293dd232019-11-19 21:34:03 +0000470 except ValueError:
Nick Coghland0034232016-08-15 13:11:34 +1000471 render_exc()
472 else:
473 self.fail("no value error was raised")
474
475 lineno_h = h.__code__.co_firstlineno
476 result_h = (
477 'Traceback (most recent call last):\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400478 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
Nick Coghland0034232016-08-15 13:11:34 +1000479 ' h()\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400480 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000481 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400482 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000483 ' return h(count-1)\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400484 f' File "{__file__}", line {lineno_h+2}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000485 ' return h(count-1)\n'
Benjamin Petersond5458692018-09-10 08:43:10 -0700486 ' [Previous line repeated 7 more times]\n'
Eric V. Smith451d0e32016-09-09 21:56:20 -0400487 f' File "{__file__}", line {lineno_h+3}, in h\n'
Nick Coghland0034232016-08-15 13:11:34 +1000488 ' g()\n'
489 )
490 expected = (result_h + result_g).splitlines()
491 actual = stderr_h.getvalue().splitlines()
492 self.assertEqual(actual, expected)
493
Benjamin Petersond5458692018-09-10 08:43:10 -0700494 # Check the boundary conditions. First, test just below the cutoff.
495 with captured_output("stderr") as stderr_g:
496 try:
497 g(traceback._RECURSIVE_CUTOFF)
Pablo Galindo293dd232019-11-19 21:34:03 +0000498 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700499 render_exc()
500 else:
501 self.fail("no error raised")
502 result_g = (
503 f' File "{__file__}", line {lineno_g+2}, in g\n'
504 ' return g(count-1)\n'
505 f' File "{__file__}", line {lineno_g+2}, in g\n'
506 ' return g(count-1)\n'
507 f' File "{__file__}", line {lineno_g+2}, in g\n'
508 ' return g(count-1)\n'
509 f' File "{__file__}", line {lineno_g+3}, in g\n'
510 ' raise ValueError\n'
511 'ValueError\n'
512 )
513 tb_line = (
514 'Traceback (most recent call last):\n'
515 f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n'
516 ' g(traceback._RECURSIVE_CUTOFF)\n'
517 )
518 expected = (tb_line + result_g).splitlines()
519 actual = stderr_g.getvalue().splitlines()
520 self.assertEqual(actual, expected)
521
522 # Second, test just above the cutoff.
523 with captured_output("stderr") as stderr_g:
524 try:
525 g(traceback._RECURSIVE_CUTOFF + 1)
Pablo Galindo293dd232019-11-19 21:34:03 +0000526 except ValueError:
Benjamin Petersond5458692018-09-10 08:43:10 -0700527 render_exc()
528 else:
529 self.fail("no error raised")
530 result_g = (
531 f' File "{__file__}", line {lineno_g+2}, in g\n'
532 ' return g(count-1)\n'
533 f' File "{__file__}", line {lineno_g+2}, in g\n'
534 ' return g(count-1)\n'
535 f' File "{__file__}", line {lineno_g+2}, in g\n'
536 ' return g(count-1)\n'
537 ' [Previous line repeated 1 more time]\n'
538 f' File "{__file__}", line {lineno_g+3}, in g\n'
539 ' raise ValueError\n'
540 'ValueError\n'
541 )
542 tb_line = (
543 'Traceback (most recent call last):\n'
544 f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n'
545 ' g(traceback._RECURSIVE_CUTOFF + 1)\n'
546 )
547 expected = (tb_line + result_g).splitlines()
548 actual = stderr_g.getvalue().splitlines()
549 self.assertEqual(actual, expected)
550
Nick Coghland0034232016-08-15 13:11:34 +1000551 def test_recursive_traceback_python(self):
552 self._check_recursive_traceback_display(traceback.print_exc)
553
554 @cpython_only
555 def test_recursive_traceback_cpython_internal(self):
556 from _testcapi import exception_print
557 def render_exc():
558 exc_type, exc_value, exc_tb = sys.exc_info()
559 exception_print(exc_value)
560 self._check_recursive_traceback_display(render_exc)
561
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300562 def test_format_stack(self):
563 def fmt():
564 return traceback.format_stack()
565 result = fmt()
566 lineno = fmt.__code__.co_firstlineno
567 self.assertEqual(result[-2:], [
568 ' File "%s", line %d, in test_format_stack\n'
569 ' result = fmt()\n' % (__file__, lineno+2),
570 ' File "%s", line %d, in fmt\n'
571 ' return traceback.format_stack()\n' % (__file__, lineno+1),
572 ])
573
Zane Bitterde860732017-10-17 17:29:39 -0400574 @cpython_only
575 def test_unhashable(self):
576 from _testcapi import exception_print
577
578 class UnhashableException(Exception):
579 def __eq__(self, other):
580 return True
581
582 ex1 = UnhashableException('ex1')
583 ex2 = UnhashableException('ex2')
584 try:
585 raise ex2 from ex1
586 except UnhashableException:
587 try:
588 raise ex1
589 except UnhashableException:
590 exc_type, exc_val, exc_tb = sys.exc_info()
591
592 with captured_output("stderr") as stderr_f:
593 exception_print(exc_val)
594
595 tb = stderr_f.getvalue().strip().splitlines()
596 self.assertEqual(11, len(tb))
597 self.assertEqual(context_message.strip(), tb[5])
598 self.assertIn('UnhashableException: ex2', tb[3])
599 self.assertIn('UnhashableException: ex1', tb[10])
600
Benjamin Petersone6528212008-07-15 15:32:09 +0000601
602cause_message = (
603 "\nThe above exception was the direct cause "
604 "of the following exception:\n\n")
605
606context_message = (
607 "\nDuring handling of the above exception, "
608 "another exception occurred:\n\n")
609
610boundaries = re.compile(
611 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
612
613
614class BaseExceptionReportingTests:
615
616 def get_exception(self, exception_or_callable):
617 if isinstance(exception_or_callable, Exception):
618 return exception_or_callable
619 try:
620 exception_or_callable()
621 except Exception as e:
622 return e
623
624 def zero_div(self):
625 1/0 # In zero_div
626
627 def check_zero_div(self, msg):
628 lines = msg.splitlines()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000629 self.assertTrue(lines[-3].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000630 self.assertIn('1/0 # In zero_div', lines[-2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000631 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000632
633 def test_simple(self):
634 try:
635 1/0 # Marker
636 except ZeroDivisionError as _:
637 e = _
638 lines = self.get_report(e).splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000639 self.assertEqual(len(lines), 4)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000640 self.assertTrue(lines[0].startswith('Traceback'))
641 self.assertTrue(lines[1].startswith(' File'))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000642 self.assertIn('1/0 # Marker', lines[2])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000643 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000644
645 def test_cause(self):
646 def inner_raise():
647 try:
648 self.zero_div()
649 except ZeroDivisionError as e:
650 raise KeyError from e
651 def outer_raise():
652 inner_raise() # Marker
653 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000654 self.assertEqual(len(blocks), 3)
655 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000656 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000657 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000658
659 def test_context(self):
660 def inner_raise():
661 try:
662 self.zero_div()
663 except ZeroDivisionError:
664 raise KeyError
665 def outer_raise():
666 inner_raise() # Marker
667 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000668 self.assertEqual(len(blocks), 3)
669 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000670 self.check_zero_div(blocks[0])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000671 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000672
Nick Coghlanab7bf212012-02-26 17:49:52 +1000673 def test_context_suppression(self):
674 try:
675 try:
676 raise Exception
677 except:
678 raise ZeroDivisionError from None
679 except ZeroDivisionError as _:
680 e = _
681 lines = self.get_report(e).splitlines()
682 self.assertEqual(len(lines), 4)
683 self.assertTrue(lines[0].startswith('Traceback'))
684 self.assertTrue(lines[1].startswith(' File'))
685 self.assertIn('ZeroDivisionError from None', lines[2])
686 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
687
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000688 def test_cause_and_context(self):
689 # When both a cause and a context are set, only the cause should be
690 # displayed and the context should be muted.
691 def inner_raise():
692 try:
693 self.zero_div()
694 except ZeroDivisionError as _e:
695 e = _e
696 try:
697 xyzzy
698 except NameError:
699 raise KeyError from e
700 def outer_raise():
701 inner_raise() # Marker
702 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000703 self.assertEqual(len(blocks), 3)
704 self.assertEqual(blocks[1], cause_message)
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000705 self.check_zero_div(blocks[0])
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000706 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitrou7b0d4a22009-11-28 16:12:28 +0000707
Benjamin Petersone6528212008-07-15 15:32:09 +0000708 def test_cause_recursive(self):
709 def inner_raise():
710 try:
711 try:
712 self.zero_div()
713 except ZeroDivisionError as e:
714 z = e
715 raise KeyError from e
716 except KeyError as e:
717 raise z from e
718 def outer_raise():
719 inner_raise() # Marker
720 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melottib3aedd42010-11-20 19:04:17 +0000721 self.assertEqual(len(blocks), 3)
722 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000723 # The first block is the KeyError raised from the ZeroDivisionError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000724 self.assertIn('raise KeyError from e', blocks[0])
725 self.assertNotIn('1/0', blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000726 # The second block (apart from the boundary) is the ZeroDivisionError
727 # re-raised from the KeyError
Benjamin Peterson577473f2010-01-19 00:09:57 +0000728 self.assertIn('inner_raise() # Marker', blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000729 self.check_zero_div(blocks[2])
730
Benjamin Peterson503d6c52010-10-24 02:52:05 +0000731 def test_syntax_error_offset_at_eol(self):
732 # See #10186.
733 def e():
734 raise SyntaxError('', ('', 0, 5, 'hello'))
735 msg = self.get_report(e).splitlines()
736 self.assertEqual(msg[-2], " ^")
Benjamin Petersona95e9772010-10-29 03:28:14 +0000737 def e():
738 exec("x = 5 | 4 |")
739 msg = self.get_report(e).splitlines()
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700740 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000741
Irit Katriel069560b2020-12-22 19:53:09 +0000742 def test_syntax_error_no_lineno(self):
743 # See #34463.
744
745 # Without filename
746 e = SyntaxError('bad syntax')
747 msg = self.get_report(e).splitlines()
748 self.assertEqual(msg,
749 ['SyntaxError: bad syntax'])
750 e.lineno = 100
751 msg = self.get_report(e).splitlines()
752 self.assertEqual(msg,
753 [' File "<string>", line 100', 'SyntaxError: bad syntax'])
754
755 # With filename
756 e = SyntaxError('bad syntax')
757 e.filename = 'myfile.py'
758
759 msg = self.get_report(e).splitlines()
760 self.assertEqual(msg,
761 ['SyntaxError: bad syntax (myfile.py)'])
762 e.lineno = 100
763 msg = self.get_report(e).splitlines()
764 self.assertEqual(msg,
765 [' File "myfile.py", line 100', 'SyntaxError: bad syntax'])
766
Martin Panterbb8b1cb2016-09-22 09:37:39 +0000767 def test_message_none(self):
768 # A message that looks like "None" should not be treated specially
769 err = self.get_report(Exception(None))
770 self.assertIn('Exception: None\n', err)
771 err = self.get_report(Exception('None'))
772 self.assertIn('Exception: None\n', err)
773 err = self.get_report(Exception())
774 self.assertIn('Exception\n', err)
775 err = self.get_report(Exception(''))
776 self.assertIn('Exception\n', err)
777
Guido van Rossum15bc9ab2020-05-14 19:22:48 -0700778 def test_syntax_error_various_offsets(self):
779 for offset in range(-5, 10):
780 for add in [0, 2]:
781 text = " "*add + "text%d" % offset
782 expected = [' File "file.py", line 1']
783 if offset < 1:
784 expected.append(" %s" % text.lstrip())
785 elif offset <= 6:
786 expected.append(" %s" % text.lstrip())
787 expected.append(" %s^" % (" "*(offset-1)))
788 else:
789 expected.append(" %s" % text.lstrip())
790 expected.append(" %s^" % (" "*5))
791 expected.append("SyntaxError: msg")
792 expected.append("")
793 err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text)))
794 exp = "\n".join(expected)
795 self.assertEqual(exp, err)
796
Miss Islington (bot)6b996d62021-09-08 09:32:19 -0700797 def test_format_exception_only_qualname(self):
798 class A:
799 class B:
800 class X(Exception):
801 def __str__(self):
802 return "I am X"
803 pass
804 err = self.get_report(A.B.X())
805 str_value = 'I am X'
806 str_name = '.'.join([A.B.X.__module__, A.B.X.__qualname__])
807 exp = "%s: %s\n" % (str_name, str_value)
808 self.assertEqual(exp, err)
809
Benjamin Petersone6528212008-07-15 15:32:09 +0000810
811class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
812 #
813 # This checks reporting through the 'traceback' module, with both
814 # format_exception() and print_exception().
815 #
816
817 def get_report(self, e):
818 e = self.get_exception(e)
819 s = ''.join(
820 traceback.format_exception(type(e), e, e.__traceback__))
821 with captured_output("stderr") as sio:
822 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000823 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000824 return s
825
826
827class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
828 #
829 # This checks built-in reporting by the interpreter.
830 #
831
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200832 @cpython_only
Benjamin Petersone6528212008-07-15 15:32:09 +0000833 def get_report(self, e):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200834 from _testcapi import exception_print
Benjamin Petersone6528212008-07-15 15:32:09 +0000835 e = self.get_exception(e)
836 with captured_output("stderr") as s:
837 exception_print(e)
838 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000839
840
Serhiy Storchaka24559e42015-05-03 13:19:46 +0300841class LimitTests(unittest.TestCase):
842
843 ''' Tests for limit argument.
844 It's enough to test extact_tb, extract_stack and format_exception '''
845
846 def last_raises1(self):
847 raise Exception('Last raised')
848
849 def last_raises2(self):
850 self.last_raises1()
851
852 def last_raises3(self):
853 self.last_raises2()
854
855 def last_raises4(self):
856 self.last_raises3()
857
858 def last_raises5(self):
859 self.last_raises4()
860
861 def last_returns_frame1(self):
862 return sys._getframe()
863
864 def last_returns_frame2(self):
865 return self.last_returns_frame1()
866
867 def last_returns_frame3(self):
868 return self.last_returns_frame2()
869
870 def last_returns_frame4(self):
871 return self.last_returns_frame3()
872
873 def last_returns_frame5(self):
874 return self.last_returns_frame4()
875
876 def test_extract_stack(self):
877 frame = self.last_returns_frame5()
878 def extract(**kwargs):
879 return traceback.extract_stack(frame, **kwargs)
880 def assertEqualExcept(actual, expected, ignore):
881 self.assertEqual(actual[:ignore], expected[:ignore])
882 self.assertEqual(actual[ignore+1:], expected[ignore+1:])
883 self.assertEqual(len(actual), len(expected))
884
885 with support.swap_attr(sys, 'tracebacklimit', 1000):
886 nolim = extract()
887 self.assertGreater(len(nolim), 5)
888 self.assertEqual(extract(limit=2), nolim[-2:])
889 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
890 self.assertEqual(extract(limit=-2), nolim[:2])
891 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
892 self.assertEqual(extract(limit=0), [])
893 del sys.tracebacklimit
894 assertEqualExcept(extract(), nolim, -5-1)
895 sys.tracebacklimit = 2
896 self.assertEqual(extract(), nolim[-2:])
897 self.assertEqual(extract(limit=3), nolim[-3:])
898 self.assertEqual(extract(limit=-3), nolim[:3])
899 sys.tracebacklimit = 0
900 self.assertEqual(extract(), [])
901 sys.tracebacklimit = -1
902 self.assertEqual(extract(), [])
903
904 def test_extract_tb(self):
905 try:
906 self.last_raises5()
907 except Exception:
908 exc_type, exc_value, tb = sys.exc_info()
909 def extract(**kwargs):
910 return traceback.extract_tb(tb, **kwargs)
911
912 with support.swap_attr(sys, 'tracebacklimit', 1000):
913 nolim = extract()
914 self.assertEqual(len(nolim), 5+1)
915 self.assertEqual(extract(limit=2), nolim[:2])
916 self.assertEqual(extract(limit=10), nolim)
917 self.assertEqual(extract(limit=-2), nolim[-2:])
918 self.assertEqual(extract(limit=-10), nolim)
919 self.assertEqual(extract(limit=0), [])
920 del sys.tracebacklimit
921 self.assertEqual(extract(), nolim)
922 sys.tracebacklimit = 2
923 self.assertEqual(extract(), nolim[:2])
924 self.assertEqual(extract(limit=3), nolim[:3])
925 self.assertEqual(extract(limit=-3), nolim[-3:])
926 sys.tracebacklimit = 0
927 self.assertEqual(extract(), [])
928 sys.tracebacklimit = -1
929 self.assertEqual(extract(), [])
930
931 def test_format_exception(self):
932 try:
933 self.last_raises5()
934 except Exception:
935 exc_type, exc_value, tb = sys.exc_info()
936 # [1:-1] to exclude "Traceback (...)" header and
937 # exception type and value
938 def extract(**kwargs):
939 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
940
941 with support.swap_attr(sys, 'tracebacklimit', 1000):
942 nolim = extract()
943 self.assertEqual(len(nolim), 5+1)
944 self.assertEqual(extract(limit=2), nolim[:2])
945 self.assertEqual(extract(limit=10), nolim)
946 self.assertEqual(extract(limit=-2), nolim[-2:])
947 self.assertEqual(extract(limit=-10), nolim)
948 self.assertEqual(extract(limit=0), [])
949 del sys.tracebacklimit
950 self.assertEqual(extract(), nolim)
951 sys.tracebacklimit = 2
952 self.assertEqual(extract(), nolim[:2])
953 self.assertEqual(extract(limit=3), nolim[:3])
954 self.assertEqual(extract(limit=-3), nolim[-3:])
955 sys.tracebacklimit = 0
956 self.assertEqual(extract(), [])
957 sys.tracebacklimit = -1
958 self.assertEqual(extract(), [])
959
960
Andrew Kuchling173a1572013-09-15 18:15:56 -0400961class MiscTracebackCases(unittest.TestCase):
962 #
963 # Check non-printing functions in traceback module
964 #
965
966 def test_clear(self):
967 def outer():
968 middle()
969 def middle():
970 inner()
971 def inner():
972 i = 1
973 1/0
974
975 try:
976 outer()
977 except:
978 type_, value, tb = sys.exc_info()
979
980 # Initial assertion: there's one local in the inner frame.
981 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
982 self.assertEqual(len(inner_frame.f_locals), 1)
983
984 # Clear traceback frames
985 traceback.clear_frames(tb)
986
987 # Local variable dict should now be empty.
988 self.assertEqual(len(inner_frame.f_locals), 0)
989
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300990 def test_extract_stack(self):
991 def extract():
992 return traceback.extract_stack()
993 result = extract()
994 lineno = extract.__code__.co_firstlineno
Serhiy Storchaka3066fc42015-09-29 22:33:36 +0300995 self.assertEqual(result[-2:], [
Serhiy Storchakae953ba72015-09-18 10:04:47 +0300996 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
997 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
998 ])
Berker Peksag9797b7a2018-09-10 20:02:33 +0300999 self.assertEqual(len(result[0]), 4)
Serhiy Storchakae953ba72015-09-18 10:04:47 +03001000
Andrew Kuchling173a1572013-09-15 18:15:56 -04001001
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001002class TestFrame(unittest.TestCase):
1003
1004 def test_basics(self):
1005 linecache.clearcache()
1006 linecache.lazycache("f", globals())
1007 f = traceback.FrameSummary("f", 1, "dummy")
Serhiy Storchaka3066fc42015-09-29 22:33:36 +03001008 self.assertEqual(f,
1009 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
1010 self.assertEqual(tuple(f),
1011 ("f", 1, "dummy", '"""Test cases for traceback module"""'))
1012 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
1013 self.assertEqual(f, tuple(f))
1014 # Since tuple.__eq__ doesn't support FrameSummary, the equality
1015 # operator fallbacks to FrameSummary.__eq__.
1016 self.assertEqual(tuple(f), f)
1017 self.assertIsNone(f.locals)
Serhiy Storchaka662db122019-08-08 08:42:54 +03001018 self.assertNotEqual(f, object())
1019 self.assertEqual(f, ALWAYS_EQ)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001020
1021 def test_lazy_lines(self):
1022 linecache.clearcache()
1023 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
1024 self.assertEqual(None, f._line)
1025 linecache.lazycache("f", globals())
1026 self.assertEqual(
1027 '"""Test cases for traceback module"""',
1028 f.line)
1029
Pablo Galindo61eb9b52021-07-08 17:47:12 +01001030 def test_no_line(self):
1031 f = traceback.FrameSummary("f", None, "dummy")
1032 self.assertEqual(f.line, None)
1033
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001034 def test_explicit_line(self):
1035 f = traceback.FrameSummary("f", 1, "dummy", line="line")
1036 self.assertEqual("line", f.line)
1037
Berker Peksag9797b7a2018-09-10 20:02:33 +03001038 def test_len(self):
1039 f = traceback.FrameSummary("f", 1, "dummy", line="line")
1040 self.assertEqual(len(f), 4)
1041
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001042
1043class TestStack(unittest.TestCase):
1044
1045 def test_walk_stack(self):
Serhiy Storchaka1c1130f2016-10-07 23:45:42 +03001046 def deeper():
1047 return list(traceback.walk_stack(None))
1048 s1 = list(traceback.walk_stack(None))
1049 s2 = deeper()
1050 self.assertEqual(len(s2) - len(s1), 1)
1051 self.assertEqual(s2[1:], s1)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001052
1053 def test_walk_tb(self):
1054 try:
1055 1/0
1056 except Exception:
1057 _, _, tb = sys.exc_info()
1058 s = list(traceback.walk_tb(tb))
1059 self.assertEqual(len(s), 1)
1060
1061 def test_extract_stack(self):
1062 s = traceback.StackSummary.extract(traceback.walk_stack(None))
1063 self.assertIsInstance(s, traceback.StackSummary)
1064
1065 def test_extract_stack_limit(self):
1066 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
1067 self.assertEqual(len(s), 5)
1068
1069 def test_extract_stack_lookup_lines(self):
1070 linecache.clearcache()
1071 linecache.updatecache('/foo.py', globals())
1072 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001073 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001074 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
1075 linecache.clearcache()
1076 self.assertEqual(s[0].line, "import sys")
1077
1078 def test_extract_stackup_deferred_lookup_lines(self):
1079 linecache.clearcache()
1080 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001081 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001082 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
1083 self.assertEqual({}, linecache.cache)
1084 linecache.updatecache('/foo.py', globals())
1085 self.assertEqual(s[0].line, "import sys")
1086
1087 def test_from_list(self):
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001088 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001089 self.assertEqual(
1090 [' File "foo.py", line 1, in fred\n line\n'],
1091 s.format())
1092
Robert Collinsbbb8ade2015-03-16 15:27:16 +13001093 def test_from_list_edited_stack(self):
1094 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
1095 s[0] = ('foo.py', 2, 'fred', 'line')
1096 s2 = traceback.StackSummary.from_list(s)
1097 self.assertEqual(
1098 [' File "foo.py", line 2, in fred\n line\n'],
1099 s2.format())
1100
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001101 def test_format_smoke(self):
1102 # For detailed tests see the format_list tests, which consume the same
1103 # code.
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001104 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001105 self.assertEqual(
1106 [' File "foo.py", line 1, in fred\n line\n'],
1107 s.format())
1108
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001109 def test_locals(self):
1110 linecache.updatecache('/foo.py', globals())
1111 c = test_code('/foo.py', 'method')
1112 f = test_frame(c, globals(), {'something': 1})
1113 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
1114 self.assertEqual(s[0].locals, {'something': '1'})
1115
1116 def test_no_locals(self):
1117 linecache.updatecache('/foo.py', globals())
1118 c = test_code('/foo.py', 'method')
1119 f = test_frame(c, globals(), {'something': 1})
1120 s = traceback.StackSummary.extract(iter([(f, 6)]))
1121 self.assertEqual(s[0].locals, None)
1122
1123 def test_format_locals(self):
1124 def some_inner(k, v):
1125 a = 1
1126 b = 2
1127 return traceback.StackSummary.extract(
1128 traceback.walk_stack(None), capture_locals=True, limit=1)
1129 s = some_inner(3, 4)
1130 self.assertEqual(
Serhiy Storchaka24559e42015-05-03 13:19:46 +03001131 [' File "%s", line %d, in some_inner\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001132 ' return traceback.StackSummary.extract(\n'
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001133 ' a = 1\n'
1134 ' b = 2\n'
1135 ' k = 3\n'
Serhiy Storchakada8d72c2018-09-17 15:17:29 +03001136 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001137 ], s.format())
1138
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001139class TestTracebackException(unittest.TestCase):
1140
1141 def test_smoke(self):
1142 try:
1143 1/0
1144 except Exception:
1145 exc_info = sys.exc_info()
1146 exc = traceback.TracebackException(*exc_info)
1147 expected_stack = traceback.StackSummary.extract(
1148 traceback.walk_tb(exc_info[2]))
1149 self.assertEqual(None, exc.__cause__)
1150 self.assertEqual(None, exc.__context__)
1151 self.assertEqual(False, exc.__suppress_context__)
1152 self.assertEqual(expected_stack, exc.stack)
1153 self.assertEqual(exc_info[0], exc.exc_type)
1154 self.assertEqual(str(exc_info[1]), str(exc))
1155
1156 def test_from_exception(self):
1157 # Check all the parameters are accepted.
1158 def foo():
1159 1/0
1160 try:
1161 foo()
1162 except Exception as e:
1163 exc_info = sys.exc_info()
1164 self.expected_stack = traceback.StackSummary.extract(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001165 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1166 capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001167 self.exc = traceback.TracebackException.from_exception(
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001168 e, limit=1, lookup_lines=False, capture_locals=True)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001169 expected_stack = self.expected_stack
1170 exc = self.exc
1171 self.assertEqual(None, exc.__cause__)
1172 self.assertEqual(None, exc.__context__)
1173 self.assertEqual(False, exc.__suppress_context__)
1174 self.assertEqual(expected_stack, exc.stack)
1175 self.assertEqual(exc_info[0], exc.exc_type)
1176 self.assertEqual(str(exc_info[1]), str(exc))
1177
1178 def test_cause(self):
1179 try:
1180 try:
1181 1/0
1182 finally:
1183 exc_info_context = sys.exc_info()
1184 exc_context = traceback.TracebackException(*exc_info_context)
1185 cause = Exception("cause")
1186 raise Exception("uh oh") from cause
1187 except Exception:
1188 exc_info = sys.exc_info()
1189 exc = traceback.TracebackException(*exc_info)
1190 expected_stack = traceback.StackSummary.extract(
1191 traceback.walk_tb(exc_info[2]))
1192 exc_cause = traceback.TracebackException(Exception, cause, None)
1193 self.assertEqual(exc_cause, exc.__cause__)
1194 self.assertEqual(exc_context, exc.__context__)
1195 self.assertEqual(True, exc.__suppress_context__)
1196 self.assertEqual(expected_stack, exc.stack)
1197 self.assertEqual(exc_info[0], exc.exc_type)
1198 self.assertEqual(str(exc_info[1]), str(exc))
1199
1200 def test_context(self):
1201 try:
1202 try:
1203 1/0
1204 finally:
1205 exc_info_context = sys.exc_info()
1206 exc_context = traceback.TracebackException(*exc_info_context)
1207 raise Exception("uh oh")
1208 except Exception:
1209 exc_info = sys.exc_info()
1210 exc = traceback.TracebackException(*exc_info)
1211 expected_stack = traceback.StackSummary.extract(
1212 traceback.walk_tb(exc_info[2]))
1213 self.assertEqual(None, exc.__cause__)
1214 self.assertEqual(exc_context, exc.__context__)
1215 self.assertEqual(False, exc.__suppress_context__)
1216 self.assertEqual(expected_stack, exc.stack)
1217 self.assertEqual(exc_info[0], exc.exc_type)
1218 self.assertEqual(str(exc_info[1]), str(exc))
1219
Irit Katriel6dfd1732021-01-12 22:14:27 +00001220 def test_long_context_chain(self):
1221 def f():
1222 try:
1223 1/0
1224 except:
1225 f()
1226
1227 try:
1228 f()
1229 except RecursionError:
1230 exc_info = sys.exc_info()
1231 else:
1232 self.fail("Exception not raised")
1233
1234 te = traceback.TracebackException(*exc_info)
1235 res = list(te.format())
1236
1237 # many ZeroDiv errors followed by the RecursionError
1238 self.assertGreater(len(res), sys.getrecursionlimit())
1239 self.assertGreater(
1240 len([l for l in res if 'ZeroDivisionError:' in l]),
1241 sys.getrecursionlimit() * 0.5)
1242 self.assertIn(
1243 "RecursionError: maximum recursion depth exceeded", res[-1])
1244
Irit Katriel4c94d742021-01-15 02:45:02 +00001245 def test_compact_with_cause(self):
1246 try:
1247 try:
1248 1/0
1249 finally:
1250 cause = Exception("cause")
1251 raise Exception("uh oh") from cause
1252 except Exception:
1253 exc_info = sys.exc_info()
1254 exc = traceback.TracebackException(*exc_info, compact=True)
1255 expected_stack = traceback.StackSummary.extract(
1256 traceback.walk_tb(exc_info[2]))
1257 exc_cause = traceback.TracebackException(Exception, cause, None)
1258 self.assertEqual(exc_cause, exc.__cause__)
1259 self.assertEqual(None, exc.__context__)
1260 self.assertEqual(True, exc.__suppress_context__)
1261 self.assertEqual(expected_stack, exc.stack)
1262 self.assertEqual(exc_info[0], exc.exc_type)
1263 self.assertEqual(str(exc_info[1]), str(exc))
1264
1265 def test_compact_no_cause(self):
1266 try:
1267 try:
1268 1/0
1269 finally:
1270 exc_info_context = sys.exc_info()
1271 exc_context = traceback.TracebackException(*exc_info_context)
1272 raise Exception("uh oh")
1273 except Exception:
1274 exc_info = sys.exc_info()
1275 exc = traceback.TracebackException(*exc_info, compact=True)
1276 expected_stack = traceback.StackSummary.extract(
1277 traceback.walk_tb(exc_info[2]))
1278 self.assertEqual(None, exc.__cause__)
1279 self.assertEqual(exc_context, exc.__context__)
1280 self.assertEqual(False, exc.__suppress_context__)
1281 self.assertEqual(expected_stack, exc.stack)
1282 self.assertEqual(exc_info[0], exc.exc_type)
1283 self.assertEqual(str(exc_info[1]), str(exc))
1284
Irit Katriel427613f2020-12-01 01:35:25 +00001285 def test_no_refs_to_exception_and_traceback_objects(self):
1286 try:
1287 1/0
1288 except Exception:
1289 exc_info = sys.exc_info()
1290
1291 refcnt1 = sys.getrefcount(exc_info[1])
1292 refcnt2 = sys.getrefcount(exc_info[2])
1293 exc = traceback.TracebackException(*exc_info)
1294 self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
1295 self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
1296
Irit Katriel44ca05a2020-11-27 16:38:54 +00001297 def test_comparison_basic(self):
Serhiy Storchaka662db122019-08-08 08:42:54 +03001298 try:
1299 1/0
1300 except Exception:
1301 exc_info = sys.exc_info()
1302 exc = traceback.TracebackException(*exc_info)
1303 exc2 = traceback.TracebackException(*exc_info)
1304 self.assertIsNot(exc, exc2)
1305 self.assertEqual(exc, exc2)
1306 self.assertNotEqual(exc, object())
1307 self.assertEqual(exc, ALWAYS_EQ)
1308
Irit Katriel44ca05a2020-11-27 16:38:54 +00001309 def test_comparison_params_variations(self):
1310 def raise_exc():
1311 try:
1312 raise ValueError('bad value')
1313 except:
1314 raise
1315
1316 def raise_with_locals():
1317 x, y = 1, 2
1318 raise_exc()
1319
1320 try:
1321 raise_with_locals()
1322 except Exception:
1323 exc_info = sys.exc_info()
1324
1325 exc = traceback.TracebackException(*exc_info)
1326 exc1 = traceback.TracebackException(*exc_info, limit=10)
1327 exc2 = traceback.TracebackException(*exc_info, limit=2)
1328
1329 self.assertEqual(exc, exc1) # limit=10 gets all frames
1330 self.assertNotEqual(exc, exc2) # limit=2 truncates the output
1331
1332 # locals change the output
1333 exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
1334 self.assertNotEqual(exc, exc3)
1335
1336 # there are no locals in the innermost frame
1337 exc4 = traceback.TracebackException(*exc_info, limit=-1)
1338 exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
1339 self.assertEqual(exc4, exc5)
1340
1341 # there are locals in the next-to-innermost frame
1342 exc6 = traceback.TracebackException(*exc_info, limit=-2)
1343 exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
1344 self.assertNotEqual(exc6, exc7)
1345
Irit Katriel427613f2020-12-01 01:35:25 +00001346 def test_comparison_equivalent_exceptions_are_equal(self):
1347 excs = []
1348 for _ in range(2):
1349 try:
1350 1/0
1351 except:
1352 excs.append(traceback.TracebackException(*sys.exc_info()))
1353 self.assertEqual(excs[0], excs[1])
1354 self.assertEqual(list(excs[0].format()), list(excs[1].format()))
1355
Zane Bitterde860732017-10-17 17:29:39 -04001356 def test_unhashable(self):
1357 class UnhashableException(Exception):
1358 def __eq__(self, other):
1359 return True
1360
1361 ex1 = UnhashableException('ex1')
1362 ex2 = UnhashableException('ex2')
1363 try:
1364 raise ex2 from ex1
1365 except UnhashableException:
1366 try:
1367 raise ex1
1368 except UnhashableException:
1369 exc_info = sys.exc_info()
1370 exc = traceback.TracebackException(*exc_info)
1371 formatted = list(exc.format())
1372 self.assertIn('UnhashableException: ex2\n', formatted[2])
1373 self.assertIn('UnhashableException: ex1\n', formatted[6])
1374
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001375 def test_limit(self):
1376 def recurse(n):
1377 if n:
1378 recurse(n-1)
1379 else:
1380 1/0
1381 try:
1382 recurse(10)
1383 except Exception:
1384 exc_info = sys.exc_info()
1385 exc = traceback.TracebackException(*exc_info, limit=5)
1386 expected_stack = traceback.StackSummary.extract(
1387 traceback.walk_tb(exc_info[2]), limit=5)
1388 self.assertEqual(expected_stack, exc.stack)
1389
1390 def test_lookup_lines(self):
1391 linecache.clearcache()
1392 e = Exception("uh oh")
1393 c = test_code('/foo.py', 'method')
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001394 f = test_frame(c, None, None)
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001395 tb = test_tb(f, 6, None)
1396 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
Irit Katriel44ca05a2020-11-27 16:38:54 +00001397 self.assertEqual(linecache.cache, {})
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001398 linecache.updatecache('/foo.py', globals())
1399 self.assertEqual(exc.stack[0].line, "import sys")
1400
Robert Collinsd7c7e0e2015-03-05 20:28:52 +13001401 def test_locals(self):
1402 linecache.updatecache('/foo.py', globals())
1403 e = Exception("uh oh")
1404 c = test_code('/foo.py', 'method')
1405 f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1406 tb = test_tb(f, 6, None)
1407 exc = traceback.TracebackException(
1408 Exception, e, tb, capture_locals=True)
1409 self.assertEqual(
1410 exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1411
1412 def test_no_locals(self):
1413 linecache.updatecache('/foo.py', globals())
1414 e = Exception("uh oh")
1415 c = test_code('/foo.py', 'method')
1416 f = test_frame(c, globals(), {'something': 1})
1417 tb = test_tb(f, 6, None)
1418 exc = traceback.TracebackException(Exception, e, tb)
1419 self.assertEqual(exc.stack[0].locals, None)
1420
Berker Peksagc3f417d2015-07-24 17:36:21 +03001421 def test_traceback_header(self):
1422 # do not print a traceback header if exc_traceback is None
1423 # see issue #24695
1424 exc = traceback.TracebackException(Exception, Exception("haven"), None)
1425 self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1426
Robert Collins6bc2c1e2015-03-05 12:07:57 +13001427
Berker Peksag716b3d32015-04-08 09:47:14 +03001428class MiscTest(unittest.TestCase):
1429
1430 def test_all(self):
1431 expected = set()
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001432 denylist = {'print_list'}
Berker Peksag716b3d32015-04-08 09:47:14 +03001433 for name in dir(traceback):
Victor Stinnerfabd7bb2020-08-11 15:26:59 +02001434 if name.startswith('_') or name in denylist:
Berker Peksag716b3d32015-04-08 09:47:14 +03001435 continue
1436 module_object = getattr(traceback, name)
1437 if getattr(module_object, '__module__', None) == 'traceback':
1438 expected.add(name)
1439 self.assertCountEqual(traceback.__all__, expected)
1440
Fred Drake2e2be372001-09-20 21:33:42 +00001441
1442if __name__ == "__main__":
Berker Peksag716b3d32015-04-08 09:47:14 +03001443 unittest.main()