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