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