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