##// END OF EJS Templates
Add test
Quentin Peter -
Show More
@@ -1,440 +1,445
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 logging
5 import logging
6 import sys
6 import sys
7 import os.path
7 import os.path
8 from textwrap import dedent
8 from textwrap import dedent
9 import traceback
9 import traceback
10 import unittest
10 import unittest
11 from unittest import mock
11 from unittest import mock
12
12
13 import IPython.core.ultratb as ultratb
13 import IPython.core.ultratb as ultratb
14 from IPython.core.ultratb import ColorTB, VerboseTB, find_recursion
14 from IPython.core.ultratb import ColorTB, VerboseTB, find_recursion
15
15
16
16
17 from IPython.testing import tools as tt
17 from IPython.testing import tools as tt
18 from IPython.testing.decorators import onlyif_unicode_paths
18 from IPython.testing.decorators import onlyif_unicode_paths
19 from IPython.utils.syspathcontext import prepended_to_syspath
19 from IPython.utils.syspathcontext import prepended_to_syspath
20 from IPython.utils.tempdir import TemporaryDirectory
20 from IPython.utils.tempdir import TemporaryDirectory
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
33
34 def recursionlimit(frames):
34 def recursionlimit(frames):
35 """
35 """
36 decorator to set the recursion limit temporarily
36 decorator to set the recursion limit temporarily
37 """
37 """
38
38
39 def inner(test_function):
39 def inner(test_function):
40 def wrapper(*args, **kwargs):
40 def wrapper(*args, **kwargs):
41 _orig_rec_limit = ultratb._FRAME_RECURSION_LIMIT
41 _orig_rec_limit = ultratb._FRAME_RECURSION_LIMIT
42 ultratb._FRAME_RECURSION_LIMIT = 50
42 ultratb._FRAME_RECURSION_LIMIT = 50
43
43
44 rl = sys.getrecursionlimit()
44 rl = sys.getrecursionlimit()
45 sys.setrecursionlimit(frames)
45 sys.setrecursionlimit(frames)
46 try:
46 try:
47 return test_function(*args, **kwargs)
47 return test_function(*args, **kwargs)
48 finally:
48 finally:
49 sys.setrecursionlimit(rl)
49 sys.setrecursionlimit(rl)
50 ultratb._FRAME_RECURSION_LIMIT = _orig_rec_limit
50 ultratb._FRAME_RECURSION_LIMIT = _orig_rec_limit
51
51
52 return wrapper
52 return wrapper
53
53
54 return inner
54 return inner
55
55
56
56
57 class ChangedPyFileTest(unittest.TestCase):
57 class ChangedPyFileTest(unittest.TestCase):
58 def test_changing_py_file(self):
58 def test_changing_py_file(self):
59 """Traceback produced if the line where the error occurred is missing?
59 """Traceback produced if the line where the error occurred is missing?
60
60
61 https://github.com/ipython/ipython/issues/1456
61 https://github.com/ipython/ipython/issues/1456
62 """
62 """
63 with TemporaryDirectory() as td:
63 with TemporaryDirectory() as td:
64 fname = os.path.join(td, "foo.py")
64 fname = os.path.join(td, "foo.py")
65 with open(fname, "w") as f:
65 with open(fname, "w") as f:
66 f.write(file_1)
66 f.write(file_1)
67
67
68 with prepended_to_syspath(td):
68 with prepended_to_syspath(td):
69 ip.run_cell("import foo")
69 ip.run_cell("import foo")
70
70
71 with tt.AssertPrints("ZeroDivisionError"):
71 with tt.AssertPrints("ZeroDivisionError"):
72 ip.run_cell("foo.f()")
72 ip.run_cell("foo.f()")
73
73
74 # Make the file shorter, so the line of the error is missing.
74 # Make the file shorter, so the line of the error is missing.
75 with open(fname, "w") as f:
75 with open(fname, "w") as f:
76 f.write(file_2)
76 f.write(file_2)
77
77
78 # For some reason, this was failing on the *second* call after
78 # For some reason, this was failing on the *second* call after
79 # changing the file, so we call f() twice.
79 # changing the file, so we call f() twice.
80 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
80 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
81 with tt.AssertPrints("ZeroDivisionError"):
81 with tt.AssertPrints("ZeroDivisionError"):
82 ip.run_cell("foo.f()")
82 ip.run_cell("foo.f()")
83 with tt.AssertPrints("ZeroDivisionError"):
83 with tt.AssertPrints("ZeroDivisionError"):
84 ip.run_cell("foo.f()")
84 ip.run_cell("foo.f()")
85
85
86 iso_8859_5_file = u'''# coding: iso-8859-5
86 iso_8859_5_file = u'''# coding: iso-8859-5
87
87
88 def fail():
88 def fail():
89 """Π΄Π±Π˜Π–"""
89 """Π΄Π±Π˜Π–"""
90 1/0 # Π΄Π±Π˜Π–
90 1/0 # Π΄Π±Π˜Π–
91 '''
91 '''
92
92
93 class NonAsciiTest(unittest.TestCase):
93 class NonAsciiTest(unittest.TestCase):
94 @onlyif_unicode_paths
94 @onlyif_unicode_paths
95 def test_nonascii_path(self):
95 def test_nonascii_path(self):
96 # Non-ascii directory name as well.
96 # Non-ascii directory name as well.
97 with TemporaryDirectory(suffix=u'Γ©') as td:
97 with TemporaryDirectory(suffix=u'Γ©') as td:
98 fname = os.path.join(td, u"fooΓ©.py")
98 fname = os.path.join(td, u"fooΓ©.py")
99 with open(fname, "w") as f:
99 with open(fname, "w") as f:
100 f.write(file_1)
100 f.write(file_1)
101
101
102 with prepended_to_syspath(td):
102 with prepended_to_syspath(td):
103 ip.run_cell("import foo")
103 ip.run_cell("import foo")
104
104
105 with tt.AssertPrints("ZeroDivisionError"):
105 with tt.AssertPrints("ZeroDivisionError"):
106 ip.run_cell("foo.f()")
106 ip.run_cell("foo.f()")
107
107
108 def test_iso8859_5(self):
108 def test_iso8859_5(self):
109 with TemporaryDirectory() as td:
109 with TemporaryDirectory() as td:
110 fname = os.path.join(td, 'dfghjkl.py')
110 fname = os.path.join(td, 'dfghjkl.py')
111
111
112 with io.open(fname, 'w', encoding='iso-8859-5') as f:
112 with io.open(fname, 'w', encoding='iso-8859-5') as f:
113 f.write(iso_8859_5_file)
113 f.write(iso_8859_5_file)
114
114
115 with prepended_to_syspath(td):
115 with prepended_to_syspath(td):
116 ip.run_cell("from dfghjkl import fail")
116 ip.run_cell("from dfghjkl import fail")
117
117
118 with tt.AssertPrints("ZeroDivisionError"):
118 with tt.AssertPrints("ZeroDivisionError"):
119 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
119 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
120 ip.run_cell('fail()')
120 ip.run_cell('fail()')
121
121
122 def test_nonascii_msg(self):
122 def test_nonascii_msg(self):
123 cell = u"raise Exception('Γ©')"
123 cell = u"raise Exception('Γ©')"
124 expected = u"Exception('Γ©')"
124 expected = u"Exception('Γ©')"
125 ip.run_cell("%xmode plain")
125 ip.run_cell("%xmode plain")
126 with tt.AssertPrints(expected):
126 with tt.AssertPrints(expected):
127 ip.run_cell(cell)
127 ip.run_cell(cell)
128
128
129 ip.run_cell("%xmode verbose")
129 ip.run_cell("%xmode verbose")
130 with tt.AssertPrints(expected):
130 with tt.AssertPrints(expected):
131 ip.run_cell(cell)
131 ip.run_cell(cell)
132
132
133 ip.run_cell("%xmode context")
133 ip.run_cell("%xmode context")
134 with tt.AssertPrints(expected):
134 with tt.AssertPrints(expected):
135 ip.run_cell(cell)
135 ip.run_cell(cell)
136
136
137 ip.run_cell("%xmode minimal")
137 ip.run_cell("%xmode minimal")
138 with tt.AssertPrints(u"Exception: Γ©"):
138 with tt.AssertPrints(u"Exception: Γ©"):
139 ip.run_cell(cell)
139 ip.run_cell(cell)
140
140
141 # Put this back into Context mode for later tests.
141 # Put this back into Context mode for later tests.
142 ip.run_cell("%xmode context")
142 ip.run_cell("%xmode context")
143
143
144 class NestedGenExprTestCase(unittest.TestCase):
144 class NestedGenExprTestCase(unittest.TestCase):
145 """
145 """
146 Regression test for the following issues:
146 Regression test for the following issues:
147 https://github.com/ipython/ipython/issues/8293
147 https://github.com/ipython/ipython/issues/8293
148 https://github.com/ipython/ipython/issues/8205
148 https://github.com/ipython/ipython/issues/8205
149 """
149 """
150 def test_nested_genexpr(self):
150 def test_nested_genexpr(self):
151 code = dedent(
151 code = dedent(
152 """\
152 """\
153 class SpecificException(Exception):
153 class SpecificException(Exception):
154 pass
154 pass
155
155
156 def foo(x):
156 def foo(x):
157 raise SpecificException("Success!")
157 raise SpecificException("Success!")
158
158
159 sum(sum(foo(x) for _ in [0]) for x in [0])
159 sum(sum(foo(x) for _ in [0]) for x in [0])
160 """
160 """
161 )
161 )
162 with tt.AssertPrints('SpecificException: Success!', suppress=False):
162 with tt.AssertPrints('SpecificException: Success!', suppress=False):
163 ip.run_cell(code)
163 ip.run_cell(code)
164
164
165
165
166 indentationerror_file = """if True:
166 indentationerror_file = """if True:
167 zoon()
167 zoon()
168 """
168 """
169
169
170 class IndentationErrorTest(unittest.TestCase):
170 class IndentationErrorTest(unittest.TestCase):
171 def test_indentationerror_shows_line(self):
171 def test_indentationerror_shows_line(self):
172 # See issue gh-2398
172 # See issue gh-2398
173 with tt.AssertPrints("IndentationError"):
173 with tt.AssertPrints("IndentationError"):
174 with tt.AssertPrints("zoon()", suppress=False):
174 with tt.AssertPrints("zoon()", suppress=False):
175 ip.run_cell(indentationerror_file)
175 ip.run_cell(indentationerror_file)
176
176
177 with TemporaryDirectory() as td:
177 with TemporaryDirectory() as td:
178 fname = os.path.join(td, "foo.py")
178 fname = os.path.join(td, "foo.py")
179 with open(fname, "w") as f:
179 with open(fname, "w") as f:
180 f.write(indentationerror_file)
180 f.write(indentationerror_file)
181
181
182 with tt.AssertPrints("IndentationError"):
182 with tt.AssertPrints("IndentationError"):
183 with tt.AssertPrints("zoon()", suppress=False):
183 with tt.AssertPrints("zoon()", suppress=False):
184 ip.magic('run %s' % fname)
184 ip.magic('run %s' % fname)
185
185
186 se_file_1 = """1
186 se_file_1 = """1
187 2
187 2
188 7/
188 7/
189 """
189 """
190
190
191 se_file_2 = """7/
191 se_file_2 = """7/
192 """
192 """
193
193
194 class SyntaxErrorTest(unittest.TestCase):
194 class SyntaxErrorTest(unittest.TestCase):
195 def test_syntaxerror_without_lineno(self):
195 def test_syntaxerror_without_lineno(self):
196 with tt.AssertNotPrints("TypeError"):
196 with tt.AssertNotPrints("TypeError"):
197 with tt.AssertPrints("line unknown"):
197 with tt.AssertPrints("line unknown"):
198 ip.run_cell("raise SyntaxError()")
198 ip.run_cell("raise SyntaxError()")
199
199
200 def test_syntaxerror_no_stacktrace_at_compile_time(self):
200 def test_syntaxerror_no_stacktrace_at_compile_time(self):
201 syntax_error_at_compile_time = """
201 syntax_error_at_compile_time = """
202 def foo():
202 def foo():
203 ..
203 ..
204 """
204 """
205 with tt.AssertPrints("SyntaxError"):
205 with tt.AssertPrints("SyntaxError"):
206 ip.run_cell(syntax_error_at_compile_time)
206 ip.run_cell(syntax_error_at_compile_time)
207
207
208 with tt.AssertNotPrints("foo()"):
208 with tt.AssertNotPrints("foo()"):
209 ip.run_cell(syntax_error_at_compile_time)
209 ip.run_cell(syntax_error_at_compile_time)
210
210
211 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
211 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
212 syntax_error_at_runtime = """
212 syntax_error_at_runtime = """
213 def foo():
213 def foo():
214 eval("..")
214 eval("..")
215
215
216 def bar():
216 def bar():
217 foo()
217 foo()
218
218
219 bar()
219 bar()
220 """
220 """
221 with tt.AssertPrints("SyntaxError"):
221 with tt.AssertPrints("SyntaxError"):
222 ip.run_cell(syntax_error_at_runtime)
222 ip.run_cell(syntax_error_at_runtime)
223 # Assert syntax error during runtime generate stacktrace
223 # Assert syntax error during runtime generate stacktrace
224 with tt.AssertPrints(["foo()", "bar()"]):
224 with tt.AssertPrints(["foo()", "bar()"]):
225 ip.run_cell(syntax_error_at_runtime)
225 ip.run_cell(syntax_error_at_runtime)
226 del ip.user_ns['bar']
226 del ip.user_ns['bar']
227 del ip.user_ns['foo']
227 del ip.user_ns['foo']
228
228
229 def test_changing_py_file(self):
229 def test_changing_py_file(self):
230 with TemporaryDirectory() as td:
230 with TemporaryDirectory() as td:
231 fname = os.path.join(td, "foo.py")
231 fname = os.path.join(td, "foo.py")
232 with open(fname, 'w') as f:
232 with open(fname, 'w') as f:
233 f.write(se_file_1)
233 f.write(se_file_1)
234
234
235 with tt.AssertPrints(["7/", "SyntaxError"]):
235 with tt.AssertPrints(["7/", "SyntaxError"]):
236 ip.magic("run " + fname)
236 ip.magic("run " + fname)
237
237
238 # Modify the file
238 # Modify the file
239 with open(fname, 'w') as f:
239 with open(fname, 'w') as f:
240 f.write(se_file_2)
240 f.write(se_file_2)
241
241
242 # The SyntaxError should point to the correct line
242 # The SyntaxError should point to the correct line
243 with tt.AssertPrints(["7/", "SyntaxError"]):
243 with tt.AssertPrints(["7/", "SyntaxError"]):
244 ip.magic("run " + fname)
244 ip.magic("run " + fname)
245
245
246 def test_non_syntaxerror(self):
246 def test_non_syntaxerror(self):
247 # SyntaxTB may be called with an error other than a SyntaxError
247 # SyntaxTB may be called with an error other than a SyntaxError
248 # See e.g. gh-4361
248 # See e.g. gh-4361
249 try:
249 try:
250 raise ValueError('QWERTY')
250 raise ValueError('QWERTY')
251 except ValueError:
251 except ValueError:
252 with tt.AssertPrints('QWERTY'):
252 with tt.AssertPrints('QWERTY'):
253 ip.showsyntaxerror()
253 ip.showsyntaxerror()
254
254
255
255
256 class Python3ChainedExceptionsTest(unittest.TestCase):
256 class Python3ChainedExceptionsTest(unittest.TestCase):
257 DIRECT_CAUSE_ERROR_CODE = """
257 DIRECT_CAUSE_ERROR_CODE = """
258 try:
258 try:
259 x = 1 + 2
259 x = 1 + 2
260 print(not_defined_here)
260 print(not_defined_here)
261 except Exception as e:
261 except Exception as e:
262 x += 55
262 x += 55
263 x - 1
263 x - 1
264 y = {}
264 y = {}
265 raise KeyError('uh') from e
265 raise KeyError('uh') from e
266 """
266 """
267
267
268 EXCEPTION_DURING_HANDLING_CODE = """
268 EXCEPTION_DURING_HANDLING_CODE = """
269 try:
269 try:
270 x = 1 + 2
270 x = 1 + 2
271 print(not_defined_here)
271 print(not_defined_here)
272 except Exception as e:
272 except Exception as e:
273 x += 55
273 x += 55
274 x - 1
274 x - 1
275 y = {}
275 y = {}
276 raise KeyError('uh')
276 raise KeyError('uh')
277 """
277 """
278
278
279 SUPPRESS_CHAINING_CODE = """
279 SUPPRESS_CHAINING_CODE = """
280 try:
280 try:
281 1/0
281 1/0
282 except Exception:
282 except Exception:
283 raise ValueError("Yikes") from None
283 raise ValueError("Yikes") from None
284 """
284 """
285
285
286 def test_direct_cause_error(self):
286 def test_direct_cause_error(self):
287 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
287 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
288 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
288 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
289
289
290 def test_exception_during_handling_error(self):
290 def test_exception_during_handling_error(self):
291 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
291 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
292 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
292 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
293
293
294 def test_suppress_exception_chaining(self):
294 def test_suppress_exception_chaining(self):
295 with tt.AssertNotPrints("ZeroDivisionError"), \
295 with tt.AssertNotPrints("ZeroDivisionError"), \
296 tt.AssertPrints("ValueError", suppress=False):
296 tt.AssertPrints("ValueError", suppress=False):
297 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
297 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
298
298
299 def test_plain_exception(self):
300 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
301 ip.run_cell("%xmode Plain")
302 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
303
299
304
300 class RecursionTest(unittest.TestCase):
305 class RecursionTest(unittest.TestCase):
301 DEFINITIONS = """
306 DEFINITIONS = """
302 def non_recurs():
307 def non_recurs():
303 1/0
308 1/0
304
309
305 def r1():
310 def r1():
306 r1()
311 r1()
307
312
308 def r3a():
313 def r3a():
309 r3b()
314 r3b()
310
315
311 def r3b():
316 def r3b():
312 r3c()
317 r3c()
313
318
314 def r3c():
319 def r3c():
315 r3a()
320 r3a()
316
321
317 def r3o1():
322 def r3o1():
318 r3a()
323 r3a()
319
324
320 def r3o2():
325 def r3o2():
321 r3o1()
326 r3o1()
322 """
327 """
323 def setUp(self):
328 def setUp(self):
324 ip.run_cell(self.DEFINITIONS)
329 ip.run_cell(self.DEFINITIONS)
325
330
326 def test_no_recursion(self):
331 def test_no_recursion(self):
327 with tt.AssertNotPrints("frames repeated"):
332 with tt.AssertNotPrints("frames repeated"):
328 ip.run_cell("non_recurs()")
333 ip.run_cell("non_recurs()")
329
334
330 @recursionlimit(150)
335 @recursionlimit(150)
331 def test_recursion_one_frame(self):
336 def test_recursion_one_frame(self):
332 with tt.AssertPrints("1 frames repeated"):
337 with tt.AssertPrints("1 frames repeated"):
333 ip.run_cell("r1()")
338 ip.run_cell("r1()")
334
339
335 @recursionlimit(150)
340 @recursionlimit(150)
336 def test_recursion_three_frames(self):
341 def test_recursion_three_frames(self):
337 with tt.AssertPrints("3 frames repeated"):
342 with tt.AssertPrints("3 frames repeated"):
338 ip.run_cell("r3o2()")
343 ip.run_cell("r3o2()")
339
344
340 @recursionlimit(150)
345 @recursionlimit(150)
341 def test_find_recursion(self):
346 def test_find_recursion(self):
342 captured = []
347 captured = []
343 def capture_exc(*args, **kwargs):
348 def capture_exc(*args, **kwargs):
344 captured.append(sys.exc_info())
349 captured.append(sys.exc_info())
345 with mock.patch.object(ip, 'showtraceback', capture_exc):
350 with mock.patch.object(ip, 'showtraceback', capture_exc):
346 ip.run_cell("r3o2()")
351 ip.run_cell("r3o2()")
347
352
348 self.assertEqual(len(captured), 1)
353 self.assertEqual(len(captured), 1)
349 etype, evalue, tb = captured[0]
354 etype, evalue, tb = captured[0]
350 self.assertIn("recursion", str(evalue))
355 self.assertIn("recursion", str(evalue))
351
356
352 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
357 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
353 for r in records[:10]:
358 for r in records[:10]:
354 print(r[1:4])
359 print(r[1:4])
355
360
356 # The outermost frames should be:
361 # The outermost frames should be:
357 # 0: the 'cell' that was running when the exception came up
362 # 0: the 'cell' that was running when the exception came up
358 # 1: r3o2()
363 # 1: r3o2()
359 # 2: r3o1()
364 # 2: r3o1()
360 # 3: r3a()
365 # 3: r3a()
361 # Then repeating r3b, r3c, r3a
366 # Then repeating r3b, r3c, r3a
362 last_unique, repeat_length = find_recursion(etype, evalue, records)
367 last_unique, repeat_length = find_recursion(etype, evalue, records)
363 self.assertEqual(last_unique, 2)
368 self.assertEqual(last_unique, 2)
364 self.assertEqual(repeat_length, 3)
369 self.assertEqual(repeat_length, 3)
365
370
366
371
367 #----------------------------------------------------------------------------
372 #----------------------------------------------------------------------------
368
373
369 # module testing (minimal)
374 # module testing (minimal)
370 def test_handlers():
375 def test_handlers():
371 def spam(c, d_e):
376 def spam(c, d_e):
372 (d, e) = d_e
377 (d, e) = d_e
373 x = c + d
378 x = c + d
374 y = c * d
379 y = c * d
375 foo(x, y)
380 foo(x, y)
376
381
377 def foo(a, b, bar=1):
382 def foo(a, b, bar=1):
378 eggs(a, b + bar)
383 eggs(a, b + bar)
379
384
380 def eggs(f, g, z=globals()):
385 def eggs(f, g, z=globals()):
381 h = f + g
386 h = f + g
382 i = f - g
387 i = f - g
383 return h / i
388 return h / i
384
389
385 buff = io.StringIO()
390 buff = io.StringIO()
386
391
387 buff.write('')
392 buff.write('')
388 buff.write('*** Before ***')
393 buff.write('*** Before ***')
389 try:
394 try:
390 buff.write(spam(1, (2, 3)))
395 buff.write(spam(1, (2, 3)))
391 except:
396 except:
392 traceback.print_exc(file=buff)
397 traceback.print_exc(file=buff)
393
398
394 handler = ColorTB(ostream=buff)
399 handler = ColorTB(ostream=buff)
395 buff.write('*** ColorTB ***')
400 buff.write('*** ColorTB ***')
396 try:
401 try:
397 buff.write(spam(1, (2, 3)))
402 buff.write(spam(1, (2, 3)))
398 except:
403 except:
399 handler(*sys.exc_info())
404 handler(*sys.exc_info())
400 buff.write('')
405 buff.write('')
401
406
402 handler = VerboseTB(ostream=buff)
407 handler = VerboseTB(ostream=buff)
403 buff.write('*** VerboseTB ***')
408 buff.write('*** VerboseTB ***')
404 try:
409 try:
405 buff.write(spam(1, (2, 3)))
410 buff.write(spam(1, (2, 3)))
406 except:
411 except:
407 handler(*sys.exc_info())
412 handler(*sys.exc_info())
408 buff.write('')
413 buff.write('')
409
414
410 from IPython.testing.decorators import skipif
415 from IPython.testing.decorators import skipif
411
416
412 class TokenizeFailureTest(unittest.TestCase):
417 class TokenizeFailureTest(unittest.TestCase):
413 """Tests related to https://github.com/ipython/ipython/issues/6864."""
418 """Tests related to https://github.com/ipython/ipython/issues/6864."""
414
419
415 # that appear to test that we are handling an exception that can be thrown
420 # that appear to test that we are handling an exception that can be thrown
416 # by the tokenizer due to a bug that seem to have been fixed in 3.8, though
421 # by the tokenizer due to a bug that seem to have been fixed in 3.8, though
417 # I'm unsure if other sequences can make it raise this error. Let's just
422 # I'm unsure if other sequences can make it raise this error. Let's just
418 # skip in 3.8 for now
423 # skip in 3.8 for now
419 @skipif(sys.version_info > (3,8))
424 @skipif(sys.version_info > (3,8))
420 def testLogging(self):
425 def testLogging(self):
421 message = "An unexpected error occurred while tokenizing input"
426 message = "An unexpected error occurred while tokenizing input"
422 cell = 'raise ValueError("""a\nb""")'
427 cell = 'raise ValueError("""a\nb""")'
423
428
424 stream = io.StringIO()
429 stream = io.StringIO()
425 handler = logging.StreamHandler(stream)
430 handler = logging.StreamHandler(stream)
426 logger = logging.getLogger()
431 logger = logging.getLogger()
427 loglevel = logger.level
432 loglevel = logger.level
428 logger.addHandler(handler)
433 logger.addHandler(handler)
429 self.addCleanup(lambda: logger.removeHandler(handler))
434 self.addCleanup(lambda: logger.removeHandler(handler))
430 self.addCleanup(lambda: logger.setLevel(loglevel))
435 self.addCleanup(lambda: logger.setLevel(loglevel))
431
436
432 logger.setLevel(logging.INFO)
437 logger.setLevel(logging.INFO)
433 with tt.AssertNotPrints(message):
438 with tt.AssertNotPrints(message):
434 ip.run_cell(cell)
439 ip.run_cell(cell)
435 self.assertNotIn(message, stream.getvalue())
440 self.assertNotIn(message, stream.getvalue())
436
441
437 logger.setLevel(logging.DEBUG)
442 logger.setLevel(logging.DEBUG)
438 with tt.AssertNotPrints(message):
443 with tt.AssertNotPrints(message):
439 ip.run_cell(cell)
444 ip.run_cell(cell)
440 self.assertIn(message, stream.getvalue())
445 self.assertIn(message, stream.getvalue())
General Comments 0
You need to be logged in to leave comments. Login now