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