blob: 3f69e5e89352007e8c8283d8d3ce950e4fe407d9 [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
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +00009
10import traceback
11
Christian Heimes81ee3ef2008-05-04 22:42:01 +000012try:
13 raise KeyError
14except KeyError:
15 type_, value, tb = sys.exc_info()
16 file_ = StringIO()
17 traceback_print(tb, file_)
18 example_traceback = file_.getvalue()
19else:
20 raise Error("unable to create test traceback string")
21
22
Benjamin Petersone6528212008-07-15 15:32:09 +000023class SyntaxTracebackCases(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
38 def syntax_error_without_caret(self):
39 # XXX why doesn't compile raise the same traceback?
Barry Warsaw408b6d32002-07-30 23:27:12 +000040 import test.badsyntax_nocaret
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000041
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000042 def syntax_error_bad_indentation(self):
Georg Brandl88fc6642007-02-09 21:28:07 +000043 compile("def spam():\n print(1)\n print(2)", "?", "exec")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000044
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000045 def test_caret(self):
46 err = self.get_exception_format(self.syntax_error_with_caret,
47 SyntaxError)
Guido van Rossume61fd5b2007-07-11 12:20:59 +000048 self.assertEqual(len(err), 4)
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000049 self.assert_(err[1].strip() == "return x!")
Thomas Wouters0e3f5912006-08-11 14:57:12 +000050 self.assert_("^" in err[2]) # third line has caret
Guido van Rossume61fd5b2007-07-11 12:20:59 +000051 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place
Tim Peters7e01e282001-04-08 07:44:07 +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)
Jeremy Hylton09ccc3a2001-03-21 20:33:04 +000060 self.assert_(err[1].strip() == "[x for x in x] = x")
61
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)")
Thomas Wouters0e3f5912006-08-11 14:57:12 +000067 self.assert_("^" 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
Thomas Wouters0e3f5912006-08-11 14:57:12 +000093
Christian Heimes81ee3ef2008-05-04 22:42:01 +000094class TracebackFormatTests(unittest.TestCase):
95
96 def test_traceback_indentation(self):
97 # Make sure that the traceback is properly indented.
98 tb_lines = example_traceback.splitlines()
99 self.assertEquals(len(tb_lines), 3)
100 banner, location, source_line = tb_lines
101 self.assert_(banner.startswith('Traceback'))
102 self.assert_(location.startswith(' File'))
Benjamin Petersone6528212008-07-15 15:32:09 +0000103 self.assert_(source_line.startswith(' raise'))
104
105
106cause_message = (
107 "\nThe above exception was the direct cause "
108 "of the following exception:\n\n")
109
110context_message = (
111 "\nDuring handling of the above exception, "
112 "another exception occurred:\n\n")
113
114boundaries = re.compile(
115 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
116
117
118class BaseExceptionReportingTests:
119
120 def get_exception(self, exception_or_callable):
121 if isinstance(exception_or_callable, Exception):
122 return exception_or_callable
123 try:
124 exception_or_callable()
125 except Exception as e:
126 return e
127
128 def zero_div(self):
129 1/0 # In zero_div
130
131 def check_zero_div(self, msg):
132 lines = msg.splitlines()
133 self.assert_(lines[-3].startswith(' File'))
134 self.assert_('1/0 # In zero_div' in lines[-2], lines[-2])
135 self.assert_(lines[-1].startswith('ZeroDivisionError'), lines[-1])
136
137 def test_simple(self):
138 try:
139 1/0 # Marker
140 except ZeroDivisionError as _:
141 e = _
142 lines = self.get_report(e).splitlines()
143 self.assertEquals(len(lines), 4)
144 self.assert_(lines[0].startswith('Traceback'))
145 self.assert_(lines[1].startswith(' File'))
146 self.assert_('1/0 # Marker' in lines[2])
147 self.assert_(lines[3].startswith('ZeroDivisionError'))
148
149 def test_cause(self):
150 def inner_raise():
151 try:
152 self.zero_div()
153 except ZeroDivisionError as e:
154 raise KeyError from e
155 def outer_raise():
156 inner_raise() # Marker
157 blocks = boundaries.split(self.get_report(outer_raise))
158 self.assertEquals(len(blocks), 3)
159 self.assertEquals(blocks[1], cause_message)
160 self.check_zero_div(blocks[0])
161 self.assert_('inner_raise() # Marker' in blocks[2])
162
163 def test_context(self):
164 def inner_raise():
165 try:
166 self.zero_div()
167 except ZeroDivisionError:
168 raise KeyError
169 def outer_raise():
170 inner_raise() # Marker
171 blocks = boundaries.split(self.get_report(outer_raise))
172 self.assertEquals(len(blocks), 3)
173 self.assertEquals(blocks[1], context_message)
174 self.check_zero_div(blocks[0])
175 self.assert_('inner_raise() # Marker' in blocks[2])
176
177 def test_cause_recursive(self):
178 def inner_raise():
179 try:
180 try:
181 self.zero_div()
182 except ZeroDivisionError as e:
183 z = e
184 raise KeyError from e
185 except KeyError as e:
186 raise z from e
187 def outer_raise():
188 inner_raise() # Marker
189 blocks = boundaries.split(self.get_report(outer_raise))
190 self.assertEquals(len(blocks), 3)
191 self.assertEquals(blocks[1], cause_message)
192 # The first block is the KeyError raised from the ZeroDivisionError
193 self.assert_('raise KeyError from e' in blocks[0])
194 self.assert_('1/0' not in blocks[0])
195 # The second block (apart from the boundary) is the ZeroDivisionError
196 # re-raised from the KeyError
197 self.assert_('inner_raise() # Marker' in blocks[2])
198 self.check_zero_div(blocks[2])
199
200
201
202class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
203 #
204 # This checks reporting through the 'traceback' module, with both
205 # format_exception() and print_exception().
206 #
207
208 def get_report(self, e):
209 e = self.get_exception(e)
210 s = ''.join(
211 traceback.format_exception(type(e), e, e.__traceback__))
212 with captured_output("stderr") as sio:
213 traceback.print_exception(type(e), e, e.__traceback__)
214 self.assertEquals(sio.getvalue(), s)
215 return s
216
217
218class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
219 #
220 # This checks built-in reporting by the interpreter.
221 #
222
223 def get_report(self, e):
224 e = self.get_exception(e)
225 with captured_output("stderr") as s:
226 exception_print(e)
227 return s.getvalue()
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000228
229
Fred Drake2e2be372001-09-20 21:33:42 +0000230def test_main():
Benjamin Petersone6528212008-07-15 15:32:09 +0000231 run_unittest(__name__)
Fred Drake2e2be372001-09-20 21:33:42 +0000232
233if __name__ == "__main__":
234 test_main()