##// END OF EJS Templates
Verbose error message for syntax error occuring at runtime
Piotr Zielinski -
Show More

The requested changes are too big and content was truncated. Show full diff

1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,347 +1,374 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.core.ultratb
2 """Tests for IPython.core.ultratb
3 """
3 """
4 import io
4 import io
5 import sys
5 import sys
6 import os.path
6 import os.path
7 from textwrap import dedent
7 from textwrap import dedent
8 import traceback
8 import traceback
9 import unittest
9 import unittest
10 from unittest import mock
10 from unittest import mock
11
11
12 from ..ultratb import ColorTB, VerboseTB, find_recursion
12 from ..ultratb import ColorTB, VerboseTB, find_recursion
13
13
14
14
15 from IPython.testing import tools as tt
15 from IPython.testing import tools as tt
16 from IPython.testing.decorators import onlyif_unicode_paths
16 from IPython.testing.decorators import onlyif_unicode_paths
17 from IPython.utils.syspathcontext import prepended_to_syspath
17 from IPython.utils.syspathcontext import prepended_to_syspath
18 from IPython.utils.tempdir import TemporaryDirectory
18 from IPython.utils.tempdir import TemporaryDirectory
19
19
20 ip = get_ipython()
20 ip = get_ipython()
21
21
22 file_1 = """1
22 file_1 = """1
23 2
23 2
24 3
24 3
25 def f():
25 def f():
26 1/0
26 1/0
27 """
27 """
28
28
29 file_2 = """def f():
29 file_2 = """def f():
30 1/0
30 1/0
31 """
31 """
32
32
33 class ChangedPyFileTest(unittest.TestCase):
33 class ChangedPyFileTest(unittest.TestCase):
34 def test_changing_py_file(self):
34 def test_changing_py_file(self):
35 """Traceback produced if the line where the error occurred is missing?
35 """Traceback produced if the line where the error occurred is missing?
36
36
37 https://github.com/ipython/ipython/issues/1456
37 https://github.com/ipython/ipython/issues/1456
38 """
38 """
39 with TemporaryDirectory() as td:
39 with TemporaryDirectory() as td:
40 fname = os.path.join(td, "foo.py")
40 fname = os.path.join(td, "foo.py")
41 with open(fname, "w") as f:
41 with open(fname, "w") as f:
42 f.write(file_1)
42 f.write(file_1)
43
43
44 with prepended_to_syspath(td):
44 with prepended_to_syspath(td):
45 ip.run_cell("import foo")
45 ip.run_cell("import foo")
46
46
47 with tt.AssertPrints("ZeroDivisionError"):
47 with tt.AssertPrints("ZeroDivisionError"):
48 ip.run_cell("foo.f()")
48 ip.run_cell("foo.f()")
49
49
50 # Make the file shorter, so the line of the error is missing.
50 # Make the file shorter, so the line of the error is missing.
51 with open(fname, "w") as f:
51 with open(fname, "w") as f:
52 f.write(file_2)
52 f.write(file_2)
53
53
54 # For some reason, this was failing on the *second* call after
54 # For some reason, this was failing on the *second* call after
55 # changing the file, so we call f() twice.
55 # changing the file, so we call f() twice.
56 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
56 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
57 with tt.AssertPrints("ZeroDivisionError"):
57 with tt.AssertPrints("ZeroDivisionError"):
58 ip.run_cell("foo.f()")
58 ip.run_cell("foo.f()")
59 with tt.AssertPrints("ZeroDivisionError"):
59 with tt.AssertPrints("ZeroDivisionError"):
60 ip.run_cell("foo.f()")
60 ip.run_cell("foo.f()")
61
61
62 iso_8859_5_file = u'''# coding: iso-8859-5
62 iso_8859_5_file = u'''# coding: iso-8859-5
63
63
64 def fail():
64 def fail():
65 """дбИЖ"""
65 """дбИЖ"""
66 1/0 # дбИЖ
66 1/0 # дбИЖ
67 '''
67 '''
68
68
69 class NonAsciiTest(unittest.TestCase):
69 class NonAsciiTest(unittest.TestCase):
70 @onlyif_unicode_paths
70 @onlyif_unicode_paths
71 def test_nonascii_path(self):
71 def test_nonascii_path(self):
72 # Non-ascii directory name as well.
72 # Non-ascii directory name as well.
73 with TemporaryDirectory(suffix=u'é') as td:
73 with TemporaryDirectory(suffix=u'é') as td:
74 fname = os.path.join(td, u"fooé.py")
74 fname = os.path.join(td, u"fooé.py")
75 with open(fname, "w") as f:
75 with open(fname, "w") as f:
76 f.write(file_1)
76 f.write(file_1)
77
77
78 with prepended_to_syspath(td):
78 with prepended_to_syspath(td):
79 ip.run_cell("import foo")
79 ip.run_cell("import foo")
80
80
81 with tt.AssertPrints("ZeroDivisionError"):
81 with tt.AssertPrints("ZeroDivisionError"):
82 ip.run_cell("foo.f()")
82 ip.run_cell("foo.f()")
83
83
84 def test_iso8859_5(self):
84 def test_iso8859_5(self):
85 with TemporaryDirectory() as td:
85 with TemporaryDirectory() as td:
86 fname = os.path.join(td, 'dfghjkl.py')
86 fname = os.path.join(td, 'dfghjkl.py')
87
87
88 with io.open(fname, 'w', encoding='iso-8859-5') as f:
88 with io.open(fname, 'w', encoding='iso-8859-5') as f:
89 f.write(iso_8859_5_file)
89 f.write(iso_8859_5_file)
90
90
91 with prepended_to_syspath(td):
91 with prepended_to_syspath(td):
92 ip.run_cell("from dfghjkl import fail")
92 ip.run_cell("from dfghjkl import fail")
93
93
94 with tt.AssertPrints("ZeroDivisionError"):
94 with tt.AssertPrints("ZeroDivisionError"):
95 with tt.AssertPrints(u'дбИЖ', suppress=False):
95 with tt.AssertPrints(u'дбИЖ', suppress=False):
96 ip.run_cell('fail()')
96 ip.run_cell('fail()')
97
97
98 def test_nonascii_msg(self):
98 def test_nonascii_msg(self):
99 cell = u"raise Exception('é')"
99 cell = u"raise Exception('é')"
100 expected = u"Exception('é')"
100 expected = u"Exception('é')"
101 ip.run_cell("%xmode plain")
101 ip.run_cell("%xmode plain")
102 with tt.AssertPrints(expected):
102 with tt.AssertPrints(expected):
103 ip.run_cell(cell)
103 ip.run_cell(cell)
104
104
105 ip.run_cell("%xmode verbose")
105 ip.run_cell("%xmode verbose")
106 with tt.AssertPrints(expected):
106 with tt.AssertPrints(expected):
107 ip.run_cell(cell)
107 ip.run_cell(cell)
108
108
109 ip.run_cell("%xmode context")
109 ip.run_cell("%xmode context")
110 with tt.AssertPrints(expected):
110 with tt.AssertPrints(expected):
111 ip.run_cell(cell)
111 ip.run_cell(cell)
112
112
113
113
114 class NestedGenExprTestCase(unittest.TestCase):
114 class NestedGenExprTestCase(unittest.TestCase):
115 """
115 """
116 Regression test for the following issues:
116 Regression test for the following issues:
117 https://github.com/ipython/ipython/issues/8293
117 https://github.com/ipython/ipython/issues/8293
118 https://github.com/ipython/ipython/issues/8205
118 https://github.com/ipython/ipython/issues/8205
119 """
119 """
120 def test_nested_genexpr(self):
120 def test_nested_genexpr(self):
121 code = dedent(
121 code = dedent(
122 """\
122 """\
123 class SpecificException(Exception):
123 class SpecificException(Exception):
124 pass
124 pass
125
125
126 def foo(x):
126 def foo(x):
127 raise SpecificException("Success!")
127 raise SpecificException("Success!")
128
128
129 sum(sum(foo(x) for _ in [0]) for x in [0])
129 sum(sum(foo(x) for _ in [0]) for x in [0])
130 """
130 """
131 )
131 )
132 with tt.AssertPrints('SpecificException: Success!', suppress=False):
132 with tt.AssertPrints('SpecificException: Success!', suppress=False):
133 ip.run_cell(code)
133 ip.run_cell(code)
134
134
135
135
136 indentationerror_file = """if True:
136 indentationerror_file = """if True:
137 zoon()
137 zoon()
138 """
138 """
139
139
140 class IndentationErrorTest(unittest.TestCase):
140 class IndentationErrorTest(unittest.TestCase):
141 def test_indentationerror_shows_line(self):
141 def test_indentationerror_shows_line(self):
142 # See issue gh-2398
142 # See issue gh-2398
143 with tt.AssertPrints("IndentationError"):
143 with tt.AssertPrints("IndentationError"):
144 with tt.AssertPrints("zoon()", suppress=False):
144 with tt.AssertPrints("zoon()", suppress=False):
145 ip.run_cell(indentationerror_file)
145 ip.run_cell(indentationerror_file)
146
146
147 with TemporaryDirectory() as td:
147 with TemporaryDirectory() as td:
148 fname = os.path.join(td, "foo.py")
148 fname = os.path.join(td, "foo.py")
149 with open(fname, "w") as f:
149 with open(fname, "w") as f:
150 f.write(indentationerror_file)
150 f.write(indentationerror_file)
151
151
152 with tt.AssertPrints("IndentationError"):
152 with tt.AssertPrints("IndentationError"):
153 with tt.AssertPrints("zoon()", suppress=False):
153 with tt.AssertPrints("zoon()", suppress=False):
154 ip.magic('run %s' % fname)
154 ip.magic('run %s' % fname)
155
155
156 se_file_1 = """1
156 se_file_1 = """1
157 2
157 2
158 7/
158 7/
159 """
159 """
160
160
161 se_file_2 = """7/
161 se_file_2 = """7/
162 """
162 """
163
163
164 class SyntaxErrorTest(unittest.TestCase):
164 class SyntaxErrorTest(unittest.TestCase):
165 def test_syntaxerror_without_lineno(self):
165 def test_syntaxerror_without_lineno(self):
166 with tt.AssertNotPrints("TypeError"):
166 with tt.AssertNotPrints("TypeError"):
167 with tt.AssertPrints("line unknown"):
167 with tt.AssertPrints("line unknown"):
168 ip.run_cell("raise SyntaxError()")
168 ip.run_cell("raise SyntaxError()")
169
169
170 def test_syntaxerror_no_stacktrace_at_compile_time(self):
171 syntax_error_at_compile_time = """
172 def foo():
173 ..
174 """
175 with tt.AssertPrints("SyntaxError"):
176 ip.run_cell(syntax_error_at_compile_time)
177
178 with tt.AssertNotPrints("foo()"):
179 ip.run_cell(syntax_error_at_compile_time)
180
181 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
182 syntax_error_at_runtime = """
183 def foo():
184 eval("..")
185
186 def bar():
187 foo()
188
189 bar()
190 """
191 with tt.AssertPrints("SyntaxError"):
192 ip.run_cell(syntax_error_at_runtime)
193 # Assert syntax error during runtime generate stacktrace
194 with tt.AssertPrints(["foo()", "bar()"]):
195 ip.run_cell(syntax_error_at_runtime)
196
170 def test_changing_py_file(self):
197 def test_changing_py_file(self):
171 with TemporaryDirectory() as td:
198 with TemporaryDirectory() as td:
172 fname = os.path.join(td, "foo.py")
199 fname = os.path.join(td, "foo.py")
173 with open(fname, 'w') as f:
200 with open(fname, 'w') as f:
174 f.write(se_file_1)
201 f.write(se_file_1)
175
202
176 with tt.AssertPrints(["7/", "SyntaxError"]):
203 with tt.AssertPrints(["7/", "SyntaxError"]):
177 ip.magic("run " + fname)
204 ip.magic("run " + fname)
178
205
179 # Modify the file
206 # Modify the file
180 with open(fname, 'w') as f:
207 with open(fname, 'w') as f:
181 f.write(se_file_2)
208 f.write(se_file_2)
182
209
183 # The SyntaxError should point to the correct line
210 # The SyntaxError should point to the correct line
184 with tt.AssertPrints(["7/", "SyntaxError"]):
211 with tt.AssertPrints(["7/", "SyntaxError"]):
185 ip.magic("run " + fname)
212 ip.magic("run " + fname)
186
213
187 def test_non_syntaxerror(self):
214 def test_non_syntaxerror(self):
188 # SyntaxTB may be called with an error other than a SyntaxError
215 # SyntaxTB may be called with an error other than a SyntaxError
189 # See e.g. gh-4361
216 # See e.g. gh-4361
190 try:
217 try:
191 raise ValueError('QWERTY')
218 raise ValueError('QWERTY')
192 except ValueError:
219 except ValueError:
193 with tt.AssertPrints('QWERTY'):
220 with tt.AssertPrints('QWERTY'):
194 ip.showsyntaxerror()
221 ip.showsyntaxerror()
195
222
196
223
197 class Python3ChainedExceptionsTest(unittest.TestCase):
224 class Python3ChainedExceptionsTest(unittest.TestCase):
198 DIRECT_CAUSE_ERROR_CODE = """
225 DIRECT_CAUSE_ERROR_CODE = """
199 try:
226 try:
200 x = 1 + 2
227 x = 1 + 2
201 print(not_defined_here)
228 print(not_defined_here)
202 except Exception as e:
229 except Exception as e:
203 x += 55
230 x += 55
204 x - 1
231 x - 1
205 y = {}
232 y = {}
206 raise KeyError('uh') from e
233 raise KeyError('uh') from e
207 """
234 """
208
235
209 EXCEPTION_DURING_HANDLING_CODE = """
236 EXCEPTION_DURING_HANDLING_CODE = """
210 try:
237 try:
211 x = 1 + 2
238 x = 1 + 2
212 print(not_defined_here)
239 print(not_defined_here)
213 except Exception as e:
240 except Exception as e:
214 x += 55
241 x += 55
215 x - 1
242 x - 1
216 y = {}
243 y = {}
217 raise KeyError('uh')
244 raise KeyError('uh')
218 """
245 """
219
246
220 SUPPRESS_CHAINING_CODE = """
247 SUPPRESS_CHAINING_CODE = """
221 try:
248 try:
222 1/0
249 1/0
223 except Exception:
250 except Exception:
224 raise ValueError("Yikes") from None
251 raise ValueError("Yikes") from None
225 """
252 """
226
253
227 def test_direct_cause_error(self):
254 def test_direct_cause_error(self):
228 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
255 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
229 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
256 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
230
257
231 def test_exception_during_handling_error(self):
258 def test_exception_during_handling_error(self):
232 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
259 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
233 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
260 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
234
261
235 def test_suppress_exception_chaining(self):
262 def test_suppress_exception_chaining(self):
236 with tt.AssertNotPrints("ZeroDivisionError"), \
263 with tt.AssertNotPrints("ZeroDivisionError"), \
237 tt.AssertPrints("ValueError", suppress=False):
264 tt.AssertPrints("ValueError", suppress=False):
238 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
265 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
239
266
240
267
241 class RecursionTest(unittest.TestCase):
268 class RecursionTest(unittest.TestCase):
242 DEFINITIONS = """
269 DEFINITIONS = """
243 def non_recurs():
270 def non_recurs():
244 1/0
271 1/0
245
272
246 def r1():
273 def r1():
247 r1()
274 r1()
248
275
249 def r3a():
276 def r3a():
250 r3b()
277 r3b()
251
278
252 def r3b():
279 def r3b():
253 r3c()
280 r3c()
254
281
255 def r3c():
282 def r3c():
256 r3a()
283 r3a()
257
284
258 def r3o1():
285 def r3o1():
259 r3a()
286 r3a()
260
287
261 def r3o2():
288 def r3o2():
262 r3o1()
289 r3o1()
263 """
290 """
264 def setUp(self):
291 def setUp(self):
265 ip.run_cell(self.DEFINITIONS)
292 ip.run_cell(self.DEFINITIONS)
266
293
267 def test_no_recursion(self):
294 def test_no_recursion(self):
268 with tt.AssertNotPrints("frames repeated"):
295 with tt.AssertNotPrints("frames repeated"):
269 ip.run_cell("non_recurs()")
296 ip.run_cell("non_recurs()")
270
297
271 def test_recursion_one_frame(self):
298 def test_recursion_one_frame(self):
272 with tt.AssertPrints("1 frames repeated"):
299 with tt.AssertPrints("1 frames repeated"):
273 ip.run_cell("r1()")
300 ip.run_cell("r1()")
274
301
275 def test_recursion_three_frames(self):
302 def test_recursion_three_frames(self):
276 with tt.AssertPrints("3 frames repeated"):
303 with tt.AssertPrints("3 frames repeated"):
277 ip.run_cell("r3o2()")
304 ip.run_cell("r3o2()")
278
305
279 def test_find_recursion(self):
306 def test_find_recursion(self):
280 captured = []
307 captured = []
281 def capture_exc(*args, **kwargs):
308 def capture_exc(*args, **kwargs):
282 captured.append(sys.exc_info())
309 captured.append(sys.exc_info())
283 with mock.patch.object(ip, 'showtraceback', capture_exc):
310 with mock.patch.object(ip, 'showtraceback', capture_exc):
284 ip.run_cell("r3o2()")
311 ip.run_cell("r3o2()")
285
312
286 self.assertEqual(len(captured), 1)
313 self.assertEqual(len(captured), 1)
287 etype, evalue, tb = captured[0]
314 etype, evalue, tb = captured[0]
288 self.assertIn("recursion", str(evalue))
315 self.assertIn("recursion", str(evalue))
289
316
290 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
317 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
291 for r in records[:10]:
318 for r in records[:10]:
292 print(r[1:4])
319 print(r[1:4])
293
320
294 # The outermost frames should be:
321 # The outermost frames should be:
295 # 0: the 'cell' that was running when the exception came up
322 # 0: the 'cell' that was running when the exception came up
296 # 1: r3o2()
323 # 1: r3o2()
297 # 2: r3o1()
324 # 2: r3o1()
298 # 3: r3a()
325 # 3: r3a()
299 # Then repeating r3b, r3c, r3a
326 # Then repeating r3b, r3c, r3a
300 last_unique, repeat_length = find_recursion(etype, evalue, records)
327 last_unique, repeat_length = find_recursion(etype, evalue, records)
301 self.assertEqual(last_unique, 2)
328 self.assertEqual(last_unique, 2)
302 self.assertEqual(repeat_length, 3)
329 self.assertEqual(repeat_length, 3)
303
330
304
331
305 #----------------------------------------------------------------------------
332 #----------------------------------------------------------------------------
306
333
307 # module testing (minimal)
334 # module testing (minimal)
308 def test_handlers():
335 def test_handlers():
309 def spam(c, d_e):
336 def spam(c, d_e):
310 (d, e) = d_e
337 (d, e) = d_e
311 x = c + d
338 x = c + d
312 y = c * d
339 y = c * d
313 foo(x, y)
340 foo(x, y)
314
341
315 def foo(a, b, bar=1):
342 def foo(a, b, bar=1):
316 eggs(a, b + bar)
343 eggs(a, b + bar)
317
344
318 def eggs(f, g, z=globals()):
345 def eggs(f, g, z=globals()):
319 h = f + g
346 h = f + g
320 i = f - g
347 i = f - g
321 return h / i
348 return h / i
322
349
323 buff = io.StringIO()
350 buff = io.StringIO()
324
351
325 buff.write('')
352 buff.write('')
326 buff.write('*** Before ***')
353 buff.write('*** Before ***')
327 try:
354 try:
328 buff.write(spam(1, (2, 3)))
355 buff.write(spam(1, (2, 3)))
329 except:
356 except:
330 traceback.print_exc(file=buff)
357 traceback.print_exc(file=buff)
331
358
332 handler = ColorTB(ostream=buff)
359 handler = ColorTB(ostream=buff)
333 buff.write('*** ColorTB ***')
360 buff.write('*** ColorTB ***')
334 try:
361 try:
335 buff.write(spam(1, (2, 3)))
362 buff.write(spam(1, (2, 3)))
336 except:
363 except:
337 handler(*sys.exc_info())
364 handler(*sys.exc_info())
338 buff.write('')
365 buff.write('')
339
366
340 handler = VerboseTB(ostream=buff)
367 handler = VerboseTB(ostream=buff)
341 buff.write('*** VerboseTB ***')
368 buff.write('*** VerboseTB ***')
342 try:
369 try:
343 buff.write(spam(1, (2, 3)))
370 buff.write(spam(1, (2, 3)))
344 except:
371 except:
345 handler(*sys.exc_info())
372 handler(*sys.exc_info())
346 buff.write('')
373 buff.write('')
347
374
General Comments 0
You need to be logged in to leave comments. Login now