blob: f9e44582202d88831eb67f1e774b84c930a5595b [file] [log] [blame]
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00001"""Test cases for traceback module"""
2
Benjamin Petersone6528212008-07-15 15:32:09 +00003from _testcapi import traceback_print, exception_print
Christian Heimes81ee3ef2008-05-04 22:42:01 +00004from io import StringIO
5import sys
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00006import unittest
Benjamin Petersone6528212008-07-15 15:32:09 +00007import re
8from test.support import run_unittest, is_jython, Error, captured_output
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +00009from test.support import TESTFN, unlink
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000010
11import traceback
12
Christian Heimes81ee3ef2008-05-04 22:42:01 +000013
Benjamin Petersone6528212008-07-15 15:32:09 +000014class SyntaxTracebackCases(unittest.TestCase):
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000015 # For now, a very minimal set of tests. I want to be sure that
16 # formatting of SyntaxErrors works based on changes for 2.1.
17
18 def get_exception_format(self, func, exc):
19 try:
20 func()
Guido van Rossumb940e112007-01-10 16:19:56 +000021 except exc as value:
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000022 return traceback.format_exception_only(exc, value)
23 else:
Collin Winter3add4d72007-08-29 23:37:32 +000024 raise ValueError("call did not raise exception")
Tim Peters7e01e282001-04-08 07:44:07 +000025
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000026 def syntax_error_with_caret(self):
27 compile("def fact(x):\n\treturn x!\n", "?", "exec")
28
Georg Brandl751899a2009-06-04 19:41:00 +000029 def syntax_error_with_caret_2(self):
30 compile("1 +\n", "?", "exec")
31
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000032 def syntax_error_without_caret(self):
33 # XXX why doesn't compile raise the same traceback?
Barry Warsaw408b6d32002-07-30 23:27:12 +000034 import test.badsyntax_nocaret
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000035
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000036 def syntax_error_bad_indentation(self):
Georg Brandl88fc6642007-02-09 21:28:07 +000037 compile("def spam():\n print(1)\n print(2)", "?", "exec")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000038
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000039 def test_caret(self):
40 err = self.get_exception_format(self.syntax_error_with_caret,
41 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000042 self.assertEqual(len(err), 4)
Georg Brandlab91fde2009-08-13 08:51:18 +000043 self.assertTrue(err[1].strip() == "return x!")
44 self.assertTrue("^" in err[2]) # third line has caret
Guido van Rossume61fd5b2007-07-11 12:20:59 +000045 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place
Tim Peters7e01e282001-04-08 07:44:07 +000046
Georg Brandl751899a2009-06-04 19:41:00 +000047 err = self.get_exception_format(self.syntax_error_with_caret_2,
48 SyntaxError)
Georg Brandlab91fde2009-08-13 08:51:18 +000049 self.assertTrue("^" in err[2]) # third line has caret
50 self.assertTrue(err[2].count('\n') == 1) # and no additional newline
51 self.assertTrue(err[1].find("+") == err[2].find("^")) # in the right place
Georg Brandl751899a2009-06-04 19:41:00 +000052
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000053 def test_nocaret(self):
Finn Bock57f0f342002-11-06 11:45:15 +000054 if is_jython:
55 # jython adds a caret in this case (why shouldn't it?)
56 return
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000057 err = self.get_exception_format(self.syntax_error_without_caret,
58 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000059 self.assertEqual(len(err), 3)
Georg Brandlab91fde2009-08-13 08:51:18 +000060 self.assertTrue(err[1].strip() == "[x for x in x] = x")
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000061
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000062 def test_bad_indentation(self):
63 err = self.get_exception_format(self.syntax_error_bad_indentation,
64 IndentationError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000065 self.assertEqual(len(err), 4)
66 self.assertEqual(err[1].strip(), "print(2)")
Georg Brandlab91fde2009-08-13 08:51:18 +000067 self.assertTrue("^" in err[2])
Guido van Rossume61fd5b2007-07-11 12:20:59 +000068 self.assertEqual(err[1].find(")"), err[2].find("^"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000069
Thomas Wouters477c8d52006-05-27 19:21:47 +000070 def test_base_exception(self):
71 # Test that exceptions derived from BaseException are formatted right
72 e = KeyboardInterrupt()
73 lst = traceback.format_exception_only(e.__class__, e)
74 self.assertEqual(lst, ['KeyboardInterrupt\n'])
75
Thomas Wouters0e3f5912006-08-11 14:57:12 +000076 def test_format_exception_only_bad__str__(self):
77 class X(Exception):
78 def __str__(self):
79 1/0
80 err = traceback.format_exception_only(X, X())
81 self.assertEqual(len(err), 1)
82 str_value = '<unprintable %s object>' % X.__name__
Georg Brandl1a3284e2007-12-02 09:40:06 +000083 if X.__module__ in ('__main__', 'builtins'):
Brett Cannon44c52612007-02-27 00:12:43 +000084 str_name = X.__name__
85 else:
86 str_name = '.'.join([X.__module__, X.__name__])
87 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
Thomas Wouters0e3f5912006-08-11 14:57:12 +000088
Thomas Wouters89f507f2006-12-13 04:49:30 +000089 def test_without_exception(self):
90 err = traceback.format_exception_only(None, None)
91 self.assertEqual(err, ['None\n'])
92
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +000093 def test_encoded_file(self):
94 # Test that tracebacks are correctly printed for encoded source files:
95 # - correct line number (Issue2384)
96 # - respect file encoding (Issue3975)
97 import tempfile, sys, subprocess, os
98
99 # The spawned subprocess has its stdout redirected to a PIPE, and its
100 # encoding may be different from the current interpreter, on Windows
101 # at least.
102 process = subprocess.Popen([sys.executable, "-c",
103 "import sys; print(sys.stdout.encoding)"],
104 stdout=subprocess.PIPE,
105 stderr=subprocess.STDOUT)
106 stdout, stderr = process.communicate()
107 output_encoding = str(stdout, 'ascii').splitlines()[0]
108
109 def do_test(firstlines, message, charset, lineno):
110 # Raise the message in a subprocess, and catch the output
111 try:
112 output = open(TESTFN, "w", encoding=charset)
113 output.write("""{0}if 1:
114 import traceback;
115 raise RuntimeError('{1}')
116 """.format(firstlines, message))
117 output.close()
118 process = subprocess.Popen([sys.executable, TESTFN],
119 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
120 stdout, stderr = process.communicate()
121 stdout = stdout.decode(output_encoding).splitlines()
122 finally:
123 unlink(TESTFN)
124
125 # The source lines are encoded with the 'backslashreplace' handler
126 encoded_message = message.encode(output_encoding,
127 'backslashreplace')
128 # and we just decoded them with the output_encoding.
129 message_ascii = encoded_message.decode(output_encoding)
130
131 err_line = "raise RuntimeError('{0}')".format(message_ascii)
132 err_msg = "RuntimeError: {0}".format(message_ascii)
133
Georg Brandlab91fde2009-08-13 08:51:18 +0000134 self.assertTrue(("line %s" % lineno) in stdout[1],
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000135 "Invalid line number: {0!r} instead of {1}".format(
136 stdout[1], lineno))
Georg Brandlab91fde2009-08-13 08:51:18 +0000137 self.assertTrue(stdout[2].endswith(err_line),
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000138 "Invalid traceback line: {0!r} instead of {1!r}".format(
139 stdout[2], err_line))
Georg Brandlab91fde2009-08-13 08:51:18 +0000140 self.assertTrue(stdout[3] == err_msg,
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +0000141 "Invalid error message: {0!r} instead of {1!r}".format(
142 stdout[3], err_msg))
143
144 do_test("", "foo", "ascii", 3)
145 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
146 if charset == "ascii":
147 text = "foo"
148 elif charset == "GBK":
149 text = "\u4E02\u5100"
150 else:
151 text = "h\xe9 ho"
152 do_test("# coding: {0}\n".format(charset),
153 text, charset, 4)
154 do_test("#!shebang\n# coding: {0}\n".format(charset),
155 text, charset, 5)
156
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000157
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000158class TracebackFormatTests(unittest.TestCase):
159
Georg Brandl236f7972009-04-05 14:28:42 +0000160 def test_traceback_format(self):
161 try:
162 raise KeyError('blah')
163 except KeyError:
164 type_, value, tb = sys.exc_info()
165 traceback_fmt = 'Traceback (most recent call last):\n' + \
166 ''.join(traceback.format_tb(tb))
167 file_ = StringIO()
168 traceback_print(tb, file_)
169 python_fmt = file_.getvalue()
170 else:
171 raise Error("unable to create test traceback string")
172
173 # Make sure that Python and the traceback module format the same thing
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000174 self.assertEqual(traceback_fmt, python_fmt)
Georg Brandl236f7972009-04-05 14:28:42 +0000175
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000176 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000177 tb_lines = python_fmt.splitlines()
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000178 self.assertEqual(len(tb_lines), 3)
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000179 banner, location, source_line = tb_lines
Georg Brandlab91fde2009-08-13 08:51:18 +0000180 self.assertTrue(banner.startswith('Traceback'))
181 self.assertTrue(location.startswith(' File'))
182 self.assertTrue(source_line.startswith(' raise'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000183
184
185cause_message = (
186 "\nThe above exception was the direct cause "
187 "of the following exception:\n\n")
188
189context_message = (
190 "\nDuring handling of the above exception, "
191 "another exception occurred:\n\n")
192
193boundaries = re.compile(
194 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
195
196
197class BaseExceptionReportingTests:
198
199 def get_exception(self, exception_or_callable):
200 if isinstance(exception_or_callable, Exception):
201 return exception_or_callable
202 try:
203 exception_or_callable()
204 except Exception as e:
205 return e
206
207 def zero_div(self):
208 1/0 # In zero_div
209
210 def check_zero_div(self, msg):
211 lines = msg.splitlines()
Georg Brandlab91fde2009-08-13 08:51:18 +0000212 self.assertTrue(lines[-3].startswith(' File'))
213 self.assertTrue('1/0 # In zero_div' in lines[-2], lines[-2])
214 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
Benjamin Petersone6528212008-07-15 15:32:09 +0000215
216 def test_simple(self):
217 try:
218 1/0 # Marker
219 except ZeroDivisionError as _:
220 e = _
221 lines = self.get_report(e).splitlines()
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000222 self.assertEqual(len(lines), 4)
Georg Brandlab91fde2009-08-13 08:51:18 +0000223 self.assertTrue(lines[0].startswith('Traceback'))
224 self.assertTrue(lines[1].startswith(' File'))
225 self.assertTrue('1/0 # Marker' in lines[2])
226 self.assertTrue(lines[3].startswith('ZeroDivisionError'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000227
228 def test_cause(self):
229 def inner_raise():
230 try:
231 self.zero_div()
232 except ZeroDivisionError as e:
233 raise KeyError from e
234 def outer_raise():
235 inner_raise() # Marker
236 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000237 self.assertEqual(len(blocks), 3)
238 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000239 self.check_zero_div(blocks[0])
Georg Brandlab91fde2009-08-13 08:51:18 +0000240 self.assertTrue('inner_raise() # Marker' in blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000241
242 def test_context(self):
243 def inner_raise():
244 try:
245 self.zero_div()
246 except ZeroDivisionError:
247 raise KeyError
248 def outer_raise():
249 inner_raise() # Marker
250 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000251 self.assertEqual(len(blocks), 3)
252 self.assertEqual(blocks[1], context_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000253 self.check_zero_div(blocks[0])
Georg Brandlab91fde2009-08-13 08:51:18 +0000254 self.assertTrue('inner_raise() # Marker' in blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000255
Antoine Pitroubcc63a82009-11-28 16:16:09 +0000256 def test_cause_and_context(self):
257 # When both a cause and a context are set, only the cause should be
258 # displayed and the context should be muted.
259 def inner_raise():
260 try:
261 self.zero_div()
262 except ZeroDivisionError as _e:
263 e = _e
264 try:
265 xyzzy
266 except NameError:
267 raise KeyError from e
268 def outer_raise():
269 inner_raise() # Marker
270 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000271 self.assertEqual(len(blocks), 3)
272 self.assertEqual(blocks[1], cause_message)
Antoine Pitroubcc63a82009-11-28 16:16:09 +0000273 self.check_zero_div(blocks[0])
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000274 self.assertIn('inner_raise() # Marker', blocks[2])
Antoine Pitroubcc63a82009-11-28 16:16:09 +0000275
Benjamin Petersone6528212008-07-15 15:32:09 +0000276 def test_cause_recursive(self):
277 def inner_raise():
278 try:
279 try:
280 self.zero_div()
281 except ZeroDivisionError as e:
282 z = e
283 raise KeyError from e
284 except KeyError as e:
285 raise z from e
286 def outer_raise():
287 inner_raise() # Marker
288 blocks = boundaries.split(self.get_report(outer_raise))
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000289 self.assertEqual(len(blocks), 3)
290 self.assertEqual(blocks[1], cause_message)
Benjamin Petersone6528212008-07-15 15:32:09 +0000291 # The first block is the KeyError raised from the ZeroDivisionError
Georg Brandlab91fde2009-08-13 08:51:18 +0000292 self.assertTrue('raise KeyError from e' in blocks[0])
293 self.assertTrue('1/0' not in blocks[0])
Benjamin Petersone6528212008-07-15 15:32:09 +0000294 # The second block (apart from the boundary) is the ZeroDivisionError
295 # re-raised from the KeyError
Georg Brandlab91fde2009-08-13 08:51:18 +0000296 self.assertTrue('inner_raise() # Marker' in blocks[2])
Benjamin Petersone6528212008-07-15 15:32:09 +0000297 self.check_zero_div(blocks[2])
298
Benjamin Peterson98316112010-10-24 02:57:31 +0000299 def test_syntax_error_offset_at_eol(self):
300 # See #10186.
301 def e():
302 raise SyntaxError('', ('', 0, 5, 'hello'))
303 msg = self.get_report(e).splitlines()
304 self.assertEqual(msg[-2], " ^")
Benjamin Petersonc8850d02010-10-29 04:02:30 +0000305 def e():
306 exec("x = 5 | 4 |")
307 msg = self.get_report(e).splitlines()
308 self.assertEqual(msg[-2], ' ^')
Benjamin Petersone6528212008-07-15 15:32:09 +0000309
310
311class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
312 #
313 # This checks reporting through the 'traceback' module, with both
314 # format_exception() and print_exception().
315 #
316
317 def get_report(self, e):
318 e = self.get_exception(e)
319 s = ''.join(
320 traceback.format_exception(type(e), e, e.__traceback__))
321 with captured_output("stderr") as sio:
322 traceback.print_exception(type(e), e, e.__traceback__)
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000323 self.assertEqual(sio.getvalue(), s)
Benjamin Petersone6528212008-07-15 15:32:09 +0000324 return s
325
326
327class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
328 #
329 # This checks built-in reporting by the interpreter.
330 #
331
332 def get_report(self, e):
333 e = self.get_exception(e)
334 with captured_output("stderr") as s:
335 exception_print(e)
336 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000337
338
Fred Drake2e2be372001-09-20 21:33:42 +0000339def test_main():
Benjamin Petersone6528212008-07-15 15:32:09 +0000340 run_unittest(__name__)
Fred Drake2e2be372001-09-20 21:33:42 +0000341
342if __name__ == "__main__":
343 test_main()