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