##// END OF EJS Templates
Merge pull request #10841 from craigcitro/noisy...
Thomas Kluyver -
r23978:7e28a947 merge
parent child Browse files
Show More
@@ -1,374 +1,400 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 logging
5 import sys
6 import sys
6 import os.path
7 import os.path
7 from textwrap import dedent
8 from textwrap import dedent
8 import traceback
9 import traceback
9 import unittest
10 import unittest
10 from unittest import mock
11 from unittest import mock
11
12
12 from ..ultratb import ColorTB, VerboseTB, find_recursion
13 from ..ultratb import ColorTB, VerboseTB, find_recursion
13
14
14
15
15 from IPython.testing import tools as tt
16 from IPython.testing import tools as tt
16 from IPython.testing.decorators import onlyif_unicode_paths
17 from IPython.testing.decorators import onlyif_unicode_paths
17 from IPython.utils.syspathcontext import prepended_to_syspath
18 from IPython.utils.syspathcontext import prepended_to_syspath
18 from IPython.utils.tempdir import TemporaryDirectory
19 from IPython.utils.tempdir import TemporaryDirectory
19
20
20 ip = get_ipython()
21 ip = get_ipython()
21
22
22 file_1 = """1
23 file_1 = """1
23 2
24 2
24 3
25 3
25 def f():
26 def f():
26 1/0
27 1/0
27 """
28 """
28
29
29 file_2 = """def f():
30 file_2 = """def f():
30 1/0
31 1/0
31 """
32 """
32
33
33 class ChangedPyFileTest(unittest.TestCase):
34 class ChangedPyFileTest(unittest.TestCase):
34 def test_changing_py_file(self):
35 def test_changing_py_file(self):
35 """Traceback produced if the line where the error occurred is missing?
36 """Traceback produced if the line where the error occurred is missing?
36
37
37 https://github.com/ipython/ipython/issues/1456
38 https://github.com/ipython/ipython/issues/1456
38 """
39 """
39 with TemporaryDirectory() as td:
40 with TemporaryDirectory() as td:
40 fname = os.path.join(td, "foo.py")
41 fname = os.path.join(td, "foo.py")
41 with open(fname, "w") as f:
42 with open(fname, "w") as f:
42 f.write(file_1)
43 f.write(file_1)
43
44
44 with prepended_to_syspath(td):
45 with prepended_to_syspath(td):
45 ip.run_cell("import foo")
46 ip.run_cell("import foo")
46
47
47 with tt.AssertPrints("ZeroDivisionError"):
48 with tt.AssertPrints("ZeroDivisionError"):
48 ip.run_cell("foo.f()")
49 ip.run_cell("foo.f()")
49
50
50 # Make the file shorter, so the line of the error is missing.
51 # Make the file shorter, so the line of the error is missing.
51 with open(fname, "w") as f:
52 with open(fname, "w") as f:
52 f.write(file_2)
53 f.write(file_2)
53
54
54 # For some reason, this was failing on the *second* call after
55 # For some reason, this was failing on the *second* call after
55 # changing the file, so we call f() twice.
56 # changing the file, so we call f() twice.
56 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
57 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
57 with tt.AssertPrints("ZeroDivisionError"):
58 with tt.AssertPrints("ZeroDivisionError"):
58 ip.run_cell("foo.f()")
59 ip.run_cell("foo.f()")
59 with tt.AssertPrints("ZeroDivisionError"):
60 with tt.AssertPrints("ZeroDivisionError"):
60 ip.run_cell("foo.f()")
61 ip.run_cell("foo.f()")
61
62
62 iso_8859_5_file = u'''# coding: iso-8859-5
63 iso_8859_5_file = u'''# coding: iso-8859-5
63
64
64 def fail():
65 def fail():
65 """Π΄Π±Π˜Π–"""
66 """Π΄Π±Π˜Π–"""
66 1/0 # Π΄Π±Π˜Π–
67 1/0 # Π΄Π±Π˜Π–
67 '''
68 '''
68
69
69 class NonAsciiTest(unittest.TestCase):
70 class NonAsciiTest(unittest.TestCase):
70 @onlyif_unicode_paths
71 @onlyif_unicode_paths
71 def test_nonascii_path(self):
72 def test_nonascii_path(self):
72 # Non-ascii directory name as well.
73 # Non-ascii directory name as well.
73 with TemporaryDirectory(suffix=u'Γ©') as td:
74 with TemporaryDirectory(suffix=u'Γ©') as td:
74 fname = os.path.join(td, u"fooΓ©.py")
75 fname = os.path.join(td, u"fooΓ©.py")
75 with open(fname, "w") as f:
76 with open(fname, "w") as f:
76 f.write(file_1)
77 f.write(file_1)
77
78
78 with prepended_to_syspath(td):
79 with prepended_to_syspath(td):
79 ip.run_cell("import foo")
80 ip.run_cell("import foo")
80
81
81 with tt.AssertPrints("ZeroDivisionError"):
82 with tt.AssertPrints("ZeroDivisionError"):
82 ip.run_cell("foo.f()")
83 ip.run_cell("foo.f()")
83
84
84 def test_iso8859_5(self):
85 def test_iso8859_5(self):
85 with TemporaryDirectory() as td:
86 with TemporaryDirectory() as td:
86 fname = os.path.join(td, 'dfghjkl.py')
87 fname = os.path.join(td, 'dfghjkl.py')
87
88
88 with io.open(fname, 'w', encoding='iso-8859-5') as f:
89 with io.open(fname, 'w', encoding='iso-8859-5') as f:
89 f.write(iso_8859_5_file)
90 f.write(iso_8859_5_file)
90
91
91 with prepended_to_syspath(td):
92 with prepended_to_syspath(td):
92 ip.run_cell("from dfghjkl import fail")
93 ip.run_cell("from dfghjkl import fail")
93
94
94 with tt.AssertPrints("ZeroDivisionError"):
95 with tt.AssertPrints("ZeroDivisionError"):
95 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
96 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
96 ip.run_cell('fail()')
97 ip.run_cell('fail()')
97
98
98 def test_nonascii_msg(self):
99 def test_nonascii_msg(self):
99 cell = u"raise Exception('Γ©')"
100 cell = u"raise Exception('Γ©')"
100 expected = u"Exception('Γ©')"
101 expected = u"Exception('Γ©')"
101 ip.run_cell("%xmode plain")
102 ip.run_cell("%xmode plain")
102 with tt.AssertPrints(expected):
103 with tt.AssertPrints(expected):
103 ip.run_cell(cell)
104 ip.run_cell(cell)
104
105
105 ip.run_cell("%xmode verbose")
106 ip.run_cell("%xmode verbose")
106 with tt.AssertPrints(expected):
107 with tt.AssertPrints(expected):
107 ip.run_cell(cell)
108 ip.run_cell(cell)
108
109
109 ip.run_cell("%xmode context")
110 ip.run_cell("%xmode context")
110 with tt.AssertPrints(expected):
111 with tt.AssertPrints(expected):
111 ip.run_cell(cell)
112 ip.run_cell(cell)
112
113
113
114
114 class NestedGenExprTestCase(unittest.TestCase):
115 class NestedGenExprTestCase(unittest.TestCase):
115 """
116 """
116 Regression test for the following issues:
117 Regression test for the following issues:
117 https://github.com/ipython/ipython/issues/8293
118 https://github.com/ipython/ipython/issues/8293
118 https://github.com/ipython/ipython/issues/8205
119 https://github.com/ipython/ipython/issues/8205
119 """
120 """
120 def test_nested_genexpr(self):
121 def test_nested_genexpr(self):
121 code = dedent(
122 code = dedent(
122 """\
123 """\
123 class SpecificException(Exception):
124 class SpecificException(Exception):
124 pass
125 pass
125
126
126 def foo(x):
127 def foo(x):
127 raise SpecificException("Success!")
128 raise SpecificException("Success!")
128
129
129 sum(sum(foo(x) for _ in [0]) for x in [0])
130 sum(sum(foo(x) for _ in [0]) for x in [0])
130 """
131 """
131 )
132 )
132 with tt.AssertPrints('SpecificException: Success!', suppress=False):
133 with tt.AssertPrints('SpecificException: Success!', suppress=False):
133 ip.run_cell(code)
134 ip.run_cell(code)
134
135
135
136
136 indentationerror_file = """if True:
137 indentationerror_file = """if True:
137 zoon()
138 zoon()
138 """
139 """
139
140
140 class IndentationErrorTest(unittest.TestCase):
141 class IndentationErrorTest(unittest.TestCase):
141 def test_indentationerror_shows_line(self):
142 def test_indentationerror_shows_line(self):
142 # See issue gh-2398
143 # See issue gh-2398
143 with tt.AssertPrints("IndentationError"):
144 with tt.AssertPrints("IndentationError"):
144 with tt.AssertPrints("zoon()", suppress=False):
145 with tt.AssertPrints("zoon()", suppress=False):
145 ip.run_cell(indentationerror_file)
146 ip.run_cell(indentationerror_file)
146
147
147 with TemporaryDirectory() as td:
148 with TemporaryDirectory() as td:
148 fname = os.path.join(td, "foo.py")
149 fname = os.path.join(td, "foo.py")
149 with open(fname, "w") as f:
150 with open(fname, "w") as f:
150 f.write(indentationerror_file)
151 f.write(indentationerror_file)
151
152
152 with tt.AssertPrints("IndentationError"):
153 with tt.AssertPrints("IndentationError"):
153 with tt.AssertPrints("zoon()", suppress=False):
154 with tt.AssertPrints("zoon()", suppress=False):
154 ip.magic('run %s' % fname)
155 ip.magic('run %s' % fname)
155
156
156 se_file_1 = """1
157 se_file_1 = """1
157 2
158 2
158 7/
159 7/
159 """
160 """
160
161
161 se_file_2 = """7/
162 se_file_2 = """7/
162 """
163 """
163
164
164 class SyntaxErrorTest(unittest.TestCase):
165 class SyntaxErrorTest(unittest.TestCase):
165 def test_syntaxerror_without_lineno(self):
166 def test_syntaxerror_without_lineno(self):
166 with tt.AssertNotPrints("TypeError"):
167 with tt.AssertNotPrints("TypeError"):
167 with tt.AssertPrints("line unknown"):
168 with tt.AssertPrints("line unknown"):
168 ip.run_cell("raise SyntaxError()")
169 ip.run_cell("raise SyntaxError()")
169
170
170 def test_syntaxerror_no_stacktrace_at_compile_time(self):
171 def test_syntaxerror_no_stacktrace_at_compile_time(self):
171 syntax_error_at_compile_time = """
172 syntax_error_at_compile_time = """
172 def foo():
173 def foo():
173 ..
174 ..
174 """
175 """
175 with tt.AssertPrints("SyntaxError"):
176 with tt.AssertPrints("SyntaxError"):
176 ip.run_cell(syntax_error_at_compile_time)
177 ip.run_cell(syntax_error_at_compile_time)
177
178
178 with tt.AssertNotPrints("foo()"):
179 with tt.AssertNotPrints("foo()"):
179 ip.run_cell(syntax_error_at_compile_time)
180 ip.run_cell(syntax_error_at_compile_time)
180
181
181 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
182 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
182 syntax_error_at_runtime = """
183 syntax_error_at_runtime = """
183 def foo():
184 def foo():
184 eval("..")
185 eval("..")
185
186
186 def bar():
187 def bar():
187 foo()
188 foo()
188
189
189 bar()
190 bar()
190 """
191 """
191 with tt.AssertPrints("SyntaxError"):
192 with tt.AssertPrints("SyntaxError"):
192 ip.run_cell(syntax_error_at_runtime)
193 ip.run_cell(syntax_error_at_runtime)
193 # Assert syntax error during runtime generate stacktrace
194 # Assert syntax error during runtime generate stacktrace
194 with tt.AssertPrints(["foo()", "bar()"]):
195 with tt.AssertPrints(["foo()", "bar()"]):
195 ip.run_cell(syntax_error_at_runtime)
196 ip.run_cell(syntax_error_at_runtime)
196
197
197 def test_changing_py_file(self):
198 def test_changing_py_file(self):
198 with TemporaryDirectory() as td:
199 with TemporaryDirectory() as td:
199 fname = os.path.join(td, "foo.py")
200 fname = os.path.join(td, "foo.py")
200 with open(fname, 'w') as f:
201 with open(fname, 'w') as f:
201 f.write(se_file_1)
202 f.write(se_file_1)
202
203
203 with tt.AssertPrints(["7/", "SyntaxError"]):
204 with tt.AssertPrints(["7/", "SyntaxError"]):
204 ip.magic("run " + fname)
205 ip.magic("run " + fname)
205
206
206 # Modify the file
207 # Modify the file
207 with open(fname, 'w') as f:
208 with open(fname, 'w') as f:
208 f.write(se_file_2)
209 f.write(se_file_2)
209
210
210 # The SyntaxError should point to the correct line
211 # The SyntaxError should point to the correct line
211 with tt.AssertPrints(["7/", "SyntaxError"]):
212 with tt.AssertPrints(["7/", "SyntaxError"]):
212 ip.magic("run " + fname)
213 ip.magic("run " + fname)
213
214
214 def test_non_syntaxerror(self):
215 def test_non_syntaxerror(self):
215 # SyntaxTB may be called with an error other than a SyntaxError
216 # SyntaxTB may be called with an error other than a SyntaxError
216 # See e.g. gh-4361
217 # See e.g. gh-4361
217 try:
218 try:
218 raise ValueError('QWERTY')
219 raise ValueError('QWERTY')
219 except ValueError:
220 except ValueError:
220 with tt.AssertPrints('QWERTY'):
221 with tt.AssertPrints('QWERTY'):
221 ip.showsyntaxerror()
222 ip.showsyntaxerror()
222
223
223
224
224 class Python3ChainedExceptionsTest(unittest.TestCase):
225 class Python3ChainedExceptionsTest(unittest.TestCase):
225 DIRECT_CAUSE_ERROR_CODE = """
226 DIRECT_CAUSE_ERROR_CODE = """
226 try:
227 try:
227 x = 1 + 2
228 x = 1 + 2
228 print(not_defined_here)
229 print(not_defined_here)
229 except Exception as e:
230 except Exception as e:
230 x += 55
231 x += 55
231 x - 1
232 x - 1
232 y = {}
233 y = {}
233 raise KeyError('uh') from e
234 raise KeyError('uh') from e
234 """
235 """
235
236
236 EXCEPTION_DURING_HANDLING_CODE = """
237 EXCEPTION_DURING_HANDLING_CODE = """
237 try:
238 try:
238 x = 1 + 2
239 x = 1 + 2
239 print(not_defined_here)
240 print(not_defined_here)
240 except Exception as e:
241 except Exception as e:
241 x += 55
242 x += 55
242 x - 1
243 x - 1
243 y = {}
244 y = {}
244 raise KeyError('uh')
245 raise KeyError('uh')
245 """
246 """
246
247
247 SUPPRESS_CHAINING_CODE = """
248 SUPPRESS_CHAINING_CODE = """
248 try:
249 try:
249 1/0
250 1/0
250 except Exception:
251 except Exception:
251 raise ValueError("Yikes") from None
252 raise ValueError("Yikes") from None
252 """
253 """
253
254
254 def test_direct_cause_error(self):
255 def test_direct_cause_error(self):
255 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
256 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
256 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
257 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
257
258
258 def test_exception_during_handling_error(self):
259 def test_exception_during_handling_error(self):
259 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
260 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
260 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
261 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
261
262
262 def test_suppress_exception_chaining(self):
263 def test_suppress_exception_chaining(self):
263 with tt.AssertNotPrints("ZeroDivisionError"), \
264 with tt.AssertNotPrints("ZeroDivisionError"), \
264 tt.AssertPrints("ValueError", suppress=False):
265 tt.AssertPrints("ValueError", suppress=False):
265 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
266 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
266
267
267
268
268 class RecursionTest(unittest.TestCase):
269 class RecursionTest(unittest.TestCase):
269 DEFINITIONS = """
270 DEFINITIONS = """
270 def non_recurs():
271 def non_recurs():
271 1/0
272 1/0
272
273
273 def r1():
274 def r1():
274 r1()
275 r1()
275
276
276 def r3a():
277 def r3a():
277 r3b()
278 r3b()
278
279
279 def r3b():
280 def r3b():
280 r3c()
281 r3c()
281
282
282 def r3c():
283 def r3c():
283 r3a()
284 r3a()
284
285
285 def r3o1():
286 def r3o1():
286 r3a()
287 r3a()
287
288
288 def r3o2():
289 def r3o2():
289 r3o1()
290 r3o1()
290 """
291 """
291 def setUp(self):
292 def setUp(self):
292 ip.run_cell(self.DEFINITIONS)
293 ip.run_cell(self.DEFINITIONS)
293
294
294 def test_no_recursion(self):
295 def test_no_recursion(self):
295 with tt.AssertNotPrints("frames repeated"):
296 with tt.AssertNotPrints("frames repeated"):
296 ip.run_cell("non_recurs()")
297 ip.run_cell("non_recurs()")
297
298
298 def test_recursion_one_frame(self):
299 def test_recursion_one_frame(self):
299 with tt.AssertPrints("1 frames repeated"):
300 with tt.AssertPrints("1 frames repeated"):
300 ip.run_cell("r1()")
301 ip.run_cell("r1()")
301
302
302 def test_recursion_three_frames(self):
303 def test_recursion_three_frames(self):
303 with tt.AssertPrints("3 frames repeated"):
304 with tt.AssertPrints("3 frames repeated"):
304 ip.run_cell("r3o2()")
305 ip.run_cell("r3o2()")
305
306
306 def test_find_recursion(self):
307 def test_find_recursion(self):
307 captured = []
308 captured = []
308 def capture_exc(*args, **kwargs):
309 def capture_exc(*args, **kwargs):
309 captured.append(sys.exc_info())
310 captured.append(sys.exc_info())
310 with mock.patch.object(ip, 'showtraceback', capture_exc):
311 with mock.patch.object(ip, 'showtraceback', capture_exc):
311 ip.run_cell("r3o2()")
312 ip.run_cell("r3o2()")
312
313
313 self.assertEqual(len(captured), 1)
314 self.assertEqual(len(captured), 1)
314 etype, evalue, tb = captured[0]
315 etype, evalue, tb = captured[0]
315 self.assertIn("recursion", str(evalue))
316 self.assertIn("recursion", str(evalue))
316
317
317 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
318 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
318 for r in records[:10]:
319 for r in records[:10]:
319 print(r[1:4])
320 print(r[1:4])
320
321
321 # The outermost frames should be:
322 # The outermost frames should be:
322 # 0: the 'cell' that was running when the exception came up
323 # 0: the 'cell' that was running when the exception came up
323 # 1: r3o2()
324 # 1: r3o2()
324 # 2: r3o1()
325 # 2: r3o1()
325 # 3: r3a()
326 # 3: r3a()
326 # Then repeating r3b, r3c, r3a
327 # Then repeating r3b, r3c, r3a
327 last_unique, repeat_length = find_recursion(etype, evalue, records)
328 last_unique, repeat_length = find_recursion(etype, evalue, records)
328 self.assertEqual(last_unique, 2)
329 self.assertEqual(last_unique, 2)
329 self.assertEqual(repeat_length, 3)
330 self.assertEqual(repeat_length, 3)
330
331
331
332
332 #----------------------------------------------------------------------------
333 #----------------------------------------------------------------------------
333
334
334 # module testing (minimal)
335 # module testing (minimal)
335 def test_handlers():
336 def test_handlers():
336 def spam(c, d_e):
337 def spam(c, d_e):
337 (d, e) = d_e
338 (d, e) = d_e
338 x = c + d
339 x = c + d
339 y = c * d
340 y = c * d
340 foo(x, y)
341 foo(x, y)
341
342
342 def foo(a, b, bar=1):
343 def foo(a, b, bar=1):
343 eggs(a, b + bar)
344 eggs(a, b + bar)
344
345
345 def eggs(f, g, z=globals()):
346 def eggs(f, g, z=globals()):
346 h = f + g
347 h = f + g
347 i = f - g
348 i = f - g
348 return h / i
349 return h / i
349
350
350 buff = io.StringIO()
351 buff = io.StringIO()
351
352
352 buff.write('')
353 buff.write('')
353 buff.write('*** Before ***')
354 buff.write('*** Before ***')
354 try:
355 try:
355 buff.write(spam(1, (2, 3)))
356 buff.write(spam(1, (2, 3)))
356 except:
357 except:
357 traceback.print_exc(file=buff)
358 traceback.print_exc(file=buff)
358
359
359 handler = ColorTB(ostream=buff)
360 handler = ColorTB(ostream=buff)
360 buff.write('*** ColorTB ***')
361 buff.write('*** ColorTB ***')
361 try:
362 try:
362 buff.write(spam(1, (2, 3)))
363 buff.write(spam(1, (2, 3)))
363 except:
364 except:
364 handler(*sys.exc_info())
365 handler(*sys.exc_info())
365 buff.write('')
366 buff.write('')
366
367
367 handler = VerboseTB(ostream=buff)
368 handler = VerboseTB(ostream=buff)
368 buff.write('*** VerboseTB ***')
369 buff.write('*** VerboseTB ***')
369 try:
370 try:
370 buff.write(spam(1, (2, 3)))
371 buff.write(spam(1, (2, 3)))
371 except:
372 except:
372 handler(*sys.exc_info())
373 handler(*sys.exc_info())
373 buff.write('')
374 buff.write('')
374
375
376
377 class TokenizeFailureTest(unittest.TestCase):
378 """Tests related to https://github.com/ipython/ipython/issues/6864."""
379
380 def testLogging(self):
381 message = "An unexpected error occurred while tokenizing input"
382 cell = 'raise ValueError("""a\nb""")'
383
384 stream = io.StringIO()
385 handler = logging.StreamHandler(stream)
386 logger = logging.getLogger()
387 loglevel = logger.level
388 logger.addHandler(handler)
389 self.addCleanup(lambda: logger.removeHandler(handler))
390 self.addCleanup(lambda: logger.setLevel(loglevel))
391
392 logger.setLevel(logging.INFO)
393 with tt.AssertNotPrints(message):
394 ip.run_cell(cell)
395 self.assertNotIn(message, stream.getvalue())
396
397 logger.setLevel(logging.DEBUG)
398 with tt.AssertNotPrints(message):
399 ip.run_cell(cell)
400 self.assertIn(message, stream.getvalue())
@@ -1,1459 +1,1464 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Verbose and colourful traceback formatting.
3 Verbose and colourful traceback formatting.
4
4
5 **ColorTB**
5 **ColorTB**
6
6
7 I've always found it a bit hard to visually parse tracebacks in Python. The
7 I've always found it a bit hard to visually parse tracebacks in Python. The
8 ColorTB class is a solution to that problem. It colors the different parts of a
8 ColorTB class is a solution to that problem. It colors the different parts of a
9 traceback in a manner similar to what you would expect from a syntax-highlighting
9 traceback in a manner similar to what you would expect from a syntax-highlighting
10 text editor.
10 text editor.
11
11
12 Installation instructions for ColorTB::
12 Installation instructions for ColorTB::
13
13
14 import sys,ultratb
14 import sys,ultratb
15 sys.excepthook = ultratb.ColorTB()
15 sys.excepthook = ultratb.ColorTB()
16
16
17 **VerboseTB**
17 **VerboseTB**
18
18
19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
20 of useful info when a traceback occurs. Ping originally had it spit out HTML
20 of useful info when a traceback occurs. Ping originally had it spit out HTML
21 and intended it for CGI programmers, but why should they have all the fun? I
21 and intended it for CGI programmers, but why should they have all the fun? I
22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
23 but kind of neat, and maybe useful for long-running programs that you believe
23 but kind of neat, and maybe useful for long-running programs that you believe
24 are bug-free. If a crash *does* occur in that type of program you want details.
24 are bug-free. If a crash *does* occur in that type of program you want details.
25 Give it a shot--you'll love it or you'll hate it.
25 Give it a shot--you'll love it or you'll hate it.
26
26
27 .. note::
27 .. note::
28
28
29 The Verbose mode prints the variables currently visible where the exception
29 The Verbose mode prints the variables currently visible where the exception
30 happened (shortening their strings if too long). This can potentially be
30 happened (shortening their strings if too long). This can potentially be
31 very slow, if you happen to have a huge data structure whose string
31 very slow, if you happen to have a huge data structure whose string
32 representation is complex to compute. Your computer may appear to freeze for
32 representation is complex to compute. Your computer may appear to freeze for
33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
34 with Ctrl-C (maybe hitting it more than once).
34 with Ctrl-C (maybe hitting it more than once).
35
35
36 If you encounter this kind of situation often, you may want to use the
36 If you encounter this kind of situation often, you may want to use the
37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
38 variables (but otherwise includes the information and context given by
38 variables (but otherwise includes the information and context given by
39 Verbose).
39 Verbose).
40
40
41 .. note::
41 .. note::
42
42
43 The verbose mode print all variables in the stack, which means it can
43 The verbose mode print all variables in the stack, which means it can
44 potentially leak sensitive information like access keys, or unencryted
44 potentially leak sensitive information like access keys, or unencryted
45 password.
45 password.
46
46
47 Installation instructions for VerboseTB::
47 Installation instructions for VerboseTB::
48
48
49 import sys,ultratb
49 import sys,ultratb
50 sys.excepthook = ultratb.VerboseTB()
50 sys.excepthook = ultratb.VerboseTB()
51
51
52 Note: Much of the code in this module was lifted verbatim from the standard
52 Note: Much of the code in this module was lifted verbatim from the standard
53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
54
54
55 Color schemes
55 Color schemes
56 -------------
56 -------------
57
57
58 The colors are defined in the class TBTools through the use of the
58 The colors are defined in the class TBTools through the use of the
59 ColorSchemeTable class. Currently the following exist:
59 ColorSchemeTable class. Currently the following exist:
60
60
61 - NoColor: allows all of this module to be used in any terminal (the color
61 - NoColor: allows all of this module to be used in any terminal (the color
62 escapes are just dummy blank strings).
62 escapes are just dummy blank strings).
63
63
64 - Linux: is meant to look good in a terminal like the Linux console (black
64 - Linux: is meant to look good in a terminal like the Linux console (black
65 or very dark background).
65 or very dark background).
66
66
67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
68 in light background terminals.
68 in light background terminals.
69
69
70 - Neutral: a neutral color scheme that should be readable on both light and
70 - Neutral: a neutral color scheme that should be readable on both light and
71 dark background
71 dark background
72
72
73 You can implement other color schemes easily, the syntax is fairly
73 You can implement other color schemes easily, the syntax is fairly
74 self-explanatory. Please send back new schemes you develop to the author for
74 self-explanatory. Please send back new schemes you develop to the author for
75 possible inclusion in future releases.
75 possible inclusion in future releases.
76
76
77 Inheritance diagram:
77 Inheritance diagram:
78
78
79 .. inheritance-diagram:: IPython.core.ultratb
79 .. inheritance-diagram:: IPython.core.ultratb
80 :parts: 3
80 :parts: 3
81 """
81 """
82
82
83 #*****************************************************************************
83 #*****************************************************************************
84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
86 #
86 #
87 # Distributed under the terms of the BSD License. The full license is in
87 # Distributed under the terms of the BSD License. The full license is in
88 # the file COPYING, distributed as part of this software.
88 # the file COPYING, distributed as part of this software.
89 #*****************************************************************************
89 #*****************************************************************************
90
90
91
91
92 import dis
92 import dis
93 import inspect
93 import inspect
94 import keyword
94 import keyword
95 import linecache
95 import linecache
96 import os
96 import os
97 import pydoc
97 import pydoc
98 import re
98 import re
99 import sys
99 import sys
100 import time
100 import time
101 import tokenize
101 import tokenize
102 import traceback
102 import traceback
103
103
104 try: # Python 2
104 try: # Python 2
105 generate_tokens = tokenize.generate_tokens
105 generate_tokens = tokenize.generate_tokens
106 except AttributeError: # Python 3
106 except AttributeError: # Python 3
107 generate_tokens = tokenize.tokenize
107 generate_tokens = tokenize.tokenize
108
108
109 # For purposes of monkeypatching inspect to fix a bug in it.
109 # For purposes of monkeypatching inspect to fix a bug in it.
110 from inspect import getsourcefile, getfile, getmodule, \
110 from inspect import getsourcefile, getfile, getmodule, \
111 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
111 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
112
112
113 # IPython's own modules
113 # IPython's own modules
114 from IPython import get_ipython
114 from IPython import get_ipython
115 from IPython.core import debugger
115 from IPython.core import debugger
116 from IPython.core.display_trap import DisplayTrap
116 from IPython.core.display_trap import DisplayTrap
117 from IPython.core.excolors import exception_colors
117 from IPython.core.excolors import exception_colors
118 from IPython.utils import PyColorize
118 from IPython.utils import PyColorize
119 from IPython.utils import openpy
119 from IPython.utils import openpy
120 from IPython.utils import path as util_path
120 from IPython.utils import path as util_path
121 from IPython.utils import py3compat
121 from IPython.utils import py3compat
122 from IPython.utils.data import uniq_stable
122 from IPython.utils.data import uniq_stable
123 from IPython.utils.terminal import get_terminal_size
123 from IPython.utils.terminal import get_terminal_size
124 from logging import info, error
124 from logging import info, error, debug
125
125
126 import IPython.utils.colorable as colorable
126 import IPython.utils.colorable as colorable
127
127
128 # Globals
128 # Globals
129 # amount of space to put line numbers before verbose tracebacks
129 # amount of space to put line numbers before verbose tracebacks
130 INDENT_SIZE = 8
130 INDENT_SIZE = 8
131
131
132 # Default color scheme. This is used, for example, by the traceback
132 # Default color scheme. This is used, for example, by the traceback
133 # formatter. When running in an actual IPython instance, the user's rc.colors
133 # formatter. When running in an actual IPython instance, the user's rc.colors
134 # value is used, but having a module global makes this functionality available
134 # value is used, but having a module global makes this functionality available
135 # to users of ultratb who are NOT running inside ipython.
135 # to users of ultratb who are NOT running inside ipython.
136 DEFAULT_SCHEME = 'NoColor'
136 DEFAULT_SCHEME = 'NoColor'
137
137
138 # ---------------------------------------------------------------------------
138 # ---------------------------------------------------------------------------
139 # Code begins
139 # Code begins
140
140
141 # Utility functions
141 # Utility functions
142 def inspect_error():
142 def inspect_error():
143 """Print a message about internal inspect errors.
143 """Print a message about internal inspect errors.
144
144
145 These are unfortunately quite common."""
145 These are unfortunately quite common."""
146
146
147 error('Internal Python error in the inspect module.\n'
147 error('Internal Python error in the inspect module.\n'
148 'Below is the traceback from this internal error.\n')
148 'Below is the traceback from this internal error.\n')
149
149
150
150
151 # This function is a monkeypatch we apply to the Python inspect module. We have
151 # This function is a monkeypatch we apply to the Python inspect module. We have
152 # now found when it's needed (see discussion on issue gh-1456), and we have a
152 # now found when it's needed (see discussion on issue gh-1456), and we have a
153 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
153 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
154 # the monkeypatch is not applied. TK, Aug 2012.
154 # the monkeypatch is not applied. TK, Aug 2012.
155 def findsource(object):
155 def findsource(object):
156 """Return the entire source file and starting line number for an object.
156 """Return the entire source file and starting line number for an object.
157
157
158 The argument may be a module, class, method, function, traceback, frame,
158 The argument may be a module, class, method, function, traceback, frame,
159 or code object. The source code is returned as a list of all the lines
159 or code object. The source code is returned as a list of all the lines
160 in the file and the line number indexes a line in that list. An IOError
160 in the file and the line number indexes a line in that list. An IOError
161 is raised if the source code cannot be retrieved.
161 is raised if the source code cannot be retrieved.
162
162
163 FIXED version with which we monkeypatch the stdlib to work around a bug."""
163 FIXED version with which we monkeypatch the stdlib to work around a bug."""
164
164
165 file = getsourcefile(object) or getfile(object)
165 file = getsourcefile(object) or getfile(object)
166 # If the object is a frame, then trying to get the globals dict from its
166 # If the object is a frame, then trying to get the globals dict from its
167 # module won't work. Instead, the frame object itself has the globals
167 # module won't work. Instead, the frame object itself has the globals
168 # dictionary.
168 # dictionary.
169 globals_dict = None
169 globals_dict = None
170 if inspect.isframe(object):
170 if inspect.isframe(object):
171 # XXX: can this ever be false?
171 # XXX: can this ever be false?
172 globals_dict = object.f_globals
172 globals_dict = object.f_globals
173 else:
173 else:
174 module = getmodule(object, file)
174 module = getmodule(object, file)
175 if module:
175 if module:
176 globals_dict = module.__dict__
176 globals_dict = module.__dict__
177 lines = linecache.getlines(file, globals_dict)
177 lines = linecache.getlines(file, globals_dict)
178 if not lines:
178 if not lines:
179 raise IOError('could not get source code')
179 raise IOError('could not get source code')
180
180
181 if ismodule(object):
181 if ismodule(object):
182 return lines, 0
182 return lines, 0
183
183
184 if isclass(object):
184 if isclass(object):
185 name = object.__name__
185 name = object.__name__
186 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
186 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
187 # make some effort to find the best matching class definition:
187 # make some effort to find the best matching class definition:
188 # use the one with the least indentation, which is the one
188 # use the one with the least indentation, which is the one
189 # that's most probably not inside a function definition.
189 # that's most probably not inside a function definition.
190 candidates = []
190 candidates = []
191 for i, line in enumerate(lines):
191 for i, line in enumerate(lines):
192 match = pat.match(line)
192 match = pat.match(line)
193 if match:
193 if match:
194 # if it's at toplevel, it's already the best one
194 # if it's at toplevel, it's already the best one
195 if line[0] == 'c':
195 if line[0] == 'c':
196 return lines, i
196 return lines, i
197 # else add whitespace to candidate list
197 # else add whitespace to candidate list
198 candidates.append((match.group(1), i))
198 candidates.append((match.group(1), i))
199 if candidates:
199 if candidates:
200 # this will sort by whitespace, and by line number,
200 # this will sort by whitespace, and by line number,
201 # less whitespace first
201 # less whitespace first
202 candidates.sort()
202 candidates.sort()
203 return lines, candidates[0][1]
203 return lines, candidates[0][1]
204 else:
204 else:
205 raise IOError('could not find class definition')
205 raise IOError('could not find class definition')
206
206
207 if ismethod(object):
207 if ismethod(object):
208 object = object.__func__
208 object = object.__func__
209 if isfunction(object):
209 if isfunction(object):
210 object = object.__code__
210 object = object.__code__
211 if istraceback(object):
211 if istraceback(object):
212 object = object.tb_frame
212 object = object.tb_frame
213 if isframe(object):
213 if isframe(object):
214 object = object.f_code
214 object = object.f_code
215 if iscode(object):
215 if iscode(object):
216 if not hasattr(object, 'co_firstlineno'):
216 if not hasattr(object, 'co_firstlineno'):
217 raise IOError('could not find function definition')
217 raise IOError('could not find function definition')
218 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
218 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
219 pmatch = pat.match
219 pmatch = pat.match
220 # fperez - fix: sometimes, co_firstlineno can give a number larger than
220 # fperez - fix: sometimes, co_firstlineno can give a number larger than
221 # the length of lines, which causes an error. Safeguard against that.
221 # the length of lines, which causes an error. Safeguard against that.
222 lnum = min(object.co_firstlineno, len(lines)) - 1
222 lnum = min(object.co_firstlineno, len(lines)) - 1
223 while lnum > 0:
223 while lnum > 0:
224 if pmatch(lines[lnum]):
224 if pmatch(lines[lnum]):
225 break
225 break
226 lnum -= 1
226 lnum -= 1
227
227
228 return lines, lnum
228 return lines, lnum
229 raise IOError('could not find code object')
229 raise IOError('could not find code object')
230
230
231
231
232 # This is a patched version of inspect.getargs that applies the (unmerged)
232 # This is a patched version of inspect.getargs that applies the (unmerged)
233 # patch for http://bugs.python.org/issue14611 by Stefano Taschini. This fixes
233 # patch for http://bugs.python.org/issue14611 by Stefano Taschini. This fixes
234 # https://github.com/ipython/ipython/issues/8205 and
234 # https://github.com/ipython/ipython/issues/8205 and
235 # https://github.com/ipython/ipython/issues/8293
235 # https://github.com/ipython/ipython/issues/8293
236 def getargs(co):
236 def getargs(co):
237 """Get information about the arguments accepted by a code object.
237 """Get information about the arguments accepted by a code object.
238
238
239 Three things are returned: (args, varargs, varkw), where 'args' is
239 Three things are returned: (args, varargs, varkw), where 'args' is
240 a list of argument names (possibly containing nested lists), and
240 a list of argument names (possibly containing nested lists), and
241 'varargs' and 'varkw' are the names of the * and ** arguments or None."""
241 'varargs' and 'varkw' are the names of the * and ** arguments or None."""
242 if not iscode(co):
242 if not iscode(co):
243 raise TypeError('{!r} is not a code object'.format(co))
243 raise TypeError('{!r} is not a code object'.format(co))
244
244
245 nargs = co.co_argcount
245 nargs = co.co_argcount
246 names = co.co_varnames
246 names = co.co_varnames
247 args = list(names[:nargs])
247 args = list(names[:nargs])
248 step = 0
248 step = 0
249
249
250 # The following acrobatics are for anonymous (tuple) arguments.
250 # The following acrobatics are for anonymous (tuple) arguments.
251 for i in range(nargs):
251 for i in range(nargs):
252 if args[i][:1] in ('', '.'):
252 if args[i][:1] in ('', '.'):
253 stack, remain, count = [], [], []
253 stack, remain, count = [], [], []
254 while step < len(co.co_code):
254 while step < len(co.co_code):
255 op = ord(co.co_code[step])
255 op = ord(co.co_code[step])
256 step = step + 1
256 step = step + 1
257 if op >= dis.HAVE_ARGUMENT:
257 if op >= dis.HAVE_ARGUMENT:
258 opname = dis.opname[op]
258 opname = dis.opname[op]
259 value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256
259 value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256
260 step = step + 2
260 step = step + 2
261 if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
261 if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
262 remain.append(value)
262 remain.append(value)
263 count.append(value)
263 count.append(value)
264 elif opname in ('STORE_FAST', 'STORE_DEREF'):
264 elif opname in ('STORE_FAST', 'STORE_DEREF'):
265 if op in dis.haslocal:
265 if op in dis.haslocal:
266 stack.append(co.co_varnames[value])
266 stack.append(co.co_varnames[value])
267 elif op in dis.hasfree:
267 elif op in dis.hasfree:
268 stack.append((co.co_cellvars + co.co_freevars)[value])
268 stack.append((co.co_cellvars + co.co_freevars)[value])
269 # Special case for sublists of length 1: def foo((bar))
269 # Special case for sublists of length 1: def foo((bar))
270 # doesn't generate the UNPACK_TUPLE bytecode, so if
270 # doesn't generate the UNPACK_TUPLE bytecode, so if
271 # `remain` is empty here, we have such a sublist.
271 # `remain` is empty here, we have such a sublist.
272 if not remain:
272 if not remain:
273 stack[0] = [stack[0]]
273 stack[0] = [stack[0]]
274 break
274 break
275 else:
275 else:
276 remain[-1] = remain[-1] - 1
276 remain[-1] = remain[-1] - 1
277 while remain[-1] == 0:
277 while remain[-1] == 0:
278 remain.pop()
278 remain.pop()
279 size = count.pop()
279 size = count.pop()
280 stack[-size:] = [stack[-size:]]
280 stack[-size:] = [stack[-size:]]
281 if not remain:
281 if not remain:
282 break
282 break
283 remain[-1] = remain[-1] - 1
283 remain[-1] = remain[-1] - 1
284 if not remain:
284 if not remain:
285 break
285 break
286 args[i] = stack[0]
286 args[i] = stack[0]
287
287
288 varargs = None
288 varargs = None
289 if co.co_flags & inspect.CO_VARARGS:
289 if co.co_flags & inspect.CO_VARARGS:
290 varargs = co.co_varnames[nargs]
290 varargs = co.co_varnames[nargs]
291 nargs = nargs + 1
291 nargs = nargs + 1
292 varkw = None
292 varkw = None
293 if co.co_flags & inspect.CO_VARKEYWORDS:
293 if co.co_flags & inspect.CO_VARKEYWORDS:
294 varkw = co.co_varnames[nargs]
294 varkw = co.co_varnames[nargs]
295 return inspect.Arguments(args, varargs, varkw)
295 return inspect.Arguments(args, varargs, varkw)
296
296
297
297
298 # Monkeypatch inspect to apply our bugfix.
298 # Monkeypatch inspect to apply our bugfix.
299 def with_patch_inspect(f):
299 def with_patch_inspect(f):
300 """
300 """
301 Deprecated since IPython 6.0
301 Deprecated since IPython 6.0
302 decorator for monkeypatching inspect.findsource
302 decorator for monkeypatching inspect.findsource
303 """
303 """
304
304
305 def wrapped(*args, **kwargs):
305 def wrapped(*args, **kwargs):
306 save_findsource = inspect.findsource
306 save_findsource = inspect.findsource
307 save_getargs = inspect.getargs
307 save_getargs = inspect.getargs
308 inspect.findsource = findsource
308 inspect.findsource = findsource
309 inspect.getargs = getargs
309 inspect.getargs = getargs
310 try:
310 try:
311 return f(*args, **kwargs)
311 return f(*args, **kwargs)
312 finally:
312 finally:
313 inspect.findsource = save_findsource
313 inspect.findsource = save_findsource
314 inspect.getargs = save_getargs
314 inspect.getargs = save_getargs
315
315
316 return wrapped
316 return wrapped
317
317
318
318
319 def fix_frame_records_filenames(records):
319 def fix_frame_records_filenames(records):
320 """Try to fix the filenames in each record from inspect.getinnerframes().
320 """Try to fix the filenames in each record from inspect.getinnerframes().
321
321
322 Particularly, modules loaded from within zip files have useless filenames
322 Particularly, modules loaded from within zip files have useless filenames
323 attached to their code object, and inspect.getinnerframes() just uses it.
323 attached to their code object, and inspect.getinnerframes() just uses it.
324 """
324 """
325 fixed_records = []
325 fixed_records = []
326 for frame, filename, line_no, func_name, lines, index in records:
326 for frame, filename, line_no, func_name, lines, index in records:
327 # Look inside the frame's globals dictionary for __file__,
327 # Look inside the frame's globals dictionary for __file__,
328 # which should be better. However, keep Cython filenames since
328 # which should be better. However, keep Cython filenames since
329 # we prefer the source filenames over the compiled .so file.
329 # we prefer the source filenames over the compiled .so file.
330 if not filename.endswith(('.pyx', '.pxd', '.pxi')):
330 if not filename.endswith(('.pyx', '.pxd', '.pxi')):
331 better_fn = frame.f_globals.get('__file__', None)
331 better_fn = frame.f_globals.get('__file__', None)
332 if isinstance(better_fn, str):
332 if isinstance(better_fn, str):
333 # Check the type just in case someone did something weird with
333 # Check the type just in case someone did something weird with
334 # __file__. It might also be None if the error occurred during
334 # __file__. It might also be None if the error occurred during
335 # import.
335 # import.
336 filename = better_fn
336 filename = better_fn
337 fixed_records.append((frame, filename, line_no, func_name, lines, index))
337 fixed_records.append((frame, filename, line_no, func_name, lines, index))
338 return fixed_records
338 return fixed_records
339
339
340
340
341 @with_patch_inspect
341 @with_patch_inspect
342 def _fixed_getinnerframes(etb, context=1, tb_offset=0):
342 def _fixed_getinnerframes(etb, context=1, tb_offset=0):
343 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
343 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
344
344
345 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
345 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
346 # If the error is at the console, don't build any context, since it would
346 # If the error is at the console, don't build any context, since it would
347 # otherwise produce 5 blank lines printed out (there is no file at the
347 # otherwise produce 5 blank lines printed out (there is no file at the
348 # console)
348 # console)
349 rec_check = records[tb_offset:]
349 rec_check = records[tb_offset:]
350 try:
350 try:
351 rname = rec_check[0][1]
351 rname = rec_check[0][1]
352 if rname == '<ipython console>' or rname.endswith('<string>'):
352 if rname == '<ipython console>' or rname.endswith('<string>'):
353 return rec_check
353 return rec_check
354 except IndexError:
354 except IndexError:
355 pass
355 pass
356
356
357 aux = traceback.extract_tb(etb)
357 aux = traceback.extract_tb(etb)
358 assert len(records) == len(aux)
358 assert len(records) == len(aux)
359 for i, (file, lnum, _, _) in enumerate(aux):
359 for i, (file, lnum, _, _) in enumerate(aux):
360 maybeStart = lnum - 1 - context // 2
360 maybeStart = lnum - 1 - context // 2
361 start = max(maybeStart, 0)
361 start = max(maybeStart, 0)
362 end = start + context
362 end = start + context
363 lines = linecache.getlines(file)[start:end]
363 lines = linecache.getlines(file)[start:end]
364 buf = list(records[i])
364 buf = list(records[i])
365 buf[LNUM_POS] = lnum
365 buf[LNUM_POS] = lnum
366 buf[INDEX_POS] = lnum - 1 - start
366 buf[INDEX_POS] = lnum - 1 - start
367 buf[LINES_POS] = lines
367 buf[LINES_POS] = lines
368 records[i] = tuple(buf)
368 records[i] = tuple(buf)
369 return records[tb_offset:]
369 return records[tb_offset:]
370
370
371 # Helper function -- largely belongs to VerboseTB, but we need the same
371 # Helper function -- largely belongs to VerboseTB, but we need the same
372 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
372 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
373 # can be recognized properly by ipython.el's py-traceback-line-re
373 # can be recognized properly by ipython.el's py-traceback-line-re
374 # (SyntaxErrors have to be treated specially because they have no traceback)
374 # (SyntaxErrors have to be treated specially because they have no traceback)
375
375
376
376
377 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None, _line_format=(lambda x,_:x,None)):
377 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None, _line_format=(lambda x,_:x,None)):
378 numbers_width = INDENT_SIZE - 1
378 numbers_width = INDENT_SIZE - 1
379 res = []
379 res = []
380 i = lnum - index
380 i = lnum - index
381
381
382 for line in lines:
382 for line in lines:
383 line = py3compat.cast_unicode(line)
383 line = py3compat.cast_unicode(line)
384
384
385 new_line, err = _line_format(line, 'str')
385 new_line, err = _line_format(line, 'str')
386 if not err: line = new_line
386 if not err: line = new_line
387
387
388 if i == lnum:
388 if i == lnum:
389 # This is the line with the error
389 # This is the line with the error
390 pad = numbers_width - len(str(i))
390 pad = numbers_width - len(str(i))
391 num = '%s%s' % (debugger.make_arrow(pad), str(lnum))
391 num = '%s%s' % (debugger.make_arrow(pad), str(lnum))
392 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
392 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
393 Colors.line, line, Colors.Normal)
393 Colors.line, line, Colors.Normal)
394 else:
394 else:
395 num = '%*s' % (numbers_width, i)
395 num = '%*s' % (numbers_width, i)
396 line = '%s%s%s %s' % (Colors.lineno, num,
396 line = '%s%s%s %s' % (Colors.lineno, num,
397 Colors.Normal, line)
397 Colors.Normal, line)
398
398
399 res.append(line)
399 res.append(line)
400 if lvals and i == lnum:
400 if lvals and i == lnum:
401 res.append(lvals + '\n')
401 res.append(lvals + '\n')
402 i = i + 1
402 i = i + 1
403 return res
403 return res
404
404
405 def is_recursion_error(etype, value, records):
405 def is_recursion_error(etype, value, records):
406 try:
406 try:
407 # RecursionError is new in Python 3.5
407 # RecursionError is new in Python 3.5
408 recursion_error_type = RecursionError
408 recursion_error_type = RecursionError
409 except NameError:
409 except NameError:
410 recursion_error_type = RuntimeError
410 recursion_error_type = RuntimeError
411
411
412 # The default recursion limit is 1000, but some of that will be taken up
412 # The default recursion limit is 1000, but some of that will be taken up
413 # by stack frames in IPython itself. >500 frames probably indicates
413 # by stack frames in IPython itself. >500 frames probably indicates
414 # a recursion error.
414 # a recursion error.
415 return (etype is recursion_error_type) \
415 return (etype is recursion_error_type) \
416 and "recursion" in str(value).lower() \
416 and "recursion" in str(value).lower() \
417 and len(records) > 500
417 and len(records) > 500
418
418
419 def find_recursion(etype, value, records):
419 def find_recursion(etype, value, records):
420 """Identify the repeating stack frames from a RecursionError traceback
420 """Identify the repeating stack frames from a RecursionError traceback
421
421
422 'records' is a list as returned by VerboseTB.get_records()
422 'records' is a list as returned by VerboseTB.get_records()
423
423
424 Returns (last_unique, repeat_length)
424 Returns (last_unique, repeat_length)
425 """
425 """
426 # This involves a bit of guesswork - we want to show enough of the traceback
426 # This involves a bit of guesswork - we want to show enough of the traceback
427 # to indicate where the recursion is occurring. We guess that the innermost
427 # to indicate where the recursion is occurring. We guess that the innermost
428 # quarter of the traceback (250 frames by default) is repeats, and find the
428 # quarter of the traceback (250 frames by default) is repeats, and find the
429 # first frame (from in to out) that looks different.
429 # first frame (from in to out) that looks different.
430 if not is_recursion_error(etype, value, records):
430 if not is_recursion_error(etype, value, records):
431 return len(records), 0
431 return len(records), 0
432
432
433 # Select filename, lineno, func_name to track frames with
433 # Select filename, lineno, func_name to track frames with
434 records = [r[1:4] for r in records]
434 records = [r[1:4] for r in records]
435 inner_frames = records[-(len(records)//4):]
435 inner_frames = records[-(len(records)//4):]
436 frames_repeated = set(inner_frames)
436 frames_repeated = set(inner_frames)
437
437
438 last_seen_at = {}
438 last_seen_at = {}
439 longest_repeat = 0
439 longest_repeat = 0
440 i = len(records)
440 i = len(records)
441 for frame in reversed(records):
441 for frame in reversed(records):
442 i -= 1
442 i -= 1
443 if frame not in frames_repeated:
443 if frame not in frames_repeated:
444 last_unique = i
444 last_unique = i
445 break
445 break
446
446
447 if frame in last_seen_at:
447 if frame in last_seen_at:
448 distance = last_seen_at[frame] - i
448 distance = last_seen_at[frame] - i
449 longest_repeat = max(longest_repeat, distance)
449 longest_repeat = max(longest_repeat, distance)
450
450
451 last_seen_at[frame] = i
451 last_seen_at[frame] = i
452 else:
452 else:
453 last_unique = 0 # The whole traceback was recursion
453 last_unique = 0 # The whole traceback was recursion
454
454
455 return last_unique, longest_repeat
455 return last_unique, longest_repeat
456
456
457 #---------------------------------------------------------------------------
457 #---------------------------------------------------------------------------
458 # Module classes
458 # Module classes
459 class TBTools(colorable.Colorable):
459 class TBTools(colorable.Colorable):
460 """Basic tools used by all traceback printer classes."""
460 """Basic tools used by all traceback printer classes."""
461
461
462 # Number of frames to skip when reporting tracebacks
462 # Number of frames to skip when reporting tracebacks
463 tb_offset = 0
463 tb_offset = 0
464
464
465 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
465 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
466 # Whether to call the interactive pdb debugger after printing
466 # Whether to call the interactive pdb debugger after printing
467 # tracebacks or not
467 # tracebacks or not
468 super(TBTools, self).__init__(parent=parent, config=config)
468 super(TBTools, self).__init__(parent=parent, config=config)
469 self.call_pdb = call_pdb
469 self.call_pdb = call_pdb
470
470
471 # Output stream to write to. Note that we store the original value in
471 # Output stream to write to. Note that we store the original value in
472 # a private attribute and then make the public ostream a property, so
472 # a private attribute and then make the public ostream a property, so
473 # that we can delay accessing sys.stdout until runtime. The way
473 # that we can delay accessing sys.stdout until runtime. The way
474 # things are written now, the sys.stdout object is dynamically managed
474 # things are written now, the sys.stdout object is dynamically managed
475 # so a reference to it should NEVER be stored statically. This
475 # so a reference to it should NEVER be stored statically. This
476 # property approach confines this detail to a single location, and all
476 # property approach confines this detail to a single location, and all
477 # subclasses can simply access self.ostream for writing.
477 # subclasses can simply access self.ostream for writing.
478 self._ostream = ostream
478 self._ostream = ostream
479
479
480 # Create color table
480 # Create color table
481 self.color_scheme_table = exception_colors()
481 self.color_scheme_table = exception_colors()
482
482
483 self.set_colors(color_scheme)
483 self.set_colors(color_scheme)
484 self.old_scheme = color_scheme # save initial value for toggles
484 self.old_scheme = color_scheme # save initial value for toggles
485
485
486 if call_pdb:
486 if call_pdb:
487 self.pdb = debugger.Pdb()
487 self.pdb = debugger.Pdb()
488 else:
488 else:
489 self.pdb = None
489 self.pdb = None
490
490
491 def _get_ostream(self):
491 def _get_ostream(self):
492 """Output stream that exceptions are written to.
492 """Output stream that exceptions are written to.
493
493
494 Valid values are:
494 Valid values are:
495
495
496 - None: the default, which means that IPython will dynamically resolve
496 - None: the default, which means that IPython will dynamically resolve
497 to sys.stdout. This ensures compatibility with most tools, including
497 to sys.stdout. This ensures compatibility with most tools, including
498 Windows (where plain stdout doesn't recognize ANSI escapes).
498 Windows (where plain stdout doesn't recognize ANSI escapes).
499
499
500 - Any object with 'write' and 'flush' attributes.
500 - Any object with 'write' and 'flush' attributes.
501 """
501 """
502 return sys.stdout if self._ostream is None else self._ostream
502 return sys.stdout if self._ostream is None else self._ostream
503
503
504 def _set_ostream(self, val):
504 def _set_ostream(self, val):
505 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
505 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
506 self._ostream = val
506 self._ostream = val
507
507
508 ostream = property(_get_ostream, _set_ostream)
508 ostream = property(_get_ostream, _set_ostream)
509
509
510 def set_colors(self, *args, **kw):
510 def set_colors(self, *args, **kw):
511 """Shorthand access to the color table scheme selector method."""
511 """Shorthand access to the color table scheme selector method."""
512
512
513 # Set own color table
513 # Set own color table
514 self.color_scheme_table.set_active_scheme(*args, **kw)
514 self.color_scheme_table.set_active_scheme(*args, **kw)
515 # for convenience, set Colors to the active scheme
515 # for convenience, set Colors to the active scheme
516 self.Colors = self.color_scheme_table.active_colors
516 self.Colors = self.color_scheme_table.active_colors
517 # Also set colors of debugger
517 # Also set colors of debugger
518 if hasattr(self, 'pdb') and self.pdb is not None:
518 if hasattr(self, 'pdb') and self.pdb is not None:
519 self.pdb.set_colors(*args, **kw)
519 self.pdb.set_colors(*args, **kw)
520
520
521 def color_toggle(self):
521 def color_toggle(self):
522 """Toggle between the currently active color scheme and NoColor."""
522 """Toggle between the currently active color scheme and NoColor."""
523
523
524 if self.color_scheme_table.active_scheme_name == 'NoColor':
524 if self.color_scheme_table.active_scheme_name == 'NoColor':
525 self.color_scheme_table.set_active_scheme(self.old_scheme)
525 self.color_scheme_table.set_active_scheme(self.old_scheme)
526 self.Colors = self.color_scheme_table.active_colors
526 self.Colors = self.color_scheme_table.active_colors
527 else:
527 else:
528 self.old_scheme = self.color_scheme_table.active_scheme_name
528 self.old_scheme = self.color_scheme_table.active_scheme_name
529 self.color_scheme_table.set_active_scheme('NoColor')
529 self.color_scheme_table.set_active_scheme('NoColor')
530 self.Colors = self.color_scheme_table.active_colors
530 self.Colors = self.color_scheme_table.active_colors
531
531
532 def stb2text(self, stb):
532 def stb2text(self, stb):
533 """Convert a structured traceback (a list) to a string."""
533 """Convert a structured traceback (a list) to a string."""
534 return '\n'.join(stb)
534 return '\n'.join(stb)
535
535
536 def text(self, etype, value, tb, tb_offset=None, context=5):
536 def text(self, etype, value, tb, tb_offset=None, context=5):
537 """Return formatted traceback.
537 """Return formatted traceback.
538
538
539 Subclasses may override this if they add extra arguments.
539 Subclasses may override this if they add extra arguments.
540 """
540 """
541 tb_list = self.structured_traceback(etype, value, tb,
541 tb_list = self.structured_traceback(etype, value, tb,
542 tb_offset, context)
542 tb_offset, context)
543 return self.stb2text(tb_list)
543 return self.stb2text(tb_list)
544
544
545 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
545 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
546 context=5, mode=None):
546 context=5, mode=None):
547 """Return a list of traceback frames.
547 """Return a list of traceback frames.
548
548
549 Must be implemented by each class.
549 Must be implemented by each class.
550 """
550 """
551 raise NotImplementedError()
551 raise NotImplementedError()
552
552
553
553
554 #---------------------------------------------------------------------------
554 #---------------------------------------------------------------------------
555 class ListTB(TBTools):
555 class ListTB(TBTools):
556 """Print traceback information from a traceback list, with optional color.
556 """Print traceback information from a traceback list, with optional color.
557
557
558 Calling requires 3 arguments: (etype, evalue, elist)
558 Calling requires 3 arguments: (etype, evalue, elist)
559 as would be obtained by::
559 as would be obtained by::
560
560
561 etype, evalue, tb = sys.exc_info()
561 etype, evalue, tb = sys.exc_info()
562 if tb:
562 if tb:
563 elist = traceback.extract_tb(tb)
563 elist = traceback.extract_tb(tb)
564 else:
564 else:
565 elist = None
565 elist = None
566
566
567 It can thus be used by programs which need to process the traceback before
567 It can thus be used by programs which need to process the traceback before
568 printing (such as console replacements based on the code module from the
568 printing (such as console replacements based on the code module from the
569 standard library).
569 standard library).
570
570
571 Because they are meant to be called without a full traceback (only a
571 Because they are meant to be called without a full traceback (only a
572 list), instances of this class can't call the interactive pdb debugger."""
572 list), instances of this class can't call the interactive pdb debugger."""
573
573
574 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
574 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
575 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
575 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
576 ostream=ostream, parent=parent,config=config)
576 ostream=ostream, parent=parent,config=config)
577
577
578 def __call__(self, etype, value, elist):
578 def __call__(self, etype, value, elist):
579 self.ostream.flush()
579 self.ostream.flush()
580 self.ostream.write(self.text(etype, value, elist))
580 self.ostream.write(self.text(etype, value, elist))
581 self.ostream.write('\n')
581 self.ostream.write('\n')
582
582
583 def structured_traceback(self, etype, value, elist, tb_offset=None,
583 def structured_traceback(self, etype, value, elist, tb_offset=None,
584 context=5):
584 context=5):
585 """Return a color formatted string with the traceback info.
585 """Return a color formatted string with the traceback info.
586
586
587 Parameters
587 Parameters
588 ----------
588 ----------
589 etype : exception type
589 etype : exception type
590 Type of the exception raised.
590 Type of the exception raised.
591
591
592 value : object
592 value : object
593 Data stored in the exception
593 Data stored in the exception
594
594
595 elist : list
595 elist : list
596 List of frames, see class docstring for details.
596 List of frames, see class docstring for details.
597
597
598 tb_offset : int, optional
598 tb_offset : int, optional
599 Number of frames in the traceback to skip. If not given, the
599 Number of frames in the traceback to skip. If not given, the
600 instance value is used (set in constructor).
600 instance value is used (set in constructor).
601
601
602 context : int, optional
602 context : int, optional
603 Number of lines of context information to print.
603 Number of lines of context information to print.
604
604
605 Returns
605 Returns
606 -------
606 -------
607 String with formatted exception.
607 String with formatted exception.
608 """
608 """
609 tb_offset = self.tb_offset if tb_offset is None else tb_offset
609 tb_offset = self.tb_offset if tb_offset is None else tb_offset
610 Colors = self.Colors
610 Colors = self.Colors
611 out_list = []
611 out_list = []
612 if elist:
612 if elist:
613
613
614 if tb_offset and len(elist) > tb_offset:
614 if tb_offset and len(elist) > tb_offset:
615 elist = elist[tb_offset:]
615 elist = elist[tb_offset:]
616
616
617 out_list.append('Traceback %s(most recent call last)%s:' %
617 out_list.append('Traceback %s(most recent call last)%s:' %
618 (Colors.normalEm, Colors.Normal) + '\n')
618 (Colors.normalEm, Colors.Normal) + '\n')
619 out_list.extend(self._format_list(elist))
619 out_list.extend(self._format_list(elist))
620 # The exception info should be a single entry in the list.
620 # The exception info should be a single entry in the list.
621 lines = ''.join(self._format_exception_only(etype, value))
621 lines = ''.join(self._format_exception_only(etype, value))
622 out_list.append(lines)
622 out_list.append(lines)
623
623
624 # Note: this code originally read:
624 # Note: this code originally read:
625
625
626 ## for line in lines[:-1]:
626 ## for line in lines[:-1]:
627 ## out_list.append(" "+line)
627 ## out_list.append(" "+line)
628 ## out_list.append(lines[-1])
628 ## out_list.append(lines[-1])
629
629
630 # This means it was indenting everything but the last line by a little
630 # This means it was indenting everything but the last line by a little
631 # bit. I've disabled this for now, but if we see ugliness somewhere we
631 # bit. I've disabled this for now, but if we see ugliness somewhere we
632 # can restore it.
632 # can restore it.
633
633
634 return out_list
634 return out_list
635
635
636 def _format_list(self, extracted_list):
636 def _format_list(self, extracted_list):
637 """Format a list of traceback entry tuples for printing.
637 """Format a list of traceback entry tuples for printing.
638
638
639 Given a list of tuples as returned by extract_tb() or
639 Given a list of tuples as returned by extract_tb() or
640 extract_stack(), return a list of strings ready for printing.
640 extract_stack(), return a list of strings ready for printing.
641 Each string in the resulting list corresponds to the item with the
641 Each string in the resulting list corresponds to the item with the
642 same index in the argument list. Each string ends in a newline;
642 same index in the argument list. Each string ends in a newline;
643 the strings may contain internal newlines as well, for those items
643 the strings may contain internal newlines as well, for those items
644 whose source text line is not None.
644 whose source text line is not None.
645
645
646 Lifted almost verbatim from traceback.py
646 Lifted almost verbatim from traceback.py
647 """
647 """
648
648
649 Colors = self.Colors
649 Colors = self.Colors
650 list = []
650 list = []
651 for filename, lineno, name, line in extracted_list[:-1]:
651 for filename, lineno, name, line in extracted_list[:-1]:
652 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
652 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
653 (Colors.filename, filename, Colors.Normal,
653 (Colors.filename, filename, Colors.Normal,
654 Colors.lineno, lineno, Colors.Normal,
654 Colors.lineno, lineno, Colors.Normal,
655 Colors.name, name, Colors.Normal)
655 Colors.name, name, Colors.Normal)
656 if line:
656 if line:
657 item += ' %s\n' % line.strip()
657 item += ' %s\n' % line.strip()
658 list.append(item)
658 list.append(item)
659 # Emphasize the last entry
659 # Emphasize the last entry
660 filename, lineno, name, line = extracted_list[-1]
660 filename, lineno, name, line = extracted_list[-1]
661 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
661 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
662 (Colors.normalEm,
662 (Colors.normalEm,
663 Colors.filenameEm, filename, Colors.normalEm,
663 Colors.filenameEm, filename, Colors.normalEm,
664 Colors.linenoEm, lineno, Colors.normalEm,
664 Colors.linenoEm, lineno, Colors.normalEm,
665 Colors.nameEm, name, Colors.normalEm,
665 Colors.nameEm, name, Colors.normalEm,
666 Colors.Normal)
666 Colors.Normal)
667 if line:
667 if line:
668 item += '%s %s%s\n' % (Colors.line, line.strip(),
668 item += '%s %s%s\n' % (Colors.line, line.strip(),
669 Colors.Normal)
669 Colors.Normal)
670 list.append(item)
670 list.append(item)
671 return list
671 return list
672
672
673 def _format_exception_only(self, etype, value):
673 def _format_exception_only(self, etype, value):
674 """Format the exception part of a traceback.
674 """Format the exception part of a traceback.
675
675
676 The arguments are the exception type and value such as given by
676 The arguments are the exception type and value such as given by
677 sys.exc_info()[:2]. The return value is a list of strings, each ending
677 sys.exc_info()[:2]. The return value is a list of strings, each ending
678 in a newline. Normally, the list contains a single string; however,
678 in a newline. Normally, the list contains a single string; however,
679 for SyntaxError exceptions, it contains several lines that (when
679 for SyntaxError exceptions, it contains several lines that (when
680 printed) display detailed information about where the syntax error
680 printed) display detailed information about where the syntax error
681 occurred. The message indicating which exception occurred is the
681 occurred. The message indicating which exception occurred is the
682 always last string in the list.
682 always last string in the list.
683
683
684 Also lifted nearly verbatim from traceback.py
684 Also lifted nearly verbatim from traceback.py
685 """
685 """
686 have_filedata = False
686 have_filedata = False
687 Colors = self.Colors
687 Colors = self.Colors
688 list = []
688 list = []
689 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
689 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
690 if value is None:
690 if value is None:
691 # Not sure if this can still happen in Python 2.6 and above
691 # Not sure if this can still happen in Python 2.6 and above
692 list.append(stype + '\n')
692 list.append(stype + '\n')
693 else:
693 else:
694 if issubclass(etype, SyntaxError):
694 if issubclass(etype, SyntaxError):
695 have_filedata = True
695 have_filedata = True
696 if not value.filename: value.filename = "<string>"
696 if not value.filename: value.filename = "<string>"
697 if value.lineno:
697 if value.lineno:
698 lineno = value.lineno
698 lineno = value.lineno
699 textline = linecache.getline(value.filename, value.lineno)
699 textline = linecache.getline(value.filename, value.lineno)
700 else:
700 else:
701 lineno = 'unknown'
701 lineno = 'unknown'
702 textline = ''
702 textline = ''
703 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
703 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
704 (Colors.normalEm,
704 (Colors.normalEm,
705 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
705 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
706 Colors.linenoEm, lineno, Colors.Normal ))
706 Colors.linenoEm, lineno, Colors.Normal ))
707 if textline == '':
707 if textline == '':
708 textline = py3compat.cast_unicode(value.text, "utf-8")
708 textline = py3compat.cast_unicode(value.text, "utf-8")
709
709
710 if textline is not None:
710 if textline is not None:
711 i = 0
711 i = 0
712 while i < len(textline) and textline[i].isspace():
712 while i < len(textline) and textline[i].isspace():
713 i += 1
713 i += 1
714 list.append('%s %s%s\n' % (Colors.line,
714 list.append('%s %s%s\n' % (Colors.line,
715 textline.strip(),
715 textline.strip(),
716 Colors.Normal))
716 Colors.Normal))
717 if value.offset is not None:
717 if value.offset is not None:
718 s = ' '
718 s = ' '
719 for c in textline[i:value.offset - 1]:
719 for c in textline[i:value.offset - 1]:
720 if c.isspace():
720 if c.isspace():
721 s += c
721 s += c
722 else:
722 else:
723 s += ' '
723 s += ' '
724 list.append('%s%s^%s\n' % (Colors.caret, s,
724 list.append('%s%s^%s\n' % (Colors.caret, s,
725 Colors.Normal))
725 Colors.Normal))
726
726
727 try:
727 try:
728 s = value.msg
728 s = value.msg
729 except Exception:
729 except Exception:
730 s = self._some_str(value)
730 s = self._some_str(value)
731 if s:
731 if s:
732 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
732 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
733 Colors.Normal, s))
733 Colors.Normal, s))
734 else:
734 else:
735 list.append('%s\n' % stype)
735 list.append('%s\n' % stype)
736
736
737 # sync with user hooks
737 # sync with user hooks
738 if have_filedata:
738 if have_filedata:
739 ipinst = get_ipython()
739 ipinst = get_ipython()
740 if ipinst is not None:
740 if ipinst is not None:
741 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
741 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
742
742
743 return list
743 return list
744
744
745 def get_exception_only(self, etype, value):
745 def get_exception_only(self, etype, value):
746 """Only print the exception type and message, without a traceback.
746 """Only print the exception type and message, without a traceback.
747
747
748 Parameters
748 Parameters
749 ----------
749 ----------
750 etype : exception type
750 etype : exception type
751 value : exception value
751 value : exception value
752 """
752 """
753 return ListTB.structured_traceback(self, etype, value, [])
753 return ListTB.structured_traceback(self, etype, value, [])
754
754
755 def show_exception_only(self, etype, evalue):
755 def show_exception_only(self, etype, evalue):
756 """Only print the exception type and message, without a traceback.
756 """Only print the exception type and message, without a traceback.
757
757
758 Parameters
758 Parameters
759 ----------
759 ----------
760 etype : exception type
760 etype : exception type
761 value : exception value
761 value : exception value
762 """
762 """
763 # This method needs to use __call__ from *this* class, not the one from
763 # This method needs to use __call__ from *this* class, not the one from
764 # a subclass whose signature or behavior may be different
764 # a subclass whose signature or behavior may be different
765 ostream = self.ostream
765 ostream = self.ostream
766 ostream.flush()
766 ostream.flush()
767 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
767 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
768 ostream.flush()
768 ostream.flush()
769
769
770 def _some_str(self, value):
770 def _some_str(self, value):
771 # Lifted from traceback.py
771 # Lifted from traceback.py
772 try:
772 try:
773 return py3compat.cast_unicode(str(value))
773 return py3compat.cast_unicode(str(value))
774 except:
774 except:
775 return u'<unprintable %s object>' % type(value).__name__
775 return u'<unprintable %s object>' % type(value).__name__
776
776
777
777
778 #----------------------------------------------------------------------------
778 #----------------------------------------------------------------------------
779 class VerboseTB(TBTools):
779 class VerboseTB(TBTools):
780 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
780 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
781 of HTML. Requires inspect and pydoc. Crazy, man.
781 of HTML. Requires inspect and pydoc. Crazy, man.
782
782
783 Modified version which optionally strips the topmost entries from the
783 Modified version which optionally strips the topmost entries from the
784 traceback, to be used with alternate interpreters (because their own code
784 traceback, to be used with alternate interpreters (because their own code
785 would appear in the traceback)."""
785 would appear in the traceback)."""
786
786
787 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
787 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
788 tb_offset=0, long_header=False, include_vars=True,
788 tb_offset=0, long_header=False, include_vars=True,
789 check_cache=None, debugger_cls = None,
789 check_cache=None, debugger_cls = None,
790 parent=None, config=None):
790 parent=None, config=None):
791 """Specify traceback offset, headers and color scheme.
791 """Specify traceback offset, headers and color scheme.
792
792
793 Define how many frames to drop from the tracebacks. Calling it with
793 Define how many frames to drop from the tracebacks. Calling it with
794 tb_offset=1 allows use of this handler in interpreters which will have
794 tb_offset=1 allows use of this handler in interpreters which will have
795 their own code at the top of the traceback (VerboseTB will first
795 their own code at the top of the traceback (VerboseTB will first
796 remove that frame before printing the traceback info)."""
796 remove that frame before printing the traceback info)."""
797 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
797 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
798 ostream=ostream, parent=parent, config=config)
798 ostream=ostream, parent=parent, config=config)
799 self.tb_offset = tb_offset
799 self.tb_offset = tb_offset
800 self.long_header = long_header
800 self.long_header = long_header
801 self.include_vars = include_vars
801 self.include_vars = include_vars
802 # By default we use linecache.checkcache, but the user can provide a
802 # By default we use linecache.checkcache, but the user can provide a
803 # different check_cache implementation. This is used by the IPython
803 # different check_cache implementation. This is used by the IPython
804 # kernel to provide tracebacks for interactive code that is cached,
804 # kernel to provide tracebacks for interactive code that is cached,
805 # by a compiler instance that flushes the linecache but preserves its
805 # by a compiler instance that flushes the linecache but preserves its
806 # own code cache.
806 # own code cache.
807 if check_cache is None:
807 if check_cache is None:
808 check_cache = linecache.checkcache
808 check_cache = linecache.checkcache
809 self.check_cache = check_cache
809 self.check_cache = check_cache
810
810
811 self.debugger_cls = debugger_cls or debugger.Pdb
811 self.debugger_cls = debugger_cls or debugger.Pdb
812
812
813 def format_records(self, records, last_unique, recursion_repeat):
813 def format_records(self, records, last_unique, recursion_repeat):
814 """Format the stack frames of the traceback"""
814 """Format the stack frames of the traceback"""
815 frames = []
815 frames = []
816 for r in records[:last_unique+recursion_repeat+1]:
816 for r in records[:last_unique+recursion_repeat+1]:
817 #print '*** record:',file,lnum,func,lines,index # dbg
817 #print '*** record:',file,lnum,func,lines,index # dbg
818 frames.append(self.format_record(*r))
818 frames.append(self.format_record(*r))
819
819
820 if recursion_repeat:
820 if recursion_repeat:
821 frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat)
821 frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat)
822 frames.append(self.format_record(*records[last_unique+recursion_repeat+1]))
822 frames.append(self.format_record(*records[last_unique+recursion_repeat+1]))
823
823
824 return frames
824 return frames
825
825
826 def format_record(self, frame, file, lnum, func, lines, index):
826 def format_record(self, frame, file, lnum, func, lines, index):
827 """Format a single stack frame"""
827 """Format a single stack frame"""
828 Colors = self.Colors # just a shorthand + quicker name lookup
828 Colors = self.Colors # just a shorthand + quicker name lookup
829 ColorsNormal = Colors.Normal # used a lot
829 ColorsNormal = Colors.Normal # used a lot
830 col_scheme = self.color_scheme_table.active_scheme_name
830 col_scheme = self.color_scheme_table.active_scheme_name
831 indent = ' ' * INDENT_SIZE
831 indent = ' ' * INDENT_SIZE
832 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
832 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
833 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
833 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
834 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
834 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
835 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
835 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
836 ColorsNormal)
836 ColorsNormal)
837 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
837 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
838 (Colors.vName, Colors.valEm, ColorsNormal)
838 (Colors.vName, Colors.valEm, ColorsNormal)
839 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
839 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
840 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
840 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
841 Colors.vName, ColorsNormal)
841 Colors.vName, ColorsNormal)
842 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
842 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
843
843
844 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
844 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
845 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm, Colors.line,
845 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm, Colors.line,
846 ColorsNormal)
846 ColorsNormal)
847
847
848 abspath = os.path.abspath
848 abspath = os.path.abspath
849
849
850
850
851 if not file:
851 if not file:
852 file = '?'
852 file = '?'
853 elif file.startswith(str("<")) and file.endswith(str(">")):
853 elif file.startswith(str("<")) and file.endswith(str(">")):
854 # Not a real filename, no problem...
854 # Not a real filename, no problem...
855 pass
855 pass
856 elif not os.path.isabs(file):
856 elif not os.path.isabs(file):
857 # Try to make the filename absolute by trying all
857 # Try to make the filename absolute by trying all
858 # sys.path entries (which is also what linecache does)
858 # sys.path entries (which is also what linecache does)
859 for dirname in sys.path:
859 for dirname in sys.path:
860 try:
860 try:
861 fullname = os.path.join(dirname, file)
861 fullname = os.path.join(dirname, file)
862 if os.path.isfile(fullname):
862 if os.path.isfile(fullname):
863 file = os.path.abspath(fullname)
863 file = os.path.abspath(fullname)
864 break
864 break
865 except Exception:
865 except Exception:
866 # Just in case that sys.path contains very
866 # Just in case that sys.path contains very
867 # strange entries...
867 # strange entries...
868 pass
868 pass
869
869
870 file = py3compat.cast_unicode(file, util_path.fs_encoding)
870 file = py3compat.cast_unicode(file, util_path.fs_encoding)
871 link = tpl_link % util_path.compress_user(file)
871 link = tpl_link % util_path.compress_user(file)
872 args, varargs, varkw, locals = inspect.getargvalues(frame)
872 args, varargs, varkw, locals = inspect.getargvalues(frame)
873
873
874 if func == '?':
874 if func == '?':
875 call = ''
875 call = ''
876 else:
876 else:
877 # Decide whether to include variable details or not
877 # Decide whether to include variable details or not
878 var_repr = self.include_vars and eqrepr or nullrepr
878 var_repr = self.include_vars and eqrepr or nullrepr
879 try:
879 try:
880 call = tpl_call % (func, inspect.formatargvalues(args,
880 call = tpl_call % (func, inspect.formatargvalues(args,
881 varargs, varkw,
881 varargs, varkw,
882 locals, formatvalue=var_repr))
882 locals, formatvalue=var_repr))
883 except KeyError:
883 except KeyError:
884 # This happens in situations like errors inside generator
884 # This happens in situations like errors inside generator
885 # expressions, where local variables are listed in the
885 # expressions, where local variables are listed in the
886 # line, but can't be extracted from the frame. I'm not
886 # line, but can't be extracted from the frame. I'm not
887 # 100% sure this isn't actually a bug in inspect itself,
887 # 100% sure this isn't actually a bug in inspect itself,
888 # but since there's no info for us to compute with, the
888 # but since there's no info for us to compute with, the
889 # best we can do is report the failure and move on. Here
889 # best we can do is report the failure and move on. Here
890 # we must *not* call any traceback construction again,
890 # we must *not* call any traceback construction again,
891 # because that would mess up use of %debug later on. So we
891 # because that would mess up use of %debug later on. So we
892 # simply report the failure and move on. The only
892 # simply report the failure and move on. The only
893 # limitation will be that this frame won't have locals
893 # limitation will be that this frame won't have locals
894 # listed in the call signature. Quite subtle problem...
894 # listed in the call signature. Quite subtle problem...
895 # I can't think of a good way to validate this in a unit
895 # I can't think of a good way to validate this in a unit
896 # test, but running a script consisting of:
896 # test, but running a script consisting of:
897 # dict( (k,v.strip()) for (k,v) in range(10) )
897 # dict( (k,v.strip()) for (k,v) in range(10) )
898 # will illustrate the error, if this exception catch is
898 # will illustrate the error, if this exception catch is
899 # disabled.
899 # disabled.
900 call = tpl_call_fail % func
900 call = tpl_call_fail % func
901
901
902 # Don't attempt to tokenize binary files.
902 # Don't attempt to tokenize binary files.
903 if file.endswith(('.so', '.pyd', '.dll')):
903 if file.endswith(('.so', '.pyd', '.dll')):
904 return '%s %s\n' % (link, call)
904 return '%s %s\n' % (link, call)
905
905
906 elif file.endswith(('.pyc', '.pyo')):
906 elif file.endswith(('.pyc', '.pyo')):
907 # Look up the corresponding source file.
907 # Look up the corresponding source file.
908 try:
908 try:
909 file = openpy.source_from_cache(file)
909 file = openpy.source_from_cache(file)
910 except ValueError:
910 except ValueError:
911 # Failed to get the source file for some reason
911 # Failed to get the source file for some reason
912 # E.g. https://github.com/ipython/ipython/issues/9486
912 # E.g. https://github.com/ipython/ipython/issues/9486
913 return '%s %s\n' % (link, call)
913 return '%s %s\n' % (link, call)
914
914
915 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
915 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
916 line = getline(file, lnum[0])
916 line = getline(file, lnum[0])
917 lnum[0] += 1
917 lnum[0] += 1
918 return line
918 return line
919
919
920 # Build the list of names on this line of code where the exception
920 # Build the list of names on this line of code where the exception
921 # occurred.
921 # occurred.
922 try:
922 try:
923 names = []
923 names = []
924 name_cont = False
924 name_cont = False
925
925
926 for token_type, token, start, end, line in generate_tokens(linereader):
926 for token_type, token, start, end, line in generate_tokens(linereader):
927 # build composite names
927 # build composite names
928 if token_type == tokenize.NAME and token not in keyword.kwlist:
928 if token_type == tokenize.NAME and token not in keyword.kwlist:
929 if name_cont:
929 if name_cont:
930 # Continuation of a dotted name
930 # Continuation of a dotted name
931 try:
931 try:
932 names[-1].append(token)
932 names[-1].append(token)
933 except IndexError:
933 except IndexError:
934 names.append([token])
934 names.append([token])
935 name_cont = False
935 name_cont = False
936 else:
936 else:
937 # Regular new names. We append everything, the caller
937 # Regular new names. We append everything, the caller
938 # will be responsible for pruning the list later. It's
938 # will be responsible for pruning the list later. It's
939 # very tricky to try to prune as we go, b/c composite
939 # very tricky to try to prune as we go, b/c composite
940 # names can fool us. The pruning at the end is easy
940 # names can fool us. The pruning at the end is easy
941 # to do (or the caller can print a list with repeated
941 # to do (or the caller can print a list with repeated
942 # names if so desired.
942 # names if so desired.
943 names.append([token])
943 names.append([token])
944 elif token == '.':
944 elif token == '.':
945 name_cont = True
945 name_cont = True
946 elif token_type == tokenize.NEWLINE:
946 elif token_type == tokenize.NEWLINE:
947 break
947 break
948
948
949 except (IndexError, UnicodeDecodeError, SyntaxError):
949 except (IndexError, UnicodeDecodeError, SyntaxError):
950 # signals exit of tokenizer
950 # signals exit of tokenizer
951 # SyntaxError can occur if the file is not actually Python
951 # SyntaxError can occur if the file is not actually Python
952 # - see gh-6300
952 # - see gh-6300
953 pass
953 pass
954 except tokenize.TokenError as msg:
954 except tokenize.TokenError as msg:
955 # Tokenizing may fail for various reasons, many of which are
956 # harmless. (A good example is when the line in question is the
957 # close of a triple-quoted string, cf gh-6864). We don't want to
958 # show this to users, but want make it available for debugging
959 # purposes.
955 _m = ("An unexpected error occurred while tokenizing input\n"
960 _m = ("An unexpected error occurred while tokenizing input\n"
956 "The following traceback may be corrupted or invalid\n"
961 "The following traceback may be corrupted or invalid\n"
957 "The error message is: %s\n" % msg)
962 "The error message is: %s\n" % msg)
958 error(_m)
963 debug(_m)
959
964
960 # Join composite names (e.g. "dict.fromkeys")
965 # Join composite names (e.g. "dict.fromkeys")
961 names = ['.'.join(n) for n in names]
966 names = ['.'.join(n) for n in names]
962 # prune names list of duplicates, but keep the right order
967 # prune names list of duplicates, but keep the right order
963 unique_names = uniq_stable(names)
968 unique_names = uniq_stable(names)
964
969
965 # Start loop over vars
970 # Start loop over vars
966 lvals = []
971 lvals = []
967 if self.include_vars:
972 if self.include_vars:
968 for name_full in unique_names:
973 for name_full in unique_names:
969 name_base = name_full.split('.', 1)[0]
974 name_base = name_full.split('.', 1)[0]
970 if name_base in frame.f_code.co_varnames:
975 if name_base in frame.f_code.co_varnames:
971 if name_base in locals:
976 if name_base in locals:
972 try:
977 try:
973 value = repr(eval(name_full, locals))
978 value = repr(eval(name_full, locals))
974 except:
979 except:
975 value = undefined
980 value = undefined
976 else:
981 else:
977 value = undefined
982 value = undefined
978 name = tpl_local_var % name_full
983 name = tpl_local_var % name_full
979 else:
984 else:
980 if name_base in frame.f_globals:
985 if name_base in frame.f_globals:
981 try:
986 try:
982 value = repr(eval(name_full, frame.f_globals))
987 value = repr(eval(name_full, frame.f_globals))
983 except:
988 except:
984 value = undefined
989 value = undefined
985 else:
990 else:
986 value = undefined
991 value = undefined
987 name = tpl_global_var % name_full
992 name = tpl_global_var % name_full
988 lvals.append(tpl_name_val % (name, value))
993 lvals.append(tpl_name_val % (name, value))
989 if lvals:
994 if lvals:
990 lvals = '%s%s' % (indent, em_normal.join(lvals))
995 lvals = '%s%s' % (indent, em_normal.join(lvals))
991 else:
996 else:
992 lvals = ''
997 lvals = ''
993
998
994 level = '%s %s\n' % (link, call)
999 level = '%s %s\n' % (link, call)
995
1000
996 if index is None:
1001 if index is None:
997 return level
1002 return level
998 else:
1003 else:
999 _line_format = PyColorize.Parser(style=col_scheme, parent=self).format2
1004 _line_format = PyColorize.Parser(style=col_scheme, parent=self).format2
1000 return '%s%s' % (level, ''.join(
1005 return '%s%s' % (level, ''.join(
1001 _format_traceback_lines(lnum, index, lines, Colors, lvals,
1006 _format_traceback_lines(lnum, index, lines, Colors, lvals,
1002 _line_format)))
1007 _line_format)))
1003
1008
1004 def prepare_chained_exception_message(self, cause):
1009 def prepare_chained_exception_message(self, cause):
1005 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
1010 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
1006 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
1011 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
1007
1012
1008 if cause:
1013 if cause:
1009 message = [[direct_cause]]
1014 message = [[direct_cause]]
1010 else:
1015 else:
1011 message = [[exception_during_handling]]
1016 message = [[exception_during_handling]]
1012 return message
1017 return message
1013
1018
1014 def prepare_header(self, etype, long_version=False):
1019 def prepare_header(self, etype, long_version=False):
1015 colors = self.Colors # just a shorthand + quicker name lookup
1020 colors = self.Colors # just a shorthand + quicker name lookup
1016 colorsnormal = colors.Normal # used a lot
1021 colorsnormal = colors.Normal # used a lot
1017 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
1022 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
1018 width = min(75, get_terminal_size()[0])
1023 width = min(75, get_terminal_size()[0])
1019 if long_version:
1024 if long_version:
1020 # Header with the exception type, python version, and date
1025 # Header with the exception type, python version, and date
1021 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1026 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1022 date = time.ctime(time.time())
1027 date = time.ctime(time.time())
1023
1028
1024 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal,
1029 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal,
1025 exc, ' ' * (width - len(str(etype)) - len(pyver)),
1030 exc, ' ' * (width - len(str(etype)) - len(pyver)),
1026 pyver, date.rjust(width) )
1031 pyver, date.rjust(width) )
1027 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
1032 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
1028 "\ncalls leading up to the error, with the most recent (innermost) call last."
1033 "\ncalls leading up to the error, with the most recent (innermost) call last."
1029 else:
1034 else:
1030 # Simplified header
1035 # Simplified header
1031 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
1036 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
1032 rjust(width - len(str(etype))) )
1037 rjust(width - len(str(etype))) )
1033
1038
1034 return head
1039 return head
1035
1040
1036 def format_exception(self, etype, evalue):
1041 def format_exception(self, etype, evalue):
1037 colors = self.Colors # just a shorthand + quicker name lookup
1042 colors = self.Colors # just a shorthand + quicker name lookup
1038 colorsnormal = colors.Normal # used a lot
1043 colorsnormal = colors.Normal # used a lot
1039 indent = ' ' * INDENT_SIZE
1044 indent = ' ' * INDENT_SIZE
1040 # Get (safely) a string form of the exception info
1045 # Get (safely) a string form of the exception info
1041 try:
1046 try:
1042 etype_str, evalue_str = map(str, (etype, evalue))
1047 etype_str, evalue_str = map(str, (etype, evalue))
1043 except:
1048 except:
1044 # User exception is improperly defined.
1049 # User exception is improperly defined.
1045 etype, evalue = str, sys.exc_info()[:2]
1050 etype, evalue = str, sys.exc_info()[:2]
1046 etype_str, evalue_str = map(str, (etype, evalue))
1051 etype_str, evalue_str = map(str, (etype, evalue))
1047 # ... and format it
1052 # ... and format it
1048 return ['%s%s%s: %s' % (colors.excName, etype_str,
1053 return ['%s%s%s: %s' % (colors.excName, etype_str,
1049 colorsnormal, py3compat.cast_unicode(evalue_str))]
1054 colorsnormal, py3compat.cast_unicode(evalue_str))]
1050
1055
1051 def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset):
1056 def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset):
1052 """Formats the header, traceback and exception message for a single exception.
1057 """Formats the header, traceback and exception message for a single exception.
1053
1058
1054 This may be called multiple times by Python 3 exception chaining
1059 This may be called multiple times by Python 3 exception chaining
1055 (PEP 3134).
1060 (PEP 3134).
1056 """
1061 """
1057 # some locals
1062 # some locals
1058 orig_etype = etype
1063 orig_etype = etype
1059 try:
1064 try:
1060 etype = etype.__name__
1065 etype = etype.__name__
1061 except AttributeError:
1066 except AttributeError:
1062 pass
1067 pass
1063
1068
1064 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1069 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1065 head = self.prepare_header(etype, self.long_header)
1070 head = self.prepare_header(etype, self.long_header)
1066 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1071 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
1067
1072
1068 if records is None:
1073 if records is None:
1069 return ""
1074 return ""
1070
1075
1071 last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records)
1076 last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records)
1072
1077
1073 frames = self.format_records(records, last_unique, recursion_repeat)
1078 frames = self.format_records(records, last_unique, recursion_repeat)
1074
1079
1075 formatted_exception = self.format_exception(etype, evalue)
1080 formatted_exception = self.format_exception(etype, evalue)
1076 if records:
1081 if records:
1077 filepath, lnum = records[-1][1:3]
1082 filepath, lnum = records[-1][1:3]
1078 filepath = os.path.abspath(filepath)
1083 filepath = os.path.abspath(filepath)
1079 ipinst = get_ipython()
1084 ipinst = get_ipython()
1080 if ipinst is not None:
1085 if ipinst is not None:
1081 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
1086 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
1082
1087
1083 return [[head] + frames + [''.join(formatted_exception[0])]]
1088 return [[head] + frames + [''.join(formatted_exception[0])]]
1084
1089
1085 def get_records(self, etb, number_of_lines_of_context, tb_offset):
1090 def get_records(self, etb, number_of_lines_of_context, tb_offset):
1086 try:
1091 try:
1087 # Try the default getinnerframes and Alex's: Alex's fixes some
1092 # Try the default getinnerframes and Alex's: Alex's fixes some
1088 # problems, but it generates empty tracebacks for console errors
1093 # problems, but it generates empty tracebacks for console errors
1089 # (5 blanks lines) where none should be returned.
1094 # (5 blanks lines) where none should be returned.
1090 return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
1095 return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
1091 except UnicodeDecodeError:
1096 except UnicodeDecodeError:
1092 # This can occur if a file's encoding magic comment is wrong.
1097 # This can occur if a file's encoding magic comment is wrong.
1093 # I can't see a way to recover without duplicating a bunch of code
1098 # I can't see a way to recover without duplicating a bunch of code
1094 # from the stdlib traceback module. --TK
1099 # from the stdlib traceback module. --TK
1095 error('\nUnicodeDecodeError while processing traceback.\n')
1100 error('\nUnicodeDecodeError while processing traceback.\n')
1096 return None
1101 return None
1097 except:
1102 except:
1098 # FIXME: I've been getting many crash reports from python 2.3
1103 # FIXME: I've been getting many crash reports from python 2.3
1099 # users, traceable to inspect.py. If I can find a small test-case
1104 # users, traceable to inspect.py. If I can find a small test-case
1100 # to reproduce this, I should either write a better workaround or
1105 # to reproduce this, I should either write a better workaround or
1101 # file a bug report against inspect (if that's the real problem).
1106 # file a bug report against inspect (if that's the real problem).
1102 # So far, I haven't been able to find an isolated example to
1107 # So far, I haven't been able to find an isolated example to
1103 # reproduce the problem.
1108 # reproduce the problem.
1104 inspect_error()
1109 inspect_error()
1105 traceback.print_exc(file=self.ostream)
1110 traceback.print_exc(file=self.ostream)
1106 info('\nUnfortunately, your original traceback can not be constructed.\n')
1111 info('\nUnfortunately, your original traceback can not be constructed.\n')
1107 return None
1112 return None
1108
1113
1109 def get_parts_of_chained_exception(self, evalue):
1114 def get_parts_of_chained_exception(self, evalue):
1110 def get_chained_exception(exception_value):
1115 def get_chained_exception(exception_value):
1111 cause = getattr(exception_value, '__cause__', None)
1116 cause = getattr(exception_value, '__cause__', None)
1112 if cause:
1117 if cause:
1113 return cause
1118 return cause
1114 if getattr(exception_value, '__suppress_context__', False):
1119 if getattr(exception_value, '__suppress_context__', False):
1115 return None
1120 return None
1116 return getattr(exception_value, '__context__', None)
1121 return getattr(exception_value, '__context__', None)
1117
1122
1118 chained_evalue = get_chained_exception(evalue)
1123 chained_evalue = get_chained_exception(evalue)
1119
1124
1120 if chained_evalue:
1125 if chained_evalue:
1121 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
1126 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
1122
1127
1123 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1128 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1124 number_of_lines_of_context=5):
1129 number_of_lines_of_context=5):
1125 """Return a nice text document describing the traceback."""
1130 """Return a nice text document describing the traceback."""
1126
1131
1127 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1132 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1128 tb_offset)
1133 tb_offset)
1129
1134
1130 colors = self.Colors # just a shorthand + quicker name lookup
1135 colors = self.Colors # just a shorthand + quicker name lookup
1131 colorsnormal = colors.Normal # used a lot
1136 colorsnormal = colors.Normal # used a lot
1132 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1137 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1133 structured_traceback_parts = [head]
1138 structured_traceback_parts = [head]
1134 if py3compat.PY3:
1139 if py3compat.PY3:
1135 chained_exceptions_tb_offset = 0
1140 chained_exceptions_tb_offset = 0
1136 lines_of_context = 3
1141 lines_of_context = 3
1137 formatted_exceptions = formatted_exception
1142 formatted_exceptions = formatted_exception
1138 exception = self.get_parts_of_chained_exception(evalue)
1143 exception = self.get_parts_of_chained_exception(evalue)
1139 if exception:
1144 if exception:
1140 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1145 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1141 etype, evalue, etb = exception
1146 etype, evalue, etb = exception
1142 else:
1147 else:
1143 evalue = None
1148 evalue = None
1144 chained_exc_ids = set()
1149 chained_exc_ids = set()
1145 while evalue:
1150 while evalue:
1146 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1151 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1147 chained_exceptions_tb_offset)
1152 chained_exceptions_tb_offset)
1148 exception = self.get_parts_of_chained_exception(evalue)
1153 exception = self.get_parts_of_chained_exception(evalue)
1149
1154
1150 if exception and not id(exception[1]) in chained_exc_ids:
1155 if exception and not id(exception[1]) in chained_exc_ids:
1151 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1156 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1152 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1157 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1153 etype, evalue, etb = exception
1158 etype, evalue, etb = exception
1154 else:
1159 else:
1155 evalue = None
1160 evalue = None
1156
1161
1157 # we want to see exceptions in a reversed order:
1162 # we want to see exceptions in a reversed order:
1158 # the first exception should be on top
1163 # the first exception should be on top
1159 for formatted_exception in reversed(formatted_exceptions):
1164 for formatted_exception in reversed(formatted_exceptions):
1160 structured_traceback_parts += formatted_exception
1165 structured_traceback_parts += formatted_exception
1161 else:
1166 else:
1162 structured_traceback_parts += formatted_exception[0]
1167 structured_traceback_parts += formatted_exception[0]
1163
1168
1164 return structured_traceback_parts
1169 return structured_traceback_parts
1165
1170
1166 def debugger(self, force=False):
1171 def debugger(self, force=False):
1167 """Call up the pdb debugger if desired, always clean up the tb
1172 """Call up the pdb debugger if desired, always clean up the tb
1168 reference.
1173 reference.
1169
1174
1170 Keywords:
1175 Keywords:
1171
1176
1172 - force(False): by default, this routine checks the instance call_pdb
1177 - force(False): by default, this routine checks the instance call_pdb
1173 flag and does not actually invoke the debugger if the flag is false.
1178 flag and does not actually invoke the debugger if the flag is false.
1174 The 'force' option forces the debugger to activate even if the flag
1179 The 'force' option forces the debugger to activate even if the flag
1175 is false.
1180 is false.
1176
1181
1177 If the call_pdb flag is set, the pdb interactive debugger is
1182 If the call_pdb flag is set, the pdb interactive debugger is
1178 invoked. In all cases, the self.tb reference to the current traceback
1183 invoked. In all cases, the self.tb reference to the current traceback
1179 is deleted to prevent lingering references which hamper memory
1184 is deleted to prevent lingering references which hamper memory
1180 management.
1185 management.
1181
1186
1182 Note that each call to pdb() does an 'import readline', so if your app
1187 Note that each call to pdb() does an 'import readline', so if your app
1183 requires a special setup for the readline completers, you'll have to
1188 requires a special setup for the readline completers, you'll have to
1184 fix that by hand after invoking the exception handler."""
1189 fix that by hand after invoking the exception handler."""
1185
1190
1186 if force or self.call_pdb:
1191 if force or self.call_pdb:
1187 if self.pdb is None:
1192 if self.pdb is None:
1188 self.pdb = self.debugger_cls()
1193 self.pdb = self.debugger_cls()
1189 # the system displayhook may have changed, restore the original
1194 # the system displayhook may have changed, restore the original
1190 # for pdb
1195 # for pdb
1191 display_trap = DisplayTrap(hook=sys.__displayhook__)
1196 display_trap = DisplayTrap(hook=sys.__displayhook__)
1192 with display_trap:
1197 with display_trap:
1193 self.pdb.reset()
1198 self.pdb.reset()
1194 # Find the right frame so we don't pop up inside ipython itself
1199 # Find the right frame so we don't pop up inside ipython itself
1195 if hasattr(self, 'tb') and self.tb is not None:
1200 if hasattr(self, 'tb') and self.tb is not None:
1196 etb = self.tb
1201 etb = self.tb
1197 else:
1202 else:
1198 etb = self.tb = sys.last_traceback
1203 etb = self.tb = sys.last_traceback
1199 while self.tb is not None and self.tb.tb_next is not None:
1204 while self.tb is not None and self.tb.tb_next is not None:
1200 self.tb = self.tb.tb_next
1205 self.tb = self.tb.tb_next
1201 if etb and etb.tb_next:
1206 if etb and etb.tb_next:
1202 etb = etb.tb_next
1207 etb = etb.tb_next
1203 self.pdb.botframe = etb.tb_frame
1208 self.pdb.botframe = etb.tb_frame
1204 self.pdb.interaction(self.tb.tb_frame, self.tb)
1209 self.pdb.interaction(self.tb.tb_frame, self.tb)
1205
1210
1206 if hasattr(self, 'tb'):
1211 if hasattr(self, 'tb'):
1207 del self.tb
1212 del self.tb
1208
1213
1209 def handler(self, info=None):
1214 def handler(self, info=None):
1210 (etype, evalue, etb) = info or sys.exc_info()
1215 (etype, evalue, etb) = info or sys.exc_info()
1211 self.tb = etb
1216 self.tb = etb
1212 ostream = self.ostream
1217 ostream = self.ostream
1213 ostream.flush()
1218 ostream.flush()
1214 ostream.write(self.text(etype, evalue, etb))
1219 ostream.write(self.text(etype, evalue, etb))
1215 ostream.write('\n')
1220 ostream.write('\n')
1216 ostream.flush()
1221 ostream.flush()
1217
1222
1218 # Changed so an instance can just be called as VerboseTB_inst() and print
1223 # Changed so an instance can just be called as VerboseTB_inst() and print
1219 # out the right info on its own.
1224 # out the right info on its own.
1220 def __call__(self, etype=None, evalue=None, etb=None):
1225 def __call__(self, etype=None, evalue=None, etb=None):
1221 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1226 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1222 if etb is None:
1227 if etb is None:
1223 self.handler()
1228 self.handler()
1224 else:
1229 else:
1225 self.handler((etype, evalue, etb))
1230 self.handler((etype, evalue, etb))
1226 try:
1231 try:
1227 self.debugger()
1232 self.debugger()
1228 except KeyboardInterrupt:
1233 except KeyboardInterrupt:
1229 print("\nKeyboardInterrupt")
1234 print("\nKeyboardInterrupt")
1230
1235
1231
1236
1232 #----------------------------------------------------------------------------
1237 #----------------------------------------------------------------------------
1233 class FormattedTB(VerboseTB, ListTB):
1238 class FormattedTB(VerboseTB, ListTB):
1234 """Subclass ListTB but allow calling with a traceback.
1239 """Subclass ListTB but allow calling with a traceback.
1235
1240
1236 It can thus be used as a sys.excepthook for Python > 2.1.
1241 It can thus be used as a sys.excepthook for Python > 2.1.
1237
1242
1238 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1243 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1239
1244
1240 Allows a tb_offset to be specified. This is useful for situations where
1245 Allows a tb_offset to be specified. This is useful for situations where
1241 one needs to remove a number of topmost frames from the traceback (such as
1246 one needs to remove a number of topmost frames from the traceback (such as
1242 occurs with python programs that themselves execute other python code,
1247 occurs with python programs that themselves execute other python code,
1243 like Python shells). """
1248 like Python shells). """
1244
1249
1245 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1250 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1246 ostream=None,
1251 ostream=None,
1247 tb_offset=0, long_header=False, include_vars=False,
1252 tb_offset=0, long_header=False, include_vars=False,
1248 check_cache=None, debugger_cls=None,
1253 check_cache=None, debugger_cls=None,
1249 parent=None, config=None):
1254 parent=None, config=None):
1250
1255
1251 # NEVER change the order of this list. Put new modes at the end:
1256 # NEVER change the order of this list. Put new modes at the end:
1252 self.valid_modes = ['Plain', 'Context', 'Verbose']
1257 self.valid_modes = ['Plain', 'Context', 'Verbose']
1253 self.verbose_modes = self.valid_modes[1:3]
1258 self.verbose_modes = self.valid_modes[1:3]
1254
1259
1255 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1260 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1256 ostream=ostream, tb_offset=tb_offset,
1261 ostream=ostream, tb_offset=tb_offset,
1257 long_header=long_header, include_vars=include_vars,
1262 long_header=long_header, include_vars=include_vars,
1258 check_cache=check_cache, debugger_cls=debugger_cls,
1263 check_cache=check_cache, debugger_cls=debugger_cls,
1259 parent=parent, config=config)
1264 parent=parent, config=config)
1260
1265
1261 # Different types of tracebacks are joined with different separators to
1266 # Different types of tracebacks are joined with different separators to
1262 # form a single string. They are taken from this dict
1267 # form a single string. They are taken from this dict
1263 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1268 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1264 # set_mode also sets the tb_join_char attribute
1269 # set_mode also sets the tb_join_char attribute
1265 self.set_mode(mode)
1270 self.set_mode(mode)
1266
1271
1267 def _extract_tb(self, tb):
1272 def _extract_tb(self, tb):
1268 if tb:
1273 if tb:
1269 return traceback.extract_tb(tb)
1274 return traceback.extract_tb(tb)
1270 else:
1275 else:
1271 return None
1276 return None
1272
1277
1273 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1278 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1274 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1279 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1275 mode = self.mode
1280 mode = self.mode
1276 if mode in self.verbose_modes:
1281 if mode in self.verbose_modes:
1277 # Verbose modes need a full traceback
1282 # Verbose modes need a full traceback
1278 return VerboseTB.structured_traceback(
1283 return VerboseTB.structured_traceback(
1279 self, etype, value, tb, tb_offset, number_of_lines_of_context
1284 self, etype, value, tb, tb_offset, number_of_lines_of_context
1280 )
1285 )
1281 else:
1286 else:
1282 # We must check the source cache because otherwise we can print
1287 # We must check the source cache because otherwise we can print
1283 # out-of-date source code.
1288 # out-of-date source code.
1284 self.check_cache()
1289 self.check_cache()
1285 # Now we can extract and format the exception
1290 # Now we can extract and format the exception
1286 elist = self._extract_tb(tb)
1291 elist = self._extract_tb(tb)
1287 return ListTB.structured_traceback(
1292 return ListTB.structured_traceback(
1288 self, etype, value, elist, tb_offset, number_of_lines_of_context
1293 self, etype, value, elist, tb_offset, number_of_lines_of_context
1289 )
1294 )
1290
1295
1291 def stb2text(self, stb):
1296 def stb2text(self, stb):
1292 """Convert a structured traceback (a list) to a string."""
1297 """Convert a structured traceback (a list) to a string."""
1293 return self.tb_join_char.join(stb)
1298 return self.tb_join_char.join(stb)
1294
1299
1295
1300
1296 def set_mode(self, mode=None):
1301 def set_mode(self, mode=None):
1297 """Switch to the desired mode.
1302 """Switch to the desired mode.
1298
1303
1299 If mode is not specified, cycles through the available modes."""
1304 If mode is not specified, cycles through the available modes."""
1300
1305
1301 if not mode:
1306 if not mode:
1302 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1307 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1303 len(self.valid_modes)
1308 len(self.valid_modes)
1304 self.mode = self.valid_modes[new_idx]
1309 self.mode = self.valid_modes[new_idx]
1305 elif mode not in self.valid_modes:
1310 elif mode not in self.valid_modes:
1306 raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n'
1311 raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n'
1307 'Valid modes: ' + str(self.valid_modes))
1312 'Valid modes: ' + str(self.valid_modes))
1308 else:
1313 else:
1309 self.mode = mode
1314 self.mode = mode
1310 # include variable details only in 'Verbose' mode
1315 # include variable details only in 'Verbose' mode
1311 self.include_vars = (self.mode == self.valid_modes[2])
1316 self.include_vars = (self.mode == self.valid_modes[2])
1312 # Set the join character for generating text tracebacks
1317 # Set the join character for generating text tracebacks
1313 self.tb_join_char = self._join_chars[self.mode]
1318 self.tb_join_char = self._join_chars[self.mode]
1314
1319
1315 # some convenient shortcuts
1320 # some convenient shortcuts
1316 def plain(self):
1321 def plain(self):
1317 self.set_mode(self.valid_modes[0])
1322 self.set_mode(self.valid_modes[0])
1318
1323
1319 def context(self):
1324 def context(self):
1320 self.set_mode(self.valid_modes[1])
1325 self.set_mode(self.valid_modes[1])
1321
1326
1322 def verbose(self):
1327 def verbose(self):
1323 self.set_mode(self.valid_modes[2])
1328 self.set_mode(self.valid_modes[2])
1324
1329
1325
1330
1326 #----------------------------------------------------------------------------
1331 #----------------------------------------------------------------------------
1327 class AutoFormattedTB(FormattedTB):
1332 class AutoFormattedTB(FormattedTB):
1328 """A traceback printer which can be called on the fly.
1333 """A traceback printer which can be called on the fly.
1329
1334
1330 It will find out about exceptions by itself.
1335 It will find out about exceptions by itself.
1331
1336
1332 A brief example::
1337 A brief example::
1333
1338
1334 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1339 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1335 try:
1340 try:
1336 ...
1341 ...
1337 except:
1342 except:
1338 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1343 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1339 """
1344 """
1340
1345
1341 def __call__(self, etype=None, evalue=None, etb=None,
1346 def __call__(self, etype=None, evalue=None, etb=None,
1342 out=None, tb_offset=None):
1347 out=None, tb_offset=None):
1343 """Print out a formatted exception traceback.
1348 """Print out a formatted exception traceback.
1344
1349
1345 Optional arguments:
1350 Optional arguments:
1346 - out: an open file-like object to direct output to.
1351 - out: an open file-like object to direct output to.
1347
1352
1348 - tb_offset: the number of frames to skip over in the stack, on a
1353 - tb_offset: the number of frames to skip over in the stack, on a
1349 per-call basis (this overrides temporarily the instance's tb_offset
1354 per-call basis (this overrides temporarily the instance's tb_offset
1350 given at initialization time. """
1355 given at initialization time. """
1351
1356
1352 if out is None:
1357 if out is None:
1353 out = self.ostream
1358 out = self.ostream
1354 out.flush()
1359 out.flush()
1355 out.write(self.text(etype, evalue, etb, tb_offset))
1360 out.write(self.text(etype, evalue, etb, tb_offset))
1356 out.write('\n')
1361 out.write('\n')
1357 out.flush()
1362 out.flush()
1358 # FIXME: we should remove the auto pdb behavior from here and leave
1363 # FIXME: we should remove the auto pdb behavior from here and leave
1359 # that to the clients.
1364 # that to the clients.
1360 try:
1365 try:
1361 self.debugger()
1366 self.debugger()
1362 except KeyboardInterrupt:
1367 except KeyboardInterrupt:
1363 print("\nKeyboardInterrupt")
1368 print("\nKeyboardInterrupt")
1364
1369
1365 def structured_traceback(self, etype=None, value=None, tb=None,
1370 def structured_traceback(self, etype=None, value=None, tb=None,
1366 tb_offset=None, number_of_lines_of_context=5):
1371 tb_offset=None, number_of_lines_of_context=5):
1367 if etype is None:
1372 if etype is None:
1368 etype, value, tb = sys.exc_info()
1373 etype, value, tb = sys.exc_info()
1369 self.tb = tb
1374 self.tb = tb
1370 return FormattedTB.structured_traceback(
1375 return FormattedTB.structured_traceback(
1371 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1376 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1372
1377
1373
1378
1374 #---------------------------------------------------------------------------
1379 #---------------------------------------------------------------------------
1375
1380
1376 # A simple class to preserve Nathan's original functionality.
1381 # A simple class to preserve Nathan's original functionality.
1377 class ColorTB(FormattedTB):
1382 class ColorTB(FormattedTB):
1378 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1383 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1379
1384
1380 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1385 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1381 FormattedTB.__init__(self, color_scheme=color_scheme,
1386 FormattedTB.__init__(self, color_scheme=color_scheme,
1382 call_pdb=call_pdb, **kwargs)
1387 call_pdb=call_pdb, **kwargs)
1383
1388
1384
1389
1385 class SyntaxTB(ListTB):
1390 class SyntaxTB(ListTB):
1386 """Extension which holds some state: the last exception value"""
1391 """Extension which holds some state: the last exception value"""
1387
1392
1388 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1393 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1389 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1394 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1390 self.last_syntax_error = None
1395 self.last_syntax_error = None
1391
1396
1392 def __call__(self, etype, value, elist):
1397 def __call__(self, etype, value, elist):
1393 self.last_syntax_error = value
1398 self.last_syntax_error = value
1394
1399
1395 ListTB.__call__(self, etype, value, elist)
1400 ListTB.__call__(self, etype, value, elist)
1396
1401
1397 def structured_traceback(self, etype, value, elist, tb_offset=None,
1402 def structured_traceback(self, etype, value, elist, tb_offset=None,
1398 context=5):
1403 context=5):
1399 # If the source file has been edited, the line in the syntax error can
1404 # If the source file has been edited, the line in the syntax error can
1400 # be wrong (retrieved from an outdated cache). This replaces it with
1405 # be wrong (retrieved from an outdated cache). This replaces it with
1401 # the current value.
1406 # the current value.
1402 if isinstance(value, SyntaxError) \
1407 if isinstance(value, SyntaxError) \
1403 and isinstance(value.filename, str) \
1408 and isinstance(value.filename, str) \
1404 and isinstance(value.lineno, int):
1409 and isinstance(value.lineno, int):
1405 linecache.checkcache(value.filename)
1410 linecache.checkcache(value.filename)
1406 newtext = linecache.getline(value.filename, value.lineno)
1411 newtext = linecache.getline(value.filename, value.lineno)
1407 if newtext:
1412 if newtext:
1408 value.text = newtext
1413 value.text = newtext
1409 self.last_syntax_error = value
1414 self.last_syntax_error = value
1410 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1415 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1411 tb_offset=tb_offset, context=context)
1416 tb_offset=tb_offset, context=context)
1412
1417
1413 def clear_err_state(self):
1418 def clear_err_state(self):
1414 """Return the current error state and clear it"""
1419 """Return the current error state and clear it"""
1415 e = self.last_syntax_error
1420 e = self.last_syntax_error
1416 self.last_syntax_error = None
1421 self.last_syntax_error = None
1417 return e
1422 return e
1418
1423
1419 def stb2text(self, stb):
1424 def stb2text(self, stb):
1420 """Convert a structured traceback (a list) to a string."""
1425 """Convert a structured traceback (a list) to a string."""
1421 return ''.join(stb)
1426 return ''.join(stb)
1422
1427
1423
1428
1424 # some internal-use functions
1429 # some internal-use functions
1425 def text_repr(value):
1430 def text_repr(value):
1426 """Hopefully pretty robust repr equivalent."""
1431 """Hopefully pretty robust repr equivalent."""
1427 # this is pretty horrible but should always return *something*
1432 # this is pretty horrible but should always return *something*
1428 try:
1433 try:
1429 return pydoc.text.repr(value)
1434 return pydoc.text.repr(value)
1430 except KeyboardInterrupt:
1435 except KeyboardInterrupt:
1431 raise
1436 raise
1432 except:
1437 except:
1433 try:
1438 try:
1434 return repr(value)
1439 return repr(value)
1435 except KeyboardInterrupt:
1440 except KeyboardInterrupt:
1436 raise
1441 raise
1437 except:
1442 except:
1438 try:
1443 try:
1439 # all still in an except block so we catch
1444 # all still in an except block so we catch
1440 # getattr raising
1445 # getattr raising
1441 name = getattr(value, '__name__', None)
1446 name = getattr(value, '__name__', None)
1442 if name:
1447 if name:
1443 # ick, recursion
1448 # ick, recursion
1444 return text_repr(name)
1449 return text_repr(name)
1445 klass = getattr(value, '__class__', None)
1450 klass = getattr(value, '__class__', None)
1446 if klass:
1451 if klass:
1447 return '%s instance' % text_repr(klass)
1452 return '%s instance' % text_repr(klass)
1448 except KeyboardInterrupt:
1453 except KeyboardInterrupt:
1449 raise
1454 raise
1450 except:
1455 except:
1451 return 'UNRECOVERABLE REPR FAILURE'
1456 return 'UNRECOVERABLE REPR FAILURE'
1452
1457
1453
1458
1454 def eqrepr(value, repr=text_repr):
1459 def eqrepr(value, repr=text_repr):
1455 return '=%s' % repr(value)
1460 return '=%s' % repr(value)
1456
1461
1457
1462
1458 def nullrepr(value, repr=text_repr):
1463 def nullrepr(value, repr=text_repr):
1459 return ''
1464 return ''
General Comments 0
You need to be logged in to leave comments. Login now