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