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