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