##// END OF EJS Templates
Highlight the executing node with stack_data
Alex Hall -
Show More
@@ -1,1063 +1,1066 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 inspect
93 93 import linecache
94 94 import pydoc
95 95 import sys
96 96 import time
97 97 import traceback
98 98
99 99 import stack_data
100 100 from pygments.formatters.terminal256 import Terminal256Formatter
101 from pygments.styles import get_style_by_name
101 102
102 103 # IPython's own modules
103 104 from IPython import get_ipython
104 105 from IPython.core import debugger
105 106 from IPython.core.display_trap import DisplayTrap
106 107 from IPython.core.excolors import exception_colors
107 108 from IPython.utils import path as util_path
108 109 from IPython.utils import py3compat
109 110 from IPython.utils.terminal import get_terminal_size
110 111
111 112 import IPython.utils.colorable as colorable
112 113
113 114 # Globals
114 115 # amount of space to put line numbers before verbose tracebacks
115 116 INDENT_SIZE = 8
116 117
117 118 # Default color scheme. This is used, for example, by the traceback
118 119 # formatter. When running in an actual IPython instance, the user's rc.colors
119 120 # value is used, but having a module global makes this functionality available
120 121 # to users of ultratb who are NOT running inside ipython.
121 122 DEFAULT_SCHEME = 'NoColor'
122 123
123 124 # ---------------------------------------------------------------------------
124 125 # Code begins
125 126
126 127 # Helper function -- largely belongs to VerboseTB, but we need the same
127 128 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
128 129 # can be recognized properly by ipython.el's py-traceback-line-re
129 130 # (SyntaxErrors have to be treated specially because they have no traceback)
130 131
131 132
132 133 def _format_traceback_lines(lines, Colors, has_colors, lvals):
133 134 """
134 135 Format tracebacks lines with pointing arrow, leading numbers...
135 136
136 137 Parameters
137 138 ==========
138 139
139 140 lines: list[Line]
140 141 Colors:
141 142 ColorScheme used.
142 143 lvals: str
143 144 Values of local variables, already colored, to inject just after the error line.
144 145 """
145 146 numbers_width = INDENT_SIZE - 1
146 147 res = []
147 148
148 149 for stack_line in lines:
149 150 if stack_line is stack_data.LINE_GAP:
150 151 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
151 152 continue
152 153
153 154 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n'
154 155 lineno = stack_line.lineno
155 156 if stack_line.is_current:
156 157 # This is the line with the error
157 158 pad = numbers_width - len(str(lineno))
158 159 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
159 160 start_color = Colors.linenoEm
160 161 else:
161 162 num = '%*s' % (numbers_width, lineno)
162 163 start_color = Colors.lineno
163 164
164 165 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line)
165 166
166 167 res.append(line)
167 168 if lvals and stack_line.is_current:
168 169 res.append(lvals + '\n')
169 170 return res
170 171
171 172
172 173 #---------------------------------------------------------------------------
173 174 # Module classes
174 175 class TBTools(colorable.Colorable):
175 176 """Basic tools used by all traceback printer classes."""
176 177
177 178 # Number of frames to skip when reporting tracebacks
178 179 tb_offset = 0
179 180
180 181 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
181 182 # Whether to call the interactive pdb debugger after printing
182 183 # tracebacks or not
183 184 super(TBTools, self).__init__(parent=parent, config=config)
184 185 self.call_pdb = call_pdb
185 186
186 187 # Output stream to write to. Note that we store the original value in
187 188 # a private attribute and then make the public ostream a property, so
188 189 # that we can delay accessing sys.stdout until runtime. The way
189 190 # things are written now, the sys.stdout object is dynamically managed
190 191 # so a reference to it should NEVER be stored statically. This
191 192 # property approach confines this detail to a single location, and all
192 193 # subclasses can simply access self.ostream for writing.
193 194 self._ostream = ostream
194 195
195 196 # Create color table
196 197 self.color_scheme_table = exception_colors()
197 198
198 199 self.set_colors(color_scheme)
199 200 self.old_scheme = color_scheme # save initial value for toggles
200 201
201 202 if call_pdb:
202 203 self.pdb = debugger.Pdb()
203 204 else:
204 205 self.pdb = None
205 206
206 207 def _get_ostream(self):
207 208 """Output stream that exceptions are written to.
208 209
209 210 Valid values are:
210 211
211 212 - None: the default, which means that IPython will dynamically resolve
212 213 to sys.stdout. This ensures compatibility with most tools, including
213 214 Windows (where plain stdout doesn't recognize ANSI escapes).
214 215
215 216 - Any object with 'write' and 'flush' attributes.
216 217 """
217 218 return sys.stdout if self._ostream is None else self._ostream
218 219
219 220 def _set_ostream(self, val):
220 221 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
221 222 self._ostream = val
222 223
223 224 ostream = property(_get_ostream, _set_ostream)
224 225
225 226 def get_parts_of_chained_exception(self, evalue):
226 227 def get_chained_exception(exception_value):
227 228 cause = getattr(exception_value, '__cause__', None)
228 229 if cause:
229 230 return cause
230 231 if getattr(exception_value, '__suppress_context__', False):
231 232 return None
232 233 return getattr(exception_value, '__context__', None)
233 234
234 235 chained_evalue = get_chained_exception(evalue)
235 236
236 237 if chained_evalue:
237 238 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
238 239
239 240 def prepare_chained_exception_message(self, cause):
240 241 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
241 242 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
242 243
243 244 if cause:
244 245 message = [[direct_cause]]
245 246 else:
246 247 message = [[exception_during_handling]]
247 248 return message
248 249
249 250 @property
250 251 def has_colors(self):
251 252 return self.color_scheme_table.active_scheme_name.lower() != "nocolor"
252 253
253 254 def set_colors(self, *args, **kw):
254 255 """Shorthand access to the color table scheme selector method."""
255 256
256 257 # Set own color table
257 258 self.color_scheme_table.set_active_scheme(*args, **kw)
258 259 # for convenience, set Colors to the active scheme
259 260 self.Colors = self.color_scheme_table.active_colors
260 261 # Also set colors of debugger
261 262 if hasattr(self, 'pdb') and self.pdb is not None:
262 263 self.pdb.set_colors(*args, **kw)
263 264
264 265 def color_toggle(self):
265 266 """Toggle between the currently active color scheme and NoColor."""
266 267
267 268 if self.color_scheme_table.active_scheme_name == 'NoColor':
268 269 self.color_scheme_table.set_active_scheme(self.old_scheme)
269 270 self.Colors = self.color_scheme_table.active_colors
270 271 else:
271 272 self.old_scheme = self.color_scheme_table.active_scheme_name
272 273 self.color_scheme_table.set_active_scheme('NoColor')
273 274 self.Colors = self.color_scheme_table.active_colors
274 275
275 276 def stb2text(self, stb):
276 277 """Convert a structured traceback (a list) to a string."""
277 278 return '\n'.join(stb)
278 279
279 280 def text(self, etype, value, tb, tb_offset=None, context=5):
280 281 """Return formatted traceback.
281 282
282 283 Subclasses may override this if they add extra arguments.
283 284 """
284 285 tb_list = self.structured_traceback(etype, value, tb,
285 286 tb_offset, context)
286 287 return self.stb2text(tb_list)
287 288
288 289 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
289 290 context=5, mode=None):
290 291 """Return a list of traceback frames.
291 292
292 293 Must be implemented by each class.
293 294 """
294 295 raise NotImplementedError()
295 296
296 297
297 298 #---------------------------------------------------------------------------
298 299 class ListTB(TBTools):
299 300 """Print traceback information from a traceback list, with optional color.
300 301
301 302 Calling requires 3 arguments: (etype, evalue, elist)
302 303 as would be obtained by::
303 304
304 305 etype, evalue, tb = sys.exc_info()
305 306 if tb:
306 307 elist = traceback.extract_tb(tb)
307 308 else:
308 309 elist = None
309 310
310 311 It can thus be used by programs which need to process the traceback before
311 312 printing (such as console replacements based on the code module from the
312 313 standard library).
313 314
314 315 Because they are meant to be called without a full traceback (only a
315 316 list), instances of this class can't call the interactive pdb debugger."""
316 317
317 318 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
318 319 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
319 320 ostream=ostream, parent=parent,config=config)
320 321
321 322 def __call__(self, etype, value, elist):
322 323 self.ostream.flush()
323 324 self.ostream.write(self.text(etype, value, elist))
324 325 self.ostream.write('\n')
325 326
326 327 def _extract_tb(self, tb):
327 328 if tb:
328 329 return traceback.extract_tb(tb)
329 330 else:
330 331 return None
331 332
332 333 def structured_traceback(self, etype, evalue, etb=None, tb_offset=None,
333 334 context=5):
334 335 """Return a color formatted string with the traceback info.
335 336
336 337 Parameters
337 338 ----------
338 339 etype : exception type
339 340 Type of the exception raised.
340 341
341 342 evalue : object
342 343 Data stored in the exception
343 344
344 345 etb : object
345 346 If list: List of frames, see class docstring for details.
346 347 If Traceback: Traceback of the exception.
347 348
348 349 tb_offset : int, optional
349 350 Number of frames in the traceback to skip. If not given, the
350 351 instance evalue is used (set in constructor).
351 352
352 353 context : int, optional
353 354 Number of lines of context information to print.
354 355
355 356 Returns
356 357 -------
357 358 String with formatted exception.
358 359 """
359 360 # This is a workaround to get chained_exc_ids in recursive calls
360 361 # etb should not be a tuple if structured_traceback is not recursive
361 362 if isinstance(etb, tuple):
362 363 etb, chained_exc_ids = etb
363 364 else:
364 365 chained_exc_ids = set()
365 366
366 367 if isinstance(etb, list):
367 368 elist = etb
368 369 elif etb is not None:
369 370 elist = self._extract_tb(etb)
370 371 else:
371 372 elist = []
372 373 tb_offset = self.tb_offset if tb_offset is None else tb_offset
373 374 Colors = self.Colors
374 375 out_list = []
375 376 if elist:
376 377
377 378 if tb_offset and len(elist) > tb_offset:
378 379 elist = elist[tb_offset:]
379 380
380 381 out_list.append('Traceback %s(most recent call last)%s:' %
381 382 (Colors.normalEm, Colors.Normal) + '\n')
382 383 out_list.extend(self._format_list(elist))
383 384 # The exception info should be a single entry in the list.
384 385 lines = ''.join(self._format_exception_only(etype, evalue))
385 386 out_list.append(lines)
386 387
387 388 exception = self.get_parts_of_chained_exception(evalue)
388 389
389 390 if exception and not id(exception[1]) in chained_exc_ids:
390 391 chained_exception_message = self.prepare_chained_exception_message(
391 392 evalue.__cause__)[0]
392 393 etype, evalue, etb = exception
393 394 # Trace exception to avoid infinite 'cause' loop
394 395 chained_exc_ids.add(id(exception[1]))
395 396 chained_exceptions_tb_offset = 0
396 397 out_list = (
397 398 self.structured_traceback(
398 399 etype, evalue, (etb, chained_exc_ids),
399 400 chained_exceptions_tb_offset, context)
400 401 + chained_exception_message
401 402 + out_list)
402 403
403 404 return out_list
404 405
405 406 def _format_list(self, extracted_list):
406 407 """Format a list of traceback entry tuples for printing.
407 408
408 409 Given a list of tuples as returned by extract_tb() or
409 410 extract_stack(), return a list of strings ready for printing.
410 411 Each string in the resulting list corresponds to the item with the
411 412 same index in the argument list. Each string ends in a newline;
412 413 the strings may contain internal newlines as well, for those items
413 414 whose source text line is not None.
414 415
415 416 Lifted almost verbatim from traceback.py
416 417 """
417 418
418 419 Colors = self.Colors
419 420 list = []
420 421 for filename, lineno, name, line in extracted_list[:-1]:
421 422 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
422 423 (Colors.filename, filename, Colors.Normal,
423 424 Colors.lineno, lineno, Colors.Normal,
424 425 Colors.name, name, Colors.Normal)
425 426 if line:
426 427 item += ' %s\n' % line.strip()
427 428 list.append(item)
428 429 # Emphasize the last entry
429 430 filename, lineno, name, line = extracted_list[-1]
430 431 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
431 432 (Colors.normalEm,
432 433 Colors.filenameEm, filename, Colors.normalEm,
433 434 Colors.linenoEm, lineno, Colors.normalEm,
434 435 Colors.nameEm, name, Colors.normalEm,
435 436 Colors.Normal)
436 437 if line:
437 438 item += '%s %s%s\n' % (Colors.line, line.strip(),
438 439 Colors.Normal)
439 440 list.append(item)
440 441 return list
441 442
442 443 def _format_exception_only(self, etype, value):
443 444 """Format the exception part of a traceback.
444 445
445 446 The arguments are the exception type and value such as given by
446 447 sys.exc_info()[:2]. The return value is a list of strings, each ending
447 448 in a newline. Normally, the list contains a single string; however,
448 449 for SyntaxError exceptions, it contains several lines that (when
449 450 printed) display detailed information about where the syntax error
450 451 occurred. The message indicating which exception occurred is the
451 452 always last string in the list.
452 453
453 454 Also lifted nearly verbatim from traceback.py
454 455 """
455 456 have_filedata = False
456 457 Colors = self.Colors
457 458 list = []
458 459 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
459 460 if value is None:
460 461 # Not sure if this can still happen in Python 2.6 and above
461 462 list.append(stype + '\n')
462 463 else:
463 464 if issubclass(etype, SyntaxError):
464 465 have_filedata = True
465 466 if not value.filename: value.filename = "<string>"
466 467 if value.lineno:
467 468 lineno = value.lineno
468 469 textline = linecache.getline(value.filename, value.lineno)
469 470 else:
470 471 lineno = 'unknown'
471 472 textline = ''
472 473 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
473 474 (Colors.normalEm,
474 475 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
475 476 Colors.linenoEm, lineno, Colors.Normal ))
476 477 if textline == '':
477 478 textline = py3compat.cast_unicode(value.text, "utf-8")
478 479
479 480 if textline is not None:
480 481 i = 0
481 482 while i < len(textline) and textline[i].isspace():
482 483 i += 1
483 484 list.append('%s %s%s\n' % (Colors.line,
484 485 textline.strip(),
485 486 Colors.Normal))
486 487 if value.offset is not None:
487 488 s = ' '
488 489 for c in textline[i:value.offset - 1]:
489 490 if c.isspace():
490 491 s += c
491 492 else:
492 493 s += ' '
493 494 list.append('%s%s^%s\n' % (Colors.caret, s,
494 495 Colors.Normal))
495 496
496 497 try:
497 498 s = value.msg
498 499 except Exception:
499 500 s = self._some_str(value)
500 501 if s:
501 502 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
502 503 Colors.Normal, s))
503 504 else:
504 505 list.append('%s\n' % stype)
505 506
506 507 # sync with user hooks
507 508 if have_filedata:
508 509 ipinst = get_ipython()
509 510 if ipinst is not None:
510 511 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
511 512
512 513 return list
513 514
514 515 def get_exception_only(self, etype, value):
515 516 """Only print the exception type and message, without a traceback.
516 517
517 518 Parameters
518 519 ----------
519 520 etype : exception type
520 521 value : exception value
521 522 """
522 523 return ListTB.structured_traceback(self, etype, value)
523 524
524 525 def show_exception_only(self, etype, evalue):
525 526 """Only print the exception type and message, without a traceback.
526 527
527 528 Parameters
528 529 ----------
529 530 etype : exception type
530 531 value : exception value
531 532 """
532 533 # This method needs to use __call__ from *this* class, not the one from
533 534 # a subclass whose signature or behavior may be different
534 535 ostream = self.ostream
535 536 ostream.flush()
536 537 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
537 538 ostream.flush()
538 539
539 540 def _some_str(self, value):
540 541 # Lifted from traceback.py
541 542 try:
542 543 return py3compat.cast_unicode(str(value))
543 544 except:
544 545 return u'<unprintable %s object>' % type(value).__name__
545 546
546 547
547 548 #----------------------------------------------------------------------------
548 549 class VerboseTB(TBTools):
549 550 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
550 551 of HTML. Requires inspect and pydoc. Crazy, man.
551 552
552 553 Modified version which optionally strips the topmost entries from the
553 554 traceback, to be used with alternate interpreters (because their own code
554 555 would appear in the traceback)."""
555 556
556 557 def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None,
557 558 tb_offset=0, long_header=False, include_vars=True,
558 559 check_cache=None, debugger_cls = None,
559 560 parent=None, config=None):
560 561 """Specify traceback offset, headers and color scheme.
561 562
562 563 Define how many frames to drop from the tracebacks. Calling it with
563 564 tb_offset=1 allows use of this handler in interpreters which will have
564 565 their own code at the top of the traceback (VerboseTB will first
565 566 remove that frame before printing the traceback info)."""
566 567 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
567 568 ostream=ostream, parent=parent, config=config)
568 569 self.tb_offset = tb_offset
569 570 self.long_header = long_header
570 571 self.include_vars = include_vars
571 572 # By default we use linecache.checkcache, but the user can provide a
572 573 # different check_cache implementation. This is used by the IPython
573 574 # kernel to provide tracebacks for interactive code that is cached,
574 575 # by a compiler instance that flushes the linecache but preserves its
575 576 # own code cache.
576 577 if check_cache is None:
577 578 check_cache = linecache.checkcache
578 579 self.check_cache = check_cache
579 580
580 581 self.debugger_cls = debugger_cls or debugger.Pdb
581 582
582 583 def format_record(self, frame_info):
583 584 """Format a single stack frame"""
584 585 Colors = self.Colors # just a shorthand + quicker name lookup
585 586 ColorsNormal = Colors.Normal # used a lot
586 587
587 588 if isinstance(frame_info, stack_data.RepeatedFrames):
588 589 return ' %s[... skipping similar frames: %s]%s\n' % (
589 590 Colors.excName, frame_info.description, ColorsNormal)
590 591
591 592 indent = ' ' * INDENT_SIZE
592 593 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
593 594 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
594 595 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
595 596 ColorsNormal)
596 597 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
597 598 (Colors.vName, Colors.valEm, ColorsNormal)
598 599 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
599 600 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
600 601
601 602 file = frame_info.filename
602 603 file = py3compat.cast_unicode(file, util_path.fs_encoding)
603 604 link = tpl_link % util_path.compress_user(file)
604 605 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
605 606
606 607 func = frame_info.executing.code_qualname()
607 608 if func == '<module>':
608 609 call = tpl_call % (func, '')
609 610 else:
610 611 # Decide whether to include variable details or not
611 612 var_repr = eqrepr if self.include_vars else nullrepr
612 613 try:
613 614 call = tpl_call % (func, inspect.formatargvalues(args,
614 615 varargs, varkw,
615 616 locals_, formatvalue=var_repr))
616 617 except KeyError:
617 618 # This happens in situations like errors inside generator
618 619 # expressions, where local variables are listed in the
619 620 # line, but can't be extracted from the frame. I'm not
620 621 # 100% sure this isn't actually a bug in inspect itself,
621 622 # but since there's no info for us to compute with, the
622 623 # best we can do is report the failure and move on. Here
623 624 # we must *not* call any traceback construction again,
624 625 # because that would mess up use of %debug later on. So we
625 626 # simply report the failure and move on. The only
626 627 # limitation will be that this frame won't have locals
627 628 # listed in the call signature. Quite subtle problem...
628 629 # I can't think of a good way to validate this in a unit
629 630 # test, but running a script consisting of:
630 631 # dict( (k,v.strip()) for (k,v) in range(10) )
631 632 # will illustrate the error, if this exception catch is
632 633 # disabled.
633 634 call = tpl_call_fail % func
634 635
635 636 lvals = ''
636 637 lvals_list = []
637 638 if self.include_vars:
638 639 for var in frame_info.variables_in_executing_piece:
639 640 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
640 641 if lvals_list:
641 642 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
642 643
643 644 result = '%s %s\n' % (link, call)
644 645
645 646 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals))
646 647 return result
647 648
648 649 def prepare_header(self, etype, long_version=False):
649 650 colors = self.Colors # just a shorthand + quicker name lookup
650 651 colorsnormal = colors.Normal # used a lot
651 652 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
652 653 width = min(75, get_terminal_size()[0])
653 654 if long_version:
654 655 # Header with the exception type, python version, and date
655 656 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
656 657 date = time.ctime(time.time())
657 658
658 659 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal,
659 660 exc, ' ' * (width - len(str(etype)) - len(pyver)),
660 661 pyver, date.rjust(width) )
661 662 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
662 663 "\ncalls leading up to the error, with the most recent (innermost) call last."
663 664 else:
664 665 # Simplified header
665 666 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
666 667 rjust(width - len(str(etype))) )
667 668
668 669 return head
669 670
670 671 def format_exception(self, etype, evalue):
671 672 colors = self.Colors # just a shorthand + quicker name lookup
672 673 colorsnormal = colors.Normal # used a lot
673 674 # Get (safely) a string form of the exception info
674 675 try:
675 676 etype_str, evalue_str = map(str, (etype, evalue))
676 677 except:
677 678 # User exception is improperly defined.
678 679 etype, evalue = str, sys.exc_info()[:2]
679 680 etype_str, evalue_str = map(str, (etype, evalue))
680 681 # ... and format it
681 682 return ['%s%s%s: %s' % (colors.excName, etype_str,
682 683 colorsnormal, py3compat.cast_unicode(evalue_str))]
683 684
684 685 def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset):
685 686 """Formats the header, traceback and exception message for a single exception.
686 687
687 688 This may be called multiple times by Python 3 exception chaining
688 689 (PEP 3134).
689 690 """
690 691 # some locals
691 692 orig_etype = etype
692 693 try:
693 694 etype = etype.__name__
694 695 except AttributeError:
695 696 pass
696 697
697 698 tb_offset = self.tb_offset if tb_offset is None else tb_offset
698 699 head = self.prepare_header(etype, self.long_header)
699 700 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
700 701
701 702 frames = list(map(self.format_record, records))
702 703
703 704 formatted_exception = self.format_exception(etype, evalue)
704 705 if records:
705 706 frame_info = records[-1]
706 707 ipinst = get_ipython()
707 708 if ipinst is not None:
708 709 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
709 710
710 711 return [[head] + frames + [''.join(formatted_exception[0])]]
711 712
712 713 def get_records(self, etb, number_of_lines_of_context, tb_offset):
713 714 context = number_of_lines_of_context - 1
714 715 after = context // 2
715 716 before = context - after
716 717 if self.has_colors:
717 formatter = Terminal256Formatter()
718 style = get_style_by_name('default')
719 style = stack_data.style_with_executing_node(style, 'bg:#00005f')
720 formatter = Terminal256Formatter(style=style)
718 721 else:
719 722 formatter = None
720 723 options = stack_data.Options(
721 724 before=before,
722 725 after=after,
723 726 pygments_formatter=formatter,
724 727 )
725 728 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
726 729
727 730 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
728 731 number_of_lines_of_context=5):
729 732 """Return a nice text document describing the traceback."""
730 733
731 734 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
732 735 tb_offset)
733 736
734 737 colors = self.Colors # just a shorthand + quicker name lookup
735 738 colorsnormal = colors.Normal # used a lot
736 739 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
737 740 structured_traceback_parts = [head]
738 741 chained_exceptions_tb_offset = 0
739 742 lines_of_context = 3
740 743 formatted_exceptions = formatted_exception
741 744 exception = self.get_parts_of_chained_exception(evalue)
742 745 if exception:
743 746 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
744 747 etype, evalue, etb = exception
745 748 else:
746 749 evalue = None
747 750 chained_exc_ids = set()
748 751 while evalue:
749 752 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
750 753 chained_exceptions_tb_offset)
751 754 exception = self.get_parts_of_chained_exception(evalue)
752 755
753 756 if exception and not id(exception[1]) in chained_exc_ids:
754 757 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
755 758 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
756 759 etype, evalue, etb = exception
757 760 else:
758 761 evalue = None
759 762
760 763 # we want to see exceptions in a reversed order:
761 764 # the first exception should be on top
762 765 for formatted_exception in reversed(formatted_exceptions):
763 766 structured_traceback_parts += formatted_exception
764 767
765 768 return structured_traceback_parts
766 769
767 770 def debugger(self, force=False):
768 771 """Call up the pdb debugger if desired, always clean up the tb
769 772 reference.
770 773
771 774 Keywords:
772 775
773 776 - force(False): by default, this routine checks the instance call_pdb
774 777 flag and does not actually invoke the debugger if the flag is false.
775 778 The 'force' option forces the debugger to activate even if the flag
776 779 is false.
777 780
778 781 If the call_pdb flag is set, the pdb interactive debugger is
779 782 invoked. In all cases, the self.tb reference to the current traceback
780 783 is deleted to prevent lingering references which hamper memory
781 784 management.
782 785
783 786 Note that each call to pdb() does an 'import readline', so if your app
784 787 requires a special setup for the readline completers, you'll have to
785 788 fix that by hand after invoking the exception handler."""
786 789
787 790 if force or self.call_pdb:
788 791 if self.pdb is None:
789 792 self.pdb = self.debugger_cls()
790 793 # the system displayhook may have changed, restore the original
791 794 # for pdb
792 795 display_trap = DisplayTrap(hook=sys.__displayhook__)
793 796 with display_trap:
794 797 self.pdb.reset()
795 798 # Find the right frame so we don't pop up inside ipython itself
796 799 if hasattr(self, 'tb') and self.tb is not None:
797 800 etb = self.tb
798 801 else:
799 802 etb = self.tb = sys.last_traceback
800 803 while self.tb is not None and self.tb.tb_next is not None:
801 804 self.tb = self.tb.tb_next
802 805 if etb and etb.tb_next:
803 806 etb = etb.tb_next
804 807 self.pdb.botframe = etb.tb_frame
805 808 self.pdb.interaction(None, etb)
806 809
807 810 if hasattr(self, 'tb'):
808 811 del self.tb
809 812
810 813 def handler(self, info=None):
811 814 (etype, evalue, etb) = info or sys.exc_info()
812 815 self.tb = etb
813 816 ostream = self.ostream
814 817 ostream.flush()
815 818 ostream.write(self.text(etype, evalue, etb))
816 819 ostream.write('\n')
817 820 ostream.flush()
818 821
819 822 # Changed so an instance can just be called as VerboseTB_inst() and print
820 823 # out the right info on its own.
821 824 def __call__(self, etype=None, evalue=None, etb=None):
822 825 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
823 826 if etb is None:
824 827 self.handler()
825 828 else:
826 829 self.handler((etype, evalue, etb))
827 830 try:
828 831 self.debugger()
829 832 except KeyboardInterrupt:
830 833 print("\nKeyboardInterrupt")
831 834
832 835
833 836 #----------------------------------------------------------------------------
834 837 class FormattedTB(VerboseTB, ListTB):
835 838 """Subclass ListTB but allow calling with a traceback.
836 839
837 840 It can thus be used as a sys.excepthook for Python > 2.1.
838 841
839 842 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
840 843
841 844 Allows a tb_offset to be specified. This is useful for situations where
842 845 one needs to remove a number of topmost frames from the traceback (such as
843 846 occurs with python programs that themselves execute other python code,
844 847 like Python shells). """
845 848
846 849 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
847 850 ostream=None,
848 851 tb_offset=0, long_header=False, include_vars=False,
849 852 check_cache=None, debugger_cls=None,
850 853 parent=None, config=None):
851 854
852 855 # NEVER change the order of this list. Put new modes at the end:
853 856 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
854 857 self.verbose_modes = self.valid_modes[1:3]
855 858
856 859 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
857 860 ostream=ostream, tb_offset=tb_offset,
858 861 long_header=long_header, include_vars=include_vars,
859 862 check_cache=check_cache, debugger_cls=debugger_cls,
860 863 parent=parent, config=config)
861 864
862 865 # Different types of tracebacks are joined with different separators to
863 866 # form a single string. They are taken from this dict
864 867 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
865 868 Minimal='')
866 869 # set_mode also sets the tb_join_char attribute
867 870 self.set_mode(mode)
868 871
869 872 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
870 873 tb_offset = self.tb_offset if tb_offset is None else tb_offset
871 874 mode = self.mode
872 875 if mode in self.verbose_modes:
873 876 # Verbose modes need a full traceback
874 877 return VerboseTB.structured_traceback(
875 878 self, etype, value, tb, tb_offset, number_of_lines_of_context
876 879 )
877 880 elif mode == 'Minimal':
878 881 return ListTB.get_exception_only(self, etype, value)
879 882 else:
880 883 # We must check the source cache because otherwise we can print
881 884 # out-of-date source code.
882 885 self.check_cache()
883 886 # Now we can extract and format the exception
884 887 return ListTB.structured_traceback(
885 888 self, etype, value, tb, tb_offset, number_of_lines_of_context
886 889 )
887 890
888 891 def stb2text(self, stb):
889 892 """Convert a structured traceback (a list) to a string."""
890 893 return self.tb_join_char.join(stb)
891 894
892 895
893 896 def set_mode(self, mode=None):
894 897 """Switch to the desired mode.
895 898
896 899 If mode is not specified, cycles through the available modes."""
897 900
898 901 if not mode:
899 902 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
900 903 len(self.valid_modes)
901 904 self.mode = self.valid_modes[new_idx]
902 905 elif mode not in self.valid_modes:
903 906 raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n'
904 907 'Valid modes: ' + str(self.valid_modes))
905 908 else:
906 909 self.mode = mode
907 910 # include variable details only in 'Verbose' mode
908 911 self.include_vars = (self.mode == self.valid_modes[2])
909 912 # Set the join character for generating text tracebacks
910 913 self.tb_join_char = self._join_chars[self.mode]
911 914
912 915 # some convenient shortcuts
913 916 def plain(self):
914 917 self.set_mode(self.valid_modes[0])
915 918
916 919 def context(self):
917 920 self.set_mode(self.valid_modes[1])
918 921
919 922 def verbose(self):
920 923 self.set_mode(self.valid_modes[2])
921 924
922 925 def minimal(self):
923 926 self.set_mode(self.valid_modes[3])
924 927
925 928
926 929 #----------------------------------------------------------------------------
927 930 class AutoFormattedTB(FormattedTB):
928 931 """A traceback printer which can be called on the fly.
929 932
930 933 It will find out about exceptions by itself.
931 934
932 935 A brief example::
933 936
934 937 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
935 938 try:
936 939 ...
937 940 except:
938 941 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
939 942 """
940 943
941 944 def __call__(self, etype=None, evalue=None, etb=None,
942 945 out=None, tb_offset=None):
943 946 """Print out a formatted exception traceback.
944 947
945 948 Optional arguments:
946 949 - out: an open file-like object to direct output to.
947 950
948 951 - tb_offset: the number of frames to skip over in the stack, on a
949 952 per-call basis (this overrides temporarily the instance's tb_offset
950 953 given at initialization time. """
951 954
952 955 if out is None:
953 956 out = self.ostream
954 957 out.flush()
955 958 out.write(self.text(etype, evalue, etb, tb_offset))
956 959 out.write('\n')
957 960 out.flush()
958 961 # FIXME: we should remove the auto pdb behavior from here and leave
959 962 # that to the clients.
960 963 try:
961 964 self.debugger()
962 965 except KeyboardInterrupt:
963 966 print("\nKeyboardInterrupt")
964 967
965 968 def structured_traceback(self, etype=None, value=None, tb=None,
966 969 tb_offset=None, number_of_lines_of_context=5):
967 970 if etype is None:
968 971 etype, value, tb = sys.exc_info()
969 972 if isinstance(tb, tuple):
970 973 # tb is a tuple if this is a chained exception.
971 974 self.tb = tb[0]
972 975 else:
973 976 self.tb = tb
974 977 return FormattedTB.structured_traceback(
975 978 self, etype, value, tb, tb_offset, number_of_lines_of_context)
976 979
977 980
978 981 #---------------------------------------------------------------------------
979 982
980 983 # A simple class to preserve Nathan's original functionality.
981 984 class ColorTB(FormattedTB):
982 985 """Shorthand to initialize a FormattedTB in Linux colors mode."""
983 986
984 987 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
985 988 FormattedTB.__init__(self, color_scheme=color_scheme,
986 989 call_pdb=call_pdb, **kwargs)
987 990
988 991
989 992 class SyntaxTB(ListTB):
990 993 """Extension which holds some state: the last exception value"""
991 994
992 995 def __init__(self, color_scheme='NoColor', parent=None, config=None):
993 996 ListTB.__init__(self, color_scheme, parent=parent, config=config)
994 997 self.last_syntax_error = None
995 998
996 999 def __call__(self, etype, value, elist):
997 1000 self.last_syntax_error = value
998 1001
999 1002 ListTB.__call__(self, etype, value, elist)
1000 1003
1001 1004 def structured_traceback(self, etype, value, elist, tb_offset=None,
1002 1005 context=5):
1003 1006 # If the source file has been edited, the line in the syntax error can
1004 1007 # be wrong (retrieved from an outdated cache). This replaces it with
1005 1008 # the current value.
1006 1009 if isinstance(value, SyntaxError) \
1007 1010 and isinstance(value.filename, str) \
1008 1011 and isinstance(value.lineno, int):
1009 1012 linecache.checkcache(value.filename)
1010 1013 newtext = linecache.getline(value.filename, value.lineno)
1011 1014 if newtext:
1012 1015 value.text = newtext
1013 1016 self.last_syntax_error = value
1014 1017 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1015 1018 tb_offset=tb_offset, context=context)
1016 1019
1017 1020 def clear_err_state(self):
1018 1021 """Return the current error state and clear it"""
1019 1022 e = self.last_syntax_error
1020 1023 self.last_syntax_error = None
1021 1024 return e
1022 1025
1023 1026 def stb2text(self, stb):
1024 1027 """Convert a structured traceback (a list) to a string."""
1025 1028 return ''.join(stb)
1026 1029
1027 1030
1028 1031 # some internal-use functions
1029 1032 def text_repr(value):
1030 1033 """Hopefully pretty robust repr equivalent."""
1031 1034 # this is pretty horrible but should always return *something*
1032 1035 try:
1033 1036 return pydoc.text.repr(value)
1034 1037 except KeyboardInterrupt:
1035 1038 raise
1036 1039 except:
1037 1040 try:
1038 1041 return repr(value)
1039 1042 except KeyboardInterrupt:
1040 1043 raise
1041 1044 except:
1042 1045 try:
1043 1046 # all still in an except block so we catch
1044 1047 # getattr raising
1045 1048 name = getattr(value, '__name__', None)
1046 1049 if name:
1047 1050 # ick, recursion
1048 1051 return text_repr(name)
1049 1052 klass = getattr(value, '__class__', None)
1050 1053 if klass:
1051 1054 return '%s instance' % text_repr(klass)
1052 1055 except KeyboardInterrupt:
1053 1056 raise
1054 1057 except:
1055 1058 return 'UNRECOVERABLE REPR FAILURE'
1056 1059
1057 1060
1058 1061 def eqrepr(value, repr=text_repr):
1059 1062 return '=%s' % repr(value)
1060 1063
1061 1064
1062 1065 def nullrepr(value, repr=text_repr):
1063 1066 return ''
General Comments 0
You need to be logged in to leave comments. Login now