blob: 64dc748a85082d4b44f7b390340060d63d38f864 [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
29 def syntax_error_without_caret(self):
30 # XXX why doesn't compile raise the same traceback?
Barry Warsaw408b6d32002-07-30 23:27:12 +000031 import test.badsyntax_nocaret
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000032
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000033 def syntax_error_bad_indentation(self):
Georg Brandl88fc6642007-02-09 21:28:07 +000034 compile("def spam():\n print(1)\n print(2)", "?", "exec")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000035
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000036 def test_caret(self):
37 err = self.get_exception_format(self.syntax_error_with_caret,
38 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000039 self.assertEqual(len(err), 4)
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000040 self.assert_(err[1].strip() == "return x!")
Thomas Wouters0e3f5912006-08-11 14:57:12 +000041 self.assert_("^" in err[2]) # third line has caret
Guido van Rossume61fd5b2007-07-11 12:20:59 +000042 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place
Tim Peters7e01e282001-04-08 07:44:07 +000043
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000044 def test_nocaret(self):
Finn Bock57f0f342002-11-06 11:45:15 +000045 if is_jython:
46 # jython adds a caret in this case (why shouldn't it?)
47 return
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000048 err = self.get_exception_format(self.syntax_error_without_caret,
49 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000050 self.assertEqual(len(err), 3)
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000051 self.assert_(err[1].strip() == "[x for x in x] = x")
52
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000053 def test_bad_indentation(self):
54 err = self.get_exception_format(self.syntax_error_bad_indentation,
55 IndentationError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000056 self.assertEqual(len(err), 4)
57 self.assertEqual(err[1].strip(), "print(2)")
Thomas Wouters0e3f5912006-08-11 14:57:12 +000058 self.assert_("^" in err[2])
Guido van Rossume61fd5b2007-07-11 12:20:59 +000059 self.assertEqual(err[1].find(")"), err[2].find("^"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000060
Thomas Wouters477c8d52006-05-27 19:21:47 +000061 def test_base_exception(self):
62 # Test that exceptions derived from BaseException are formatted right
63 e = KeyboardInterrupt()
64 lst = traceback.format_exception_only(e.__class__, e)
65 self.assertEqual(lst, ['KeyboardInterrupt\n'])
66
Thomas Wouters0e3f5912006-08-11 14:57:12 +000067 def test_format_exception_only_bad__str__(self):
68 class X(Exception):
69 def __str__(self):
70 1/0
71 err = traceback.format_exception_only(X, X())
72 self.assertEqual(len(err), 1)
73 str_value = '<unprintable %s object>' % X.__name__
Georg Brandl1a3284e2007-12-02 09:40:06 +000074 if X.__module__ in ('__main__', 'builtins'):
Brett Cannon44c52612007-02-27 00:12:43 +000075 str_name = X.__name__
76 else:
77 str_name = '.'.join([X.__module__, X.__name__])
78 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
Thomas Wouters0e3f5912006-08-11 14:57:12 +000079
Thomas Wouters89f507f2006-12-13 04:49:30 +000080 def test_without_exception(self):
81 err = traceback.format_exception_only(None, None)
82 self.assertEqual(err, ['None\n'])
83
Amaury Forgeot d'Arccf8016a2008-10-09 23:37:48 +000084 def test_encoded_file(self):
85 # Test that tracebacks are correctly printed for encoded source files:
86 # - correct line number (Issue2384)
87 # - respect file encoding (Issue3975)
88 import tempfile, sys, subprocess, os
89
90 # The spawned subprocess has its stdout redirected to a PIPE, and its
91 # encoding may be different from the current interpreter, on Windows
92 # at least.
93 process = subprocess.Popen([sys.executable, "-c",
94 "import sys; print(sys.stdout.encoding)"],
95 stdout=subprocess.PIPE,
96 stderr=subprocess.STDOUT)
97 stdout, stderr = process.communicate()
98 output_encoding = str(stdout, 'ascii').splitlines()[0]
99
100 def do_test(firstlines, message, charset, lineno):
101 # Raise the message in a subprocess, and catch the output
102 try:
103 output = open(TESTFN, "w", encoding=charset)
104 output.write("""{0}if 1:
105 import traceback;
106 raise RuntimeError('{1}')
107 """.format(firstlines, message))
108 output.close()
109 process = subprocess.Popen([sys.executable, TESTFN],
110 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
111 stdout, stderr = process.communicate()
112 stdout = stdout.decode(output_encoding).splitlines()
113 finally:
114 unlink(TESTFN)
115
116 # The source lines are encoded with the 'backslashreplace' handler
117 encoded_message = message.encode(output_encoding,
118 'backslashreplace')
119 # and we just decoded them with the output_encoding.
120 message_ascii = encoded_message.decode(output_encoding)
121
122 err_line = "raise RuntimeError('{0}')".format(message_ascii)
123 err_msg = "RuntimeError: {0}".format(message_ascii)
124
125 self.assert_(("line %s" % lineno) in stdout[1],
126 "Invalid line number: {0!r} instead of {1}".format(
127 stdout[1], lineno))
128 self.assert_(stdout[2].endswith(err_line),
129 "Invalid traceback line: {0!r} instead of {1!r}".format(
130 stdout[2], err_line))
131 self.assert_(stdout[3] == err_msg,
132 "Invalid error message: {0!r} instead of {1!r}".format(
133 stdout[3], err_msg))
134
135 do_test("", "foo", "ascii", 3)
136 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"):
137 if charset == "ascii":
138 text = "foo"
139 elif charset == "GBK":
140 text = "\u4E02\u5100"
141 else:
142 text = "h\xe9 ho"
143 do_test("# coding: {0}\n".format(charset),
144 text, charset, 4)
145 do_test("#!shebang\n# coding: {0}\n".format(charset),
146 text, charset, 5)
147
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000148
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000149class TracebackFormatTests(unittest.TestCase):
150
Georg Brandl236f7972009-04-05 14:28:42 +0000151 def test_traceback_format(self):
152 try:
153 raise KeyError('blah')
154 except KeyError:
155 type_, value, tb = sys.exc_info()
156 traceback_fmt = 'Traceback (most recent call last):\n' + \
157 ''.join(traceback.format_tb(tb))
158 file_ = StringIO()
159 traceback_print(tb, file_)
160 python_fmt = file_.getvalue()
161 else:
162 raise Error("unable to create test traceback string")
163
164 # Make sure that Python and the traceback module format the same thing
165 self.assertEquals(traceback_fmt, python_fmt)
166
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000167 # Make sure that the traceback is properly indented.
Georg Brandl236f7972009-04-05 14:28:42 +0000168 tb_lines = python_fmt.splitlines()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000169 self.assertEquals(len(tb_lines), 3)
170 banner, location, source_line = tb_lines
171 self.assert_(banner.startswith('Traceback'))
172 self.assert_(location.startswith(' File'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000173 self.assert_(source_line.startswith(' raise'))
174
175
176cause_message = (
177 "\nThe above exception was the direct cause "
178 "of the following exception:\n\n")
179
180context_message = (
181 "\nDuring handling of the above exception, "
182 "another exception occurred:\n\n")
183
184boundaries = re.compile(
185 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
186
187
188class BaseExceptionReportingTests:
189
190 def get_exception(self, exception_or_callable):
191 if isinstance(exception_or_callable, Exception):
192 return exception_or_callable
193 try:
194 exception_or_callable()
195 except Exception as e:
196 return e
197
198 def zero_div(self):
199 1/0 # In zero_div
200
201 def check_zero_div(self, msg):
202 lines = msg.splitlines()
203 self.assert_(lines[-3].startswith(' File'))
204 self.assert_('1/0 # In zero_div' in lines[-2], lines[-2])
205 self.assert_(lines[-1].startswith('ZeroDivisionError'), lines[-1])
206
207 def test_simple(self):
208 try:
209 1/0 # Marker
210 except ZeroDivisionError as _:
211 e = _
212 lines = self.get_report(e).splitlines()
213 self.assertEquals(len(lines), 4)
214 self.assert_(lines[0].startswith('Traceback'))
215 self.assert_(lines[1].startswith(' File'))
216 self.assert_('1/0 # Marker' in lines[2])
217 self.assert_(lines[3].startswith('ZeroDivisionError'))
218
219 def test_cause(self):
220 def inner_raise():
221 try:
222 self.zero_div()
223 except ZeroDivisionError as e:
224 raise KeyError from e
225 def outer_raise():
226 inner_raise() # Marker
227 blocks = boundaries.split(self.get_report(outer_raise))
228 self.assertEquals(len(blocks), 3)
229 self.assertEquals(blocks[1], cause_message)
230 self.check_zero_div(blocks[0])
231 self.assert_('inner_raise() # Marker' in blocks[2])
232
233 def test_context(self):
234 def inner_raise():
235 try:
236 self.zero_div()
237 except ZeroDivisionError:
238 raise KeyError
239 def outer_raise():
240 inner_raise() # Marker
241 blocks = boundaries.split(self.get_report(outer_raise))
242 self.assertEquals(len(blocks), 3)
243 self.assertEquals(blocks[1], context_message)
244 self.check_zero_div(blocks[0])
245 self.assert_('inner_raise() # Marker' in blocks[2])
246
247 def test_cause_recursive(self):
248 def inner_raise():
249 try:
250 try:
251 self.zero_div()
252 except ZeroDivisionError as e:
253 z = e
254 raise KeyError from e
255 except KeyError as e:
256 raise z from e
257 def outer_raise():
258 inner_raise() # Marker
259 blocks = boundaries.split(self.get_report(outer_raise))
260 self.assertEquals(len(blocks), 3)
261 self.assertEquals(blocks[1], cause_message)
262 # The first block is the KeyError raised from the ZeroDivisionError
263 self.assert_('raise KeyError from e' in blocks[0])
264 self.assert_('1/0' not in blocks[0])
265 # The second block (apart from the boundary) is the ZeroDivisionError
266 # re-raised from the KeyError
267 self.assert_('inner_raise() # Marker' in blocks[2])
268 self.check_zero_div(blocks[2])
269
270
271
272class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
273 #
274 # This checks reporting through the 'traceback' module, with both
275 # format_exception() and print_exception().
276 #
277
278 def get_report(self, e):
279 e = self.get_exception(e)
280 s = ''.join(
281 traceback.format_exception(type(e), e, e.__traceback__))
282 with captured_output("stderr") as sio:
283 traceback.print_exception(type(e), e, e.__traceback__)
284 self.assertEquals(sio.getvalue(), s)
285 return s
286
287
288class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
289 #
290 # This checks built-in reporting by the interpreter.
291 #
292
293 def get_report(self, e):
294 e = self.get_exception(e)
295 with captured_output("stderr") as s:
296 exception_print(e)
297 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000298
299
Fred Drake2e2be372001-09-20 21:33:42 +0000300def test_main():
Benjamin Petersone6528212008-07-15 15:32:09 +0000301 run_unittest(__name__)
Fred Drake2e2be372001-09-20 21:33:42 +0000302
303if __name__ == "__main__":
304 test_main()