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