##// END OF EJS Templates
Merge pull request #7071 from jdemeyer/tb_abs_filename...
Matthias Bussonnier -
r19199:310ccf30 merge
parent child Browse files
Show More
@@ -1,1360 +1,1368
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__, which should
249 249 # be better.
250 250 better_fn = frame.f_globals.get('__file__', None)
251 251 if isinstance(better_fn, str):
252 252 # Check the type just in case someone did something weird with
253 253 # __file__. It might also be None if the error occurred during
254 254 # import.
255 255 filename = better_fn
256 256 fixed_records.append((frame, filename, line_no, func_name, lines, index))
257 257 return fixed_records
258 258
259 259
260 260 @with_patch_inspect
261 261 def _fixed_getinnerframes(etb, context=1, tb_offset=0):
262 262 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
263 263
264 264 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
265 265 # If the error is at the console, don't build any context, since it would
266 266 # otherwise produce 5 blank lines printed out (there is no file at the
267 267 # console)
268 268 rec_check = records[tb_offset:]
269 269 try:
270 270 rname = rec_check[0][1]
271 271 if rname == '<ipython console>' or rname.endswith('<string>'):
272 272 return rec_check
273 273 except IndexError:
274 274 pass
275 275
276 276 aux = traceback.extract_tb(etb)
277 277 assert len(records) == len(aux)
278 278 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
279 279 maybeStart = lnum - 1 - context // 2
280 280 start = max(maybeStart, 0)
281 281 end = start + context
282 282 lines = ulinecache.getlines(file)[start:end]
283 283 buf = list(records[i])
284 284 buf[LNUM_POS] = lnum
285 285 buf[INDEX_POS] = lnum - 1 - start
286 286 buf[LINES_POS] = lines
287 287 records[i] = tuple(buf)
288 288 return records[tb_offset:]
289 289
290 290 # Helper function -- largely belongs to VerboseTB, but we need the same
291 291 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
292 292 # can be recognized properly by ipython.el's py-traceback-line-re
293 293 # (SyntaxErrors have to be treated specially because they have no traceback)
294 294
295 295 _parser = PyColorize.Parser()
296 296
297 297
298 298 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None, scheme=None):
299 299 numbers_width = INDENT_SIZE - 1
300 300 res = []
301 301 i = lnum - index
302 302
303 303 # This lets us get fully syntax-highlighted tracebacks.
304 304 if scheme is None:
305 305 ipinst = get_ipython()
306 306 if ipinst is not None:
307 307 scheme = ipinst.colors
308 308 else:
309 309 scheme = DEFAULT_SCHEME
310 310
311 311 _line_format = _parser.format2
312 312
313 313 for line in lines:
314 314 line = py3compat.cast_unicode(line)
315 315
316 316 new_line, err = _line_format(line, 'str', scheme)
317 317 if not err: line = new_line
318 318
319 319 if i == lnum:
320 320 # This is the line with the error
321 321 pad = numbers_width - len(str(i))
322 322 if pad >= 3:
323 323 marker = '-' * (pad - 3) + '-> '
324 324 elif pad == 2:
325 325 marker = '> '
326 326 elif pad == 1:
327 327 marker = '>'
328 328 else:
329 329 marker = ''
330 330 num = marker + str(i)
331 331 line = '%s%s%s %s%s' % (Colors.linenoEm, num,
332 332 Colors.line, line, Colors.Normal)
333 333 else:
334 334 num = '%*s' % (numbers_width, i)
335 335 line = '%s%s%s %s' % (Colors.lineno, num,
336 336 Colors.Normal, line)
337 337
338 338 res.append(line)
339 339 if lvals and i == lnum:
340 340 res.append(lvals + '\n')
341 341 i = i + 1
342 342 return res
343 343
344 344
345 345 #---------------------------------------------------------------------------
346 346 # Module classes
347 347 class TBTools(object):
348 348 """Basic tools used by all traceback printer classes."""
349 349
350 350 # Number of frames to skip when reporting tracebacks
351 351 tb_offset = 0
352 352
353 353 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
354 354 # Whether to call the interactive pdb debugger after printing
355 355 # tracebacks or not
356 356 self.call_pdb = call_pdb
357 357
358 358 # Output stream to write to. Note that we store the original value in
359 359 # a private attribute and then make the public ostream a property, so
360 360 # that we can delay accessing io.stdout until runtime. The way
361 361 # things are written now, the io.stdout object is dynamically managed
362 362 # so a reference to it should NEVER be stored statically. This
363 363 # property approach confines this detail to a single location, and all
364 364 # subclasses can simply access self.ostream for writing.
365 365 self._ostream = ostream
366 366
367 367 # Create color table
368 368 self.color_scheme_table = exception_colors()
369 369
370 370 self.set_colors(color_scheme)
371 371 self.old_scheme = color_scheme # save initial value for toggles
372 372
373 373 if call_pdb:
374 374 self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name)
375 375 else:
376 376 self.pdb = None
377 377
378 378 def _get_ostream(self):
379 379 """Output stream that exceptions are written to.
380 380
381 381 Valid values are:
382 382
383 383 - None: the default, which means that IPython will dynamically resolve
384 384 to io.stdout. This ensures compatibility with most tools, including
385 385 Windows (where plain stdout doesn't recognize ANSI escapes).
386 386
387 387 - Any object with 'write' and 'flush' attributes.
388 388 """
389 389 return io.stdout if self._ostream is None else self._ostream
390 390
391 391 def _set_ostream(self, val):
392 392 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
393 393 self._ostream = val
394 394
395 395 ostream = property(_get_ostream, _set_ostream)
396 396
397 397 def set_colors(self, *args, **kw):
398 398 """Shorthand access to the color table scheme selector method."""
399 399
400 400 # Set own color table
401 401 self.color_scheme_table.set_active_scheme(*args, **kw)
402 402 # for convenience, set Colors to the active scheme
403 403 self.Colors = self.color_scheme_table.active_colors
404 404 # Also set colors of debugger
405 405 if hasattr(self, 'pdb') and self.pdb is not None:
406 406 self.pdb.set_colors(*args, **kw)
407 407
408 408 def color_toggle(self):
409 409 """Toggle between the currently active color scheme and NoColor."""
410 410
411 411 if self.color_scheme_table.active_scheme_name == 'NoColor':
412 412 self.color_scheme_table.set_active_scheme(self.old_scheme)
413 413 self.Colors = self.color_scheme_table.active_colors
414 414 else:
415 415 self.old_scheme = self.color_scheme_table.active_scheme_name
416 416 self.color_scheme_table.set_active_scheme('NoColor')
417 417 self.Colors = self.color_scheme_table.active_colors
418 418
419 419 def stb2text(self, stb):
420 420 """Convert a structured traceback (a list) to a string."""
421 421 return '\n'.join(stb)
422 422
423 423 def text(self, etype, value, tb, tb_offset=None, context=5):
424 424 """Return formatted traceback.
425 425
426 426 Subclasses may override this if they add extra arguments.
427 427 """
428 428 tb_list = self.structured_traceback(etype, value, tb,
429 429 tb_offset, context)
430 430 return self.stb2text(tb_list)
431 431
432 432 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
433 433 context=5, mode=None):
434 434 """Return a list of traceback frames.
435 435
436 436 Must be implemented by each class.
437 437 """
438 438 raise NotImplementedError()
439 439
440 440
441 441 #---------------------------------------------------------------------------
442 442 class ListTB(TBTools):
443 443 """Print traceback information from a traceback list, with optional color.
444 444
445 445 Calling requires 3 arguments: (etype, evalue, elist)
446 446 as would be obtained by::
447 447
448 448 etype, evalue, tb = sys.exc_info()
449 449 if tb:
450 450 elist = traceback.extract_tb(tb)
451 451 else:
452 452 elist = None
453 453
454 454 It can thus be used by programs which need to process the traceback before
455 455 printing (such as console replacements based on the code module from the
456 456 standard library).
457 457
458 458 Because they are meant to be called without a full traceback (only a
459 459 list), instances of this class can't call the interactive pdb debugger."""
460 460
461 461 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
462 462 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
463 463 ostream=ostream)
464 464
465 465 def __call__(self, etype, value, elist):
466 466 self.ostream.flush()
467 467 self.ostream.write(self.text(etype, value, elist))
468 468 self.ostream.write('\n')
469 469
470 470 def structured_traceback(self, etype, value, elist, tb_offset=None,
471 471 context=5):
472 472 """Return a color formatted string with the traceback info.
473 473
474 474 Parameters
475 475 ----------
476 476 etype : exception type
477 477 Type of the exception raised.
478 478
479 479 value : object
480 480 Data stored in the exception
481 481
482 482 elist : list
483 483 List of frames, see class docstring for details.
484 484
485 485 tb_offset : int, optional
486 486 Number of frames in the traceback to skip. If not given, the
487 487 instance value is used (set in constructor).
488 488
489 489 context : int, optional
490 490 Number of lines of context information to print.
491 491
492 492 Returns
493 493 -------
494 494 String with formatted exception.
495 495 """
496 496 tb_offset = self.tb_offset if tb_offset is None else tb_offset
497 497 Colors = self.Colors
498 498 out_list = []
499 499 if elist:
500 500
501 501 if tb_offset and len(elist) > tb_offset:
502 502 elist = elist[tb_offset:]
503 503
504 504 out_list.append('Traceback %s(most recent call last)%s:' %
505 505 (Colors.normalEm, Colors.Normal) + '\n')
506 506 out_list.extend(self._format_list(elist))
507 507 # The exception info should be a single entry in the list.
508 508 lines = ''.join(self._format_exception_only(etype, value))
509 509 out_list.append(lines)
510 510
511 511 # Note: this code originally read:
512 512
513 513 ## for line in lines[:-1]:
514 514 ## out_list.append(" "+line)
515 515 ## out_list.append(lines[-1])
516 516
517 517 # This means it was indenting everything but the last line by a little
518 518 # bit. I've disabled this for now, but if we see ugliness somewhere we
519 519 # can restore it.
520 520
521 521 return out_list
522 522
523 523 def _format_list(self, extracted_list):
524 524 """Format a list of traceback entry tuples for printing.
525 525
526 526 Given a list of tuples as returned by extract_tb() or
527 527 extract_stack(), return a list of strings ready for printing.
528 528 Each string in the resulting list corresponds to the item with the
529 529 same index in the argument list. Each string ends in a newline;
530 530 the strings may contain internal newlines as well, for those items
531 531 whose source text line is not None.
532 532
533 533 Lifted almost verbatim from traceback.py
534 534 """
535 535
536 536 Colors = self.Colors
537 537 list = []
538 538 for filename, lineno, name, line in extracted_list[:-1]:
539 539 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
540 540 (Colors.filename, filename, Colors.Normal,
541 541 Colors.lineno, lineno, Colors.Normal,
542 542 Colors.name, name, Colors.Normal)
543 543 if line:
544 544 item += ' %s\n' % line.strip()
545 545 list.append(item)
546 546 # Emphasize the last entry
547 547 filename, lineno, name, line = extracted_list[-1]
548 548 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
549 549 (Colors.normalEm,
550 550 Colors.filenameEm, filename, Colors.normalEm,
551 551 Colors.linenoEm, lineno, Colors.normalEm,
552 552 Colors.nameEm, name, Colors.normalEm,
553 553 Colors.Normal)
554 554 if line:
555 555 item += '%s %s%s\n' % (Colors.line, line.strip(),
556 556 Colors.Normal)
557 557 list.append(item)
558 558 return list
559 559
560 560 def _format_exception_only(self, etype, value):
561 561 """Format the exception part of a traceback.
562 562
563 563 The arguments are the exception type and value such as given by
564 564 sys.exc_info()[:2]. The return value is a list of strings, each ending
565 565 in a newline. Normally, the list contains a single string; however,
566 566 for SyntaxError exceptions, it contains several lines that (when
567 567 printed) display detailed information about where the syntax error
568 568 occurred. The message indicating which exception occurred is the
569 569 always last string in the list.
570 570
571 571 Also lifted nearly verbatim from traceback.py
572 572 """
573 573 have_filedata = False
574 574 Colors = self.Colors
575 575 list = []
576 576 stype = Colors.excName + etype.__name__ + Colors.Normal
577 577 if value is None:
578 578 # Not sure if this can still happen in Python 2.6 and above
579 579 list.append(py3compat.cast_unicode(stype) + '\n')
580 580 else:
581 581 if issubclass(etype, SyntaxError):
582 582 have_filedata = True
583 583 if not value.filename: value.filename = "<string>"
584 584 if value.lineno:
585 585 lineno = value.lineno
586 586 textline = ulinecache.getline(value.filename, value.lineno)
587 587 else:
588 588 lineno = 'unknown'
589 589 textline = ''
590 590 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
591 591 (Colors.normalEm,
592 592 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
593 593 Colors.linenoEm, lineno, Colors.Normal ))
594 594 if textline == '':
595 595 textline = py3compat.cast_unicode(value.text, "utf-8")
596 596
597 597 if textline is not None:
598 598 i = 0
599 599 while i < len(textline) and textline[i].isspace():
600 600 i += 1
601 601 list.append('%s %s%s\n' % (Colors.line,
602 602 textline.strip(),
603 603 Colors.Normal))
604 604 if value.offset is not None:
605 605 s = ' '
606 606 for c in textline[i:value.offset - 1]:
607 607 if c.isspace():
608 608 s += c
609 609 else:
610 610 s += ' '
611 611 list.append('%s%s^%s\n' % (Colors.caret, s,
612 612 Colors.Normal))
613 613
614 614 try:
615 615 s = value.msg
616 616 except Exception:
617 617 s = self._some_str(value)
618 618 if s:
619 619 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
620 620 Colors.Normal, s))
621 621 else:
622 622 list.append('%s\n' % str(stype))
623 623
624 624 # sync with user hooks
625 625 if have_filedata:
626 626 ipinst = get_ipython()
627 627 if ipinst is not None:
628 628 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
629 629
630 630 return list
631 631
632 632 def get_exception_only(self, etype, value):
633 633 """Only print the exception type and message, without a traceback.
634 634
635 635 Parameters
636 636 ----------
637 637 etype : exception type
638 638 value : exception value
639 639 """
640 640 return ListTB.structured_traceback(self, etype, value, [])
641 641
642 642 def show_exception_only(self, etype, evalue):
643 643 """Only print the exception type and message, without a traceback.
644 644
645 645 Parameters
646 646 ----------
647 647 etype : exception type
648 648 value : exception value
649 649 """
650 650 # This method needs to use __call__ from *this* class, not the one from
651 651 # a subclass whose signature or behavior may be different
652 652 ostream = self.ostream
653 653 ostream.flush()
654 654 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
655 655 ostream.flush()
656 656
657 657 def _some_str(self, value):
658 658 # Lifted from traceback.py
659 659 try:
660 660 return str(value)
661 661 except:
662 662 return '<unprintable %s object>' % type(value).__name__
663 663
664 664
665 665 #----------------------------------------------------------------------------
666 666 class VerboseTB(TBTools):
667 667 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
668 668 of HTML. Requires inspect and pydoc. Crazy, man.
669 669
670 670 Modified version which optionally strips the topmost entries from the
671 671 traceback, to be used with alternate interpreters (because their own code
672 672 would appear in the traceback)."""
673 673
674 674 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
675 675 tb_offset=0, long_header=False, include_vars=True,
676 676 check_cache=None):
677 677 """Specify traceback offset, headers and color scheme.
678 678
679 679 Define how many frames to drop from the tracebacks. Calling it with
680 680 tb_offset=1 allows use of this handler in interpreters which will have
681 681 their own code at the top of the traceback (VerboseTB will first
682 682 remove that frame before printing the traceback info)."""
683 683 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
684 684 ostream=ostream)
685 685 self.tb_offset = tb_offset
686 686 self.long_header = long_header
687 687 self.include_vars = include_vars
688 688 # By default we use linecache.checkcache, but the user can provide a
689 689 # different check_cache implementation. This is used by the IPython
690 690 # kernel to provide tracebacks for interactive code that is cached,
691 691 # by a compiler instance that flushes the linecache but preserves its
692 692 # own code cache.
693 693 if check_cache is None:
694 694 check_cache = linecache.checkcache
695 695 self.check_cache = check_cache
696 696
697 697 def format_records(self, records):
698 698 Colors = self.Colors # just a shorthand + quicker name lookup
699 699 ColorsNormal = Colors.Normal # used a lot
700 700 col_scheme = self.color_scheme_table.active_scheme_name
701 701 indent = ' ' * INDENT_SIZE
702 702 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
703 703 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
704 704 frames = []
705 705 # build some color string templates outside these nested loops
706 706 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
707 707 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
708 708 ColorsNormal)
709 709 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
710 710 (Colors.vName, Colors.valEm, ColorsNormal)
711 711 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
712 712 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
713 713 Colors.vName, ColorsNormal)
714 714 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
715 715
716 716 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
717 717 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm, Colors.line,
718 718 ColorsNormal)
719 719
720 720 abspath = os.path.abspath
721 721 for frame, file, lnum, func, lines, index in records:
722 722 #print '*** record:',file,lnum,func,lines,index # dbg
723 723 if not file:
724 724 file = '?'
725 elif not (file.startswith(str("<")) and file.endswith(str(">"))):
726 # Guess that filenames like <string> aren't real filenames, so
727 # don't call abspath on them.
728 try:
729 file = abspath(file)
730 except OSError:
731 # Not sure if this can still happen: abspath now works with
732 # file names like <string>
733 pass
725 elif file.startswith(str("<")) and file.endswith(str(">")):
726 # Not a real filename, no problem...
727 pass
728 elif not os.path.isabs(file):
729 # Try to make the filename absolute by trying all
730 # sys.path entries (which is also what linecache does)
731 for dirname in sys.path:
732 try:
733 fullname = os.path.join(dirname, file)
734 if os.path.isfile(fullname):
735 file = os.path.abspath(fullname)
736 break
737 except Exception:
738 # Just in case that sys.path contains very
739 # strange entries...
740 pass
741
734 742 file = py3compat.cast_unicode(file, util_path.fs_encoding)
735 743 link = tpl_link % file
736 744 args, varargs, varkw, locals = inspect.getargvalues(frame)
737 745
738 746 if func == '?':
739 747 call = ''
740 748 else:
741 749 # Decide whether to include variable details or not
742 750 var_repr = self.include_vars and eqrepr or nullrepr
743 751 try:
744 752 call = tpl_call % (func, inspect.formatargvalues(args,
745 753 varargs, varkw,
746 754 locals, formatvalue=var_repr))
747 755 except KeyError:
748 756 # This happens in situations like errors inside generator
749 757 # expressions, where local variables are listed in the
750 758 # line, but can't be extracted from the frame. I'm not
751 759 # 100% sure this isn't actually a bug in inspect itself,
752 760 # but since there's no info for us to compute with, the
753 761 # best we can do is report the failure and move on. Here
754 762 # we must *not* call any traceback construction again,
755 763 # because that would mess up use of %debug later on. So we
756 764 # simply report the failure and move on. The only
757 765 # limitation will be that this frame won't have locals
758 766 # listed in the call signature. Quite subtle problem...
759 767 # I can't think of a good way to validate this in a unit
760 768 # test, but running a script consisting of:
761 769 # dict( (k,v.strip()) for (k,v) in range(10) )
762 770 # will illustrate the error, if this exception catch is
763 771 # disabled.
764 772 call = tpl_call_fail % func
765 773
766 774 # Don't attempt to tokenize binary files.
767 775 if file.endswith(('.so', '.pyd', '.dll')):
768 776 frames.append('%s %s\n' % (link, call))
769 777 continue
770 778 elif file.endswith(('.pyc', '.pyo')):
771 779 # Look up the corresponding source file.
772 780 file = openpy.source_from_cache(file)
773 781
774 782 def linereader(file=file, lnum=[lnum], getline=ulinecache.getline):
775 783 line = getline(file, lnum[0])
776 784 lnum[0] += 1
777 785 return line
778 786
779 787 # Build the list of names on this line of code where the exception
780 788 # occurred.
781 789 try:
782 790 names = []
783 791 name_cont = False
784 792
785 793 for token_type, token, start, end, line in generate_tokens(linereader):
786 794 # build composite names
787 795 if token_type == tokenize.NAME and token not in keyword.kwlist:
788 796 if name_cont:
789 797 # Continuation of a dotted name
790 798 try:
791 799 names[-1].append(token)
792 800 except IndexError:
793 801 names.append([token])
794 802 name_cont = False
795 803 else:
796 804 # Regular new names. We append everything, the caller
797 805 # will be responsible for pruning the list later. It's
798 806 # very tricky to try to prune as we go, b/c composite
799 807 # names can fool us. The pruning at the end is easy
800 808 # to do (or the caller can print a list with repeated
801 809 # names if so desired.
802 810 names.append([token])
803 811 elif token == '.':
804 812 name_cont = True
805 813 elif token_type == tokenize.NEWLINE:
806 814 break
807 815
808 816 except (IndexError, UnicodeDecodeError, SyntaxError):
809 817 # signals exit of tokenizer
810 818 # SyntaxError can occur if the file is not actually Python
811 819 # - see gh-6300
812 820 pass
813 821 except tokenize.TokenError as msg:
814 822 _m = ("An unexpected error occurred while tokenizing input\n"
815 823 "The following traceback may be corrupted or invalid\n"
816 824 "The error message is: %s\n" % msg)
817 825 error(_m)
818 826
819 827 # Join composite names (e.g. "dict.fromkeys")
820 828 names = ['.'.join(n) for n in names]
821 829 # prune names list of duplicates, but keep the right order
822 830 unique_names = uniq_stable(names)
823 831
824 832 # Start loop over vars
825 833 lvals = []
826 834 if self.include_vars:
827 835 for name_full in unique_names:
828 836 name_base = name_full.split('.', 1)[0]
829 837 if name_base in frame.f_code.co_varnames:
830 838 if name_base in locals:
831 839 try:
832 840 value = repr(eval(name_full, locals))
833 841 except:
834 842 value = undefined
835 843 else:
836 844 value = undefined
837 845 name = tpl_local_var % name_full
838 846 else:
839 847 if name_base in frame.f_globals:
840 848 try:
841 849 value = repr(eval(name_full, frame.f_globals))
842 850 except:
843 851 value = undefined
844 852 else:
845 853 value = undefined
846 854 name = tpl_global_var % name_full
847 855 lvals.append(tpl_name_val % (name, value))
848 856 if lvals:
849 857 lvals = '%s%s' % (indent, em_normal.join(lvals))
850 858 else:
851 859 lvals = ''
852 860
853 861 level = '%s %s\n' % (link, call)
854 862
855 863 if index is None:
856 864 frames.append(level)
857 865 else:
858 866 frames.append('%s%s' % (level, ''.join(
859 867 _format_traceback_lines(lnum, index, lines, Colors, lvals,
860 868 col_scheme))))
861 869
862 870 return frames
863 871
864 872 def prepare_chained_exception_message(self, cause):
865 873 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
866 874 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
867 875
868 876 if cause:
869 877 message = [[direct_cause]]
870 878 else:
871 879 message = [[exception_during_handling]]
872 880 return message
873 881
874 882 def prepare_header(self, etype, long_version=False):
875 883 colors = self.Colors # just a shorthand + quicker name lookup
876 884 colorsnormal = colors.Normal # used a lot
877 885 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
878 886 if long_version:
879 887 # Header with the exception type, python version, and date
880 888 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
881 889 date = time.ctime(time.time())
882 890
883 891 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * 75, colorsnormal,
884 892 exc, ' ' * (75 - len(str(etype)) - len(pyver)),
885 893 pyver, date.rjust(75) )
886 894 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
887 895 "\ncalls leading up to the error, with the most recent (innermost) call last."
888 896 else:
889 897 # Simplified header
890 898 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
891 899 rjust(75 - len(str(etype))) )
892 900
893 901 return head
894 902
895 903 def format_exception(self, etype, evalue):
896 904 colors = self.Colors # just a shorthand + quicker name lookup
897 905 colorsnormal = colors.Normal # used a lot
898 906 indent = ' ' * INDENT_SIZE
899 907 # Get (safely) a string form of the exception info
900 908 try:
901 909 etype_str, evalue_str = map(str, (etype, evalue))
902 910 except:
903 911 # User exception is improperly defined.
904 912 etype, evalue = str, sys.exc_info()[:2]
905 913 etype_str, evalue_str = map(str, (etype, evalue))
906 914 # ... and format it
907 915 exception = ['%s%s%s: %s' % (colors.excName, etype_str,
908 916 colorsnormal, py3compat.cast_unicode(evalue_str))]
909 917
910 918 if (not py3compat.PY3) and type(evalue) is types.InstanceType:
911 919 try:
912 920 names = [w for w in dir(evalue) if isinstance(w, py3compat.string_types)]
913 921 except:
914 922 # Every now and then, an object with funny internals blows up
915 923 # when dir() is called on it. We do the best we can to report
916 924 # the problem and continue
917 925 _m = '%sException reporting error (object with broken dir())%s:'
918 926 exception.append(_m % (colors.excName, colorsnormal))
919 927 etype_str, evalue_str = map(str, sys.exc_info()[:2])
920 928 exception.append('%s%s%s: %s' % (colors.excName, etype_str,
921 929 colorsnormal, py3compat.cast_unicode(evalue_str)))
922 930 names = []
923 931 for name in names:
924 932 value = text_repr(getattr(evalue, name))
925 933 exception.append('\n%s%s = %s' % (indent, name, value))
926 934
927 935 return exception
928 936
929 937 def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset):
930 938 # some locals
931 939 try:
932 940 etype = etype.__name__
933 941 except AttributeError:
934 942 pass
935 943
936 944 tb_offset = self.tb_offset if tb_offset is None else tb_offset
937 945 head = self.prepare_header(etype, self.long_header)
938 946 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
939 947
940 948 frames = self.format_records(records)
941 949 if records is None:
942 950 return ""
943 951
944 952 formatted_exception = self.format_exception(etype, evalue)
945 953 if records:
946 954 filepath, lnum = records[-1][1:3]
947 955 filepath = os.path.abspath(filepath)
948 956 ipinst = get_ipython()
949 957 if ipinst is not None:
950 958 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
951 959
952 960 return [[head] + frames + [''.join(formatted_exception[0])]]
953 961
954 962 def get_records(self, etb, number_of_lines_of_context, tb_offset):
955 963 try:
956 964 # Try the default getinnerframes and Alex's: Alex's fixes some
957 965 # problems, but it generates empty tracebacks for console errors
958 966 # (5 blanks lines) where none should be returned.
959 967 return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
960 968 except:
961 969 # FIXME: I've been getting many crash reports from python 2.3
962 970 # users, traceable to inspect.py. If I can find a small test-case
963 971 # to reproduce this, I should either write a better workaround or
964 972 # file a bug report against inspect (if that's the real problem).
965 973 # So far, I haven't been able to find an isolated example to
966 974 # reproduce the problem.
967 975 inspect_error()
968 976 traceback.print_exc(file=self.ostream)
969 977 info('\nUnfortunately, your original traceback can not be constructed.\n')
970 978 return None
971 979
972 980 def get_parts_of_chained_exception(self, evalue):
973 981 def get_chained_exception(exception_value):
974 982 cause = getattr(exception_value, '__cause__', None)
975 983 if cause:
976 984 return cause
977 985 return getattr(exception_value, '__context__', None)
978 986
979 987 chained_evalue = get_chained_exception(evalue)
980 988
981 989 if chained_evalue:
982 990 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
983 991
984 992 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
985 993 number_of_lines_of_context=5):
986 994 """Return a nice text document describing the traceback."""
987 995
988 996 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
989 997 tb_offset)
990 998
991 999 colors = self.Colors # just a shorthand + quicker name lookup
992 1000 colorsnormal = colors.Normal # used a lot
993 1001 head = '%s%s%s' % (colors.topline, '-' * 75, colorsnormal)
994 1002 structured_traceback_parts = [head]
995 1003 if py3compat.PY3:
996 1004 chained_exceptions_tb_offset = 0
997 1005 lines_of_context = 3
998 1006 formatted_exceptions = formatted_exception
999 1007 exception = self.get_parts_of_chained_exception(evalue)
1000 1008 if exception:
1001 1009 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1002 1010 etype, evalue, etb = exception
1003 1011 else:
1004 1012 evalue = None
1005 1013 while evalue:
1006 1014 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1007 1015 chained_exceptions_tb_offset)
1008 1016 exception = self.get_parts_of_chained_exception(evalue)
1009 1017
1010 1018 if exception:
1011 1019 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1012 1020 etype, evalue, etb = exception
1013 1021 else:
1014 1022 evalue = None
1015 1023
1016 1024 # we want to see exceptions in a reversed order:
1017 1025 # the first exception should be on top
1018 1026 for formatted_exception in reversed(formatted_exceptions):
1019 1027 structured_traceback_parts += formatted_exception
1020 1028 else:
1021 1029 structured_traceback_parts += formatted_exception[0]
1022 1030
1023 1031 return structured_traceback_parts
1024 1032
1025 1033 def debugger(self, force=False):
1026 1034 """Call up the pdb debugger if desired, always clean up the tb
1027 1035 reference.
1028 1036
1029 1037 Keywords:
1030 1038
1031 1039 - force(False): by default, this routine checks the instance call_pdb
1032 1040 flag and does not actually invoke the debugger if the flag is false.
1033 1041 The 'force' option forces the debugger to activate even if the flag
1034 1042 is false.
1035 1043
1036 1044 If the call_pdb flag is set, the pdb interactive debugger is
1037 1045 invoked. In all cases, the self.tb reference to the current traceback
1038 1046 is deleted to prevent lingering references which hamper memory
1039 1047 management.
1040 1048
1041 1049 Note that each call to pdb() does an 'import readline', so if your app
1042 1050 requires a special setup for the readline completers, you'll have to
1043 1051 fix that by hand after invoking the exception handler."""
1044 1052
1045 1053 if force or self.call_pdb:
1046 1054 if self.pdb is None:
1047 1055 self.pdb = debugger.Pdb(
1048 1056 self.color_scheme_table.active_scheme_name)
1049 1057 # the system displayhook may have changed, restore the original
1050 1058 # for pdb
1051 1059 display_trap = DisplayTrap(hook=sys.__displayhook__)
1052 1060 with display_trap:
1053 1061 self.pdb.reset()
1054 1062 # Find the right frame so we don't pop up inside ipython itself
1055 1063 if hasattr(self, 'tb') and self.tb is not None:
1056 1064 etb = self.tb
1057 1065 else:
1058 1066 etb = self.tb = sys.last_traceback
1059 1067 while self.tb is not None and self.tb.tb_next is not None:
1060 1068 self.tb = self.tb.tb_next
1061 1069 if etb and etb.tb_next:
1062 1070 etb = etb.tb_next
1063 1071 self.pdb.botframe = etb.tb_frame
1064 1072 self.pdb.interaction(self.tb.tb_frame, self.tb)
1065 1073
1066 1074 if hasattr(self, 'tb'):
1067 1075 del self.tb
1068 1076
1069 1077 def handler(self, info=None):
1070 1078 (etype, evalue, etb) = info or sys.exc_info()
1071 1079 self.tb = etb
1072 1080 ostream = self.ostream
1073 1081 ostream.flush()
1074 1082 ostream.write(self.text(etype, evalue, etb))
1075 1083 ostream.write('\n')
1076 1084 ostream.flush()
1077 1085
1078 1086 # Changed so an instance can just be called as VerboseTB_inst() and print
1079 1087 # out the right info on its own.
1080 1088 def __call__(self, etype=None, evalue=None, etb=None):
1081 1089 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1082 1090 if etb is None:
1083 1091 self.handler()
1084 1092 else:
1085 1093 self.handler((etype, evalue, etb))
1086 1094 try:
1087 1095 self.debugger()
1088 1096 except KeyboardInterrupt:
1089 1097 print("\nKeyboardInterrupt")
1090 1098
1091 1099
1092 1100 #----------------------------------------------------------------------------
1093 1101 class FormattedTB(VerboseTB, ListTB):
1094 1102 """Subclass ListTB but allow calling with a traceback.
1095 1103
1096 1104 It can thus be used as a sys.excepthook for Python > 2.1.
1097 1105
1098 1106 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1099 1107
1100 1108 Allows a tb_offset to be specified. This is useful for situations where
1101 1109 one needs to remove a number of topmost frames from the traceback (such as
1102 1110 occurs with python programs that themselves execute other python code,
1103 1111 like Python shells). """
1104 1112
1105 1113 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1106 1114 ostream=None,
1107 1115 tb_offset=0, long_header=False, include_vars=False,
1108 1116 check_cache=None):
1109 1117
1110 1118 # NEVER change the order of this list. Put new modes at the end:
1111 1119 self.valid_modes = ['Plain', 'Context', 'Verbose']
1112 1120 self.verbose_modes = self.valid_modes[1:3]
1113 1121
1114 1122 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1115 1123 ostream=ostream, tb_offset=tb_offset,
1116 1124 long_header=long_header, include_vars=include_vars,
1117 1125 check_cache=check_cache)
1118 1126
1119 1127 # Different types of tracebacks are joined with different separators to
1120 1128 # form a single string. They are taken from this dict
1121 1129 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1122 1130 # set_mode also sets the tb_join_char attribute
1123 1131 self.set_mode(mode)
1124 1132
1125 1133 def _extract_tb(self, tb):
1126 1134 if tb:
1127 1135 return traceback.extract_tb(tb)
1128 1136 else:
1129 1137 return None
1130 1138
1131 1139 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1132 1140 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1133 1141 mode = self.mode
1134 1142 if mode in self.verbose_modes:
1135 1143 # Verbose modes need a full traceback
1136 1144 return VerboseTB.structured_traceback(
1137 1145 self, etype, value, tb, tb_offset, number_of_lines_of_context
1138 1146 )
1139 1147 else:
1140 1148 # We must check the source cache because otherwise we can print
1141 1149 # out-of-date source code.
1142 1150 self.check_cache()
1143 1151 # Now we can extract and format the exception
1144 1152 elist = self._extract_tb(tb)
1145 1153 return ListTB.structured_traceback(
1146 1154 self, etype, value, elist, tb_offset, number_of_lines_of_context
1147 1155 )
1148 1156
1149 1157 def stb2text(self, stb):
1150 1158 """Convert a structured traceback (a list) to a string."""
1151 1159 return self.tb_join_char.join(stb)
1152 1160
1153 1161
1154 1162 def set_mode(self, mode=None):
1155 1163 """Switch to the desired mode.
1156 1164
1157 1165 If mode is not specified, cycles through the available modes."""
1158 1166
1159 1167 if not mode:
1160 1168 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1161 1169 len(self.valid_modes)
1162 1170 self.mode = self.valid_modes[new_idx]
1163 1171 elif mode not in self.valid_modes:
1164 1172 raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n'
1165 1173 'Valid modes: ' + str(self.valid_modes))
1166 1174 else:
1167 1175 self.mode = mode
1168 1176 # include variable details only in 'Verbose' mode
1169 1177 self.include_vars = (self.mode == self.valid_modes[2])
1170 1178 # Set the join character for generating text tracebacks
1171 1179 self.tb_join_char = self._join_chars[self.mode]
1172 1180
1173 1181 # some convenient shortcuts
1174 1182 def plain(self):
1175 1183 self.set_mode(self.valid_modes[0])
1176 1184
1177 1185 def context(self):
1178 1186 self.set_mode(self.valid_modes[1])
1179 1187
1180 1188 def verbose(self):
1181 1189 self.set_mode(self.valid_modes[2])
1182 1190
1183 1191
1184 1192 #----------------------------------------------------------------------------
1185 1193 class AutoFormattedTB(FormattedTB):
1186 1194 """A traceback printer which can be called on the fly.
1187 1195
1188 1196 It will find out about exceptions by itself.
1189 1197
1190 1198 A brief example::
1191 1199
1192 1200 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1193 1201 try:
1194 1202 ...
1195 1203 except:
1196 1204 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1197 1205 """
1198 1206
1199 1207 def __call__(self, etype=None, evalue=None, etb=None,
1200 1208 out=None, tb_offset=None):
1201 1209 """Print out a formatted exception traceback.
1202 1210
1203 1211 Optional arguments:
1204 1212 - out: an open file-like object to direct output to.
1205 1213
1206 1214 - tb_offset: the number of frames to skip over in the stack, on a
1207 1215 per-call basis (this overrides temporarily the instance's tb_offset
1208 1216 given at initialization time. """
1209 1217
1210 1218 if out is None:
1211 1219 out = self.ostream
1212 1220 out.flush()
1213 1221 out.write(self.text(etype, evalue, etb, tb_offset))
1214 1222 out.write('\n')
1215 1223 out.flush()
1216 1224 # FIXME: we should remove the auto pdb behavior from here and leave
1217 1225 # that to the clients.
1218 1226 try:
1219 1227 self.debugger()
1220 1228 except KeyboardInterrupt:
1221 1229 print("\nKeyboardInterrupt")
1222 1230
1223 1231 def structured_traceback(self, etype=None, value=None, tb=None,
1224 1232 tb_offset=None, number_of_lines_of_context=5):
1225 1233 if etype is None:
1226 1234 etype, value, tb = sys.exc_info()
1227 1235 self.tb = tb
1228 1236 return FormattedTB.structured_traceback(
1229 1237 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1230 1238
1231 1239
1232 1240 #---------------------------------------------------------------------------
1233 1241
1234 1242 # A simple class to preserve Nathan's original functionality.
1235 1243 class ColorTB(FormattedTB):
1236 1244 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1237 1245
1238 1246 def __init__(self, color_scheme='Linux', call_pdb=0):
1239 1247 FormattedTB.__init__(self, color_scheme=color_scheme,
1240 1248 call_pdb=call_pdb)
1241 1249
1242 1250
1243 1251 class SyntaxTB(ListTB):
1244 1252 """Extension which holds some state: the last exception value"""
1245 1253
1246 1254 def __init__(self, color_scheme='NoColor'):
1247 1255 ListTB.__init__(self, color_scheme)
1248 1256 self.last_syntax_error = None
1249 1257
1250 1258 def __call__(self, etype, value, elist):
1251 1259 self.last_syntax_error = value
1252 1260
1253 1261 ListTB.__call__(self, etype, value, elist)
1254 1262
1255 1263 def structured_traceback(self, etype, value, elist, tb_offset=None,
1256 1264 context=5):
1257 1265 # If the source file has been edited, the line in the syntax error can
1258 1266 # be wrong (retrieved from an outdated cache). This replaces it with
1259 1267 # the current value.
1260 1268 if isinstance(value, SyntaxError) \
1261 1269 and isinstance(value.filename, py3compat.string_types) \
1262 1270 and isinstance(value.lineno, int):
1263 1271 linecache.checkcache(value.filename)
1264 1272 newtext = ulinecache.getline(value.filename, value.lineno)
1265 1273 if newtext:
1266 1274 value.text = newtext
1267 1275 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1268 1276 tb_offset=tb_offset, context=context)
1269 1277
1270 1278 def clear_err_state(self):
1271 1279 """Return the current error state and clear it"""
1272 1280 e = self.last_syntax_error
1273 1281 self.last_syntax_error = None
1274 1282 return e
1275 1283
1276 1284 def stb2text(self, stb):
1277 1285 """Convert a structured traceback (a list) to a string."""
1278 1286 return ''.join(stb)
1279 1287
1280 1288
1281 1289 # some internal-use functions
1282 1290 def text_repr(value):
1283 1291 """Hopefully pretty robust repr equivalent."""
1284 1292 # this is pretty horrible but should always return *something*
1285 1293 try:
1286 1294 return pydoc.text.repr(value)
1287 1295 except KeyboardInterrupt:
1288 1296 raise
1289 1297 except:
1290 1298 try:
1291 1299 return repr(value)
1292 1300 except KeyboardInterrupt:
1293 1301 raise
1294 1302 except:
1295 1303 try:
1296 1304 # all still in an except block so we catch
1297 1305 # getattr raising
1298 1306 name = getattr(value, '__name__', None)
1299 1307 if name:
1300 1308 # ick, recursion
1301 1309 return text_repr(name)
1302 1310 klass = getattr(value, '__class__', None)
1303 1311 if klass:
1304 1312 return '%s instance' % text_repr(klass)
1305 1313 except KeyboardInterrupt:
1306 1314 raise
1307 1315 except:
1308 1316 return 'UNRECOVERABLE REPR FAILURE'
1309 1317
1310 1318
1311 1319 def eqrepr(value, repr=text_repr):
1312 1320 return '=%s' % repr(value)
1313 1321
1314 1322
1315 1323 def nullrepr(value, repr=text_repr):
1316 1324 return ''
1317 1325
1318 1326
1319 1327 #----------------------------------------------------------------------------
1320 1328
1321 1329 # module testing (minimal)
1322 1330 if __name__ == "__main__":
1323 1331 def spam(c, d_e):
1324 1332 (d, e) = d_e
1325 1333 x = c + d
1326 1334 y = c * d
1327 1335 foo(x, y)
1328 1336
1329 1337 def foo(a, b, bar=1):
1330 1338 eggs(a, b + bar)
1331 1339
1332 1340 def eggs(f, g, z=globals()):
1333 1341 h = f + g
1334 1342 i = f - g
1335 1343 return h / i
1336 1344
1337 1345 print('')
1338 1346 print('*** Before ***')
1339 1347 try:
1340 1348 print(spam(1, (2, 3)))
1341 1349 except:
1342 1350 traceback.print_exc()
1343 1351 print('')
1344 1352
1345 1353 handler = ColorTB()
1346 1354 print('*** ColorTB ***')
1347 1355 try:
1348 1356 print(spam(1, (2, 3)))
1349 1357 except:
1350 1358 handler(*sys.exc_info())
1351 1359 print('')
1352 1360
1353 1361 handler = VerboseTB()
1354 1362 print('*** VerboseTB ***')
1355 1363 try:
1356 1364 print(spam(1, (2, 3)))
1357 1365 except:
1358 1366 handler(*sys.exc_info())
1359 1367 print('')
1360 1368
General Comments 0
You need to be logged in to leave comments. Login now