##// END OF EJS Templates
Handle OSError cases where traceback frames occur from built files (#13964)...
Matthias Bussonnier -
r28196:92027083 merge
parent child Browse files
Show More
@@ -1,1395 +1,1399 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,
395 395 etype: type,
396 396 evalue: Optional[BaseException],
397 397 etb: Optional[TracebackType] = None,
398 398 tb_offset: Optional[int] = None,
399 399 context=5,
400 400 ):
401 401 """Return a list of traceback frames.
402 402
403 403 Must be implemented by each class.
404 404 """
405 405 raise NotImplementedError()
406 406
407 407
408 408 #---------------------------------------------------------------------------
409 409 class ListTB(TBTools):
410 410 """Print traceback information from a traceback list, with optional color.
411 411
412 412 Calling requires 3 arguments: (etype, evalue, elist)
413 413 as would be obtained by::
414 414
415 415 etype, evalue, tb = sys.exc_info()
416 416 if tb:
417 417 elist = traceback.extract_tb(tb)
418 418 else:
419 419 elist = None
420 420
421 421 It can thus be used by programs which need to process the traceback before
422 422 printing (such as console replacements based on the code module from the
423 423 standard library).
424 424
425 425 Because they are meant to be called without a full traceback (only a
426 426 list), instances of this class can't call the interactive pdb debugger."""
427 427
428 428
429 429 def __call__(self, etype, value, elist):
430 430 self.ostream.flush()
431 431 self.ostream.write(self.text(etype, value, elist))
432 432 self.ostream.write('\n')
433 433
434 434 def _extract_tb(self, tb):
435 435 if tb:
436 436 return traceback.extract_tb(tb)
437 437 else:
438 438 return None
439 439
440 440 def structured_traceback(
441 441 self,
442 442 etype: type,
443 443 evalue: Optional[BaseException],
444 444 etb: Optional[TracebackType] = None,
445 445 tb_offset: Optional[int] = None,
446 446 context=5,
447 447 ):
448 448 """Return a color formatted string with the traceback info.
449 449
450 450 Parameters
451 451 ----------
452 452 etype : exception type
453 453 Type of the exception raised.
454 454 evalue : object
455 455 Data stored in the exception
456 456 etb : list | TracebackType | None
457 457 If list: List of frames, see class docstring for details.
458 458 If Traceback: Traceback of the exception.
459 459 tb_offset : int, optional
460 460 Number of frames in the traceback to skip. If not given, the
461 461 instance evalue is used (set in constructor).
462 462 context : int, optional
463 463 Number of lines of context information to print.
464 464
465 465 Returns
466 466 -------
467 467 String with formatted exception.
468 468 """
469 469 # This is a workaround to get chained_exc_ids in recursive calls
470 470 # etb should not be a tuple if structured_traceback is not recursive
471 471 if isinstance(etb, tuple):
472 472 etb, chained_exc_ids = etb
473 473 else:
474 474 chained_exc_ids = set()
475 475
476 476 if isinstance(etb, list):
477 477 elist = etb
478 478 elif etb is not None:
479 479 elist = self._extract_tb(etb)
480 480 else:
481 481 elist = []
482 482 tb_offset = self.tb_offset if tb_offset is None else tb_offset
483 483 assert isinstance(tb_offset, int)
484 484 Colors = self.Colors
485 485 out_list = []
486 486 if elist:
487 487
488 488 if tb_offset and len(elist) > tb_offset:
489 489 elist = elist[tb_offset:]
490 490
491 491 out_list.append('Traceback %s(most recent call last)%s:' %
492 492 (Colors.normalEm, Colors.Normal) + '\n')
493 493 out_list.extend(self._format_list(elist))
494 494 # The exception info should be a single entry in the list.
495 495 lines = ''.join(self._format_exception_only(etype, evalue))
496 496 out_list.append(lines)
497 497
498 498 exception = self.get_parts_of_chained_exception(evalue)
499 499
500 500 if exception and not id(exception[1]) in chained_exc_ids:
501 501 chained_exception_message = (
502 502 self.prepare_chained_exception_message(evalue.__cause__)[0]
503 503 if evalue is not None
504 504 else ""
505 505 )
506 506 etype, evalue, etb = exception
507 507 # Trace exception to avoid infinite 'cause' loop
508 508 chained_exc_ids.add(id(exception[1]))
509 509 chained_exceptions_tb_offset = 0
510 510 out_list = (
511 511 self.structured_traceback(
512 512 etype, evalue, (etb, chained_exc_ids),
513 513 chained_exceptions_tb_offset, context)
514 514 + chained_exception_message
515 515 + out_list)
516 516
517 517 return out_list
518 518
519 519 def _format_list(self, extracted_list):
520 520 """Format a list of traceback entry tuples for printing.
521 521
522 522 Given a list of tuples as returned by extract_tb() or
523 523 extract_stack(), return a list of strings ready for printing.
524 524 Each string in the resulting list corresponds to the item with the
525 525 same index in the argument list. Each string ends in a newline;
526 526 the strings may contain internal newlines as well, for those items
527 527 whose source text line is not None.
528 528
529 529 Lifted almost verbatim from traceback.py
530 530 """
531 531
532 532 Colors = self.Colors
533 533 list = []
534 534 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
535 535 normalCol, nameCol, fileCol, lineCol = (
536 536 # Emphasize the last entry
537 537 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
538 538 if ind == len(extracted_list) - 1
539 539 else (Colors.Normal, Colors.name, Colors.filename, "")
540 540 )
541 541
542 542 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
543 543 item = f"{normalCol} {fns}"
544 544
545 545 if name != "<module>":
546 546 item += f" in {nameCol}{name}{normalCol}\n"
547 547 else:
548 548 item += "\n"
549 549 if line:
550 550 item += f"{lineCol} {line.strip()}{normalCol}\n"
551 551 list.append(item)
552 552
553 553 return list
554 554
555 555 def _format_exception_only(self, etype, value):
556 556 """Format the exception part of a traceback.
557 557
558 558 The arguments are the exception type and value such as given by
559 559 sys.exc_info()[:2]. The return value is a list of strings, each ending
560 560 in a newline. Normally, the list contains a single string; however,
561 561 for SyntaxError exceptions, it contains several lines that (when
562 562 printed) display detailed information about where the syntax error
563 563 occurred. The message indicating which exception occurred is the
564 564 always last string in the list.
565 565
566 566 Also lifted nearly verbatim from traceback.py
567 567 """
568 568 have_filedata = False
569 569 Colors = self.Colors
570 570 list = []
571 571 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
572 572 if value is None:
573 573 # Not sure if this can still happen in Python 2.6 and above
574 574 list.append(stype + '\n')
575 575 else:
576 576 if issubclass(etype, SyntaxError):
577 577 have_filedata = True
578 578 if not value.filename: value.filename = "<string>"
579 579 if value.lineno:
580 580 lineno = value.lineno
581 581 textline = linecache.getline(value.filename, value.lineno)
582 582 else:
583 583 lineno = "unknown"
584 584 textline = ""
585 585 list.append(
586 586 "%s %s%s\n"
587 587 % (
588 588 Colors.normalEm,
589 589 _format_filename(
590 590 value.filename,
591 591 Colors.filenameEm,
592 592 Colors.normalEm,
593 593 lineno=(None if lineno == "unknown" else lineno),
594 594 ),
595 595 Colors.Normal,
596 596 )
597 597 )
598 598 if textline == "":
599 599 textline = py3compat.cast_unicode(value.text, "utf-8")
600 600
601 601 if textline is not None:
602 602 i = 0
603 603 while i < len(textline) and textline[i].isspace():
604 604 i += 1
605 605 list.append('%s %s%s\n' % (Colors.line,
606 606 textline.strip(),
607 607 Colors.Normal))
608 608 if value.offset is not None:
609 609 s = ' '
610 610 for c in textline[i:value.offset - 1]:
611 611 if c.isspace():
612 612 s += c
613 613 else:
614 614 s += ' '
615 615 list.append('%s%s^%s\n' % (Colors.caret, s,
616 616 Colors.Normal))
617 617
618 618 try:
619 619 s = value.msg
620 620 except Exception:
621 621 s = self._some_str(value)
622 622 if s:
623 623 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
624 624 Colors.Normal, s))
625 625 else:
626 626 list.append('%s\n' % stype)
627 627
628 628 # sync with user hooks
629 629 if have_filedata:
630 630 ipinst = get_ipython()
631 631 if ipinst is not None:
632 632 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
633 633
634 634 return list
635 635
636 636 def get_exception_only(self, etype, value):
637 637 """Only print the exception type and message, without a traceback.
638 638
639 639 Parameters
640 640 ----------
641 641 etype : exception type
642 642 value : exception value
643 643 """
644 644 return ListTB.structured_traceback(self, etype, value)
645 645
646 646 def show_exception_only(self, etype, evalue):
647 647 """Only print the exception type and message, without a traceback.
648 648
649 649 Parameters
650 650 ----------
651 651 etype : exception type
652 652 evalue : exception value
653 653 """
654 654 # This method needs to use __call__ from *this* class, not the one from
655 655 # a subclass whose signature or behavior may be different
656 656 ostream = self.ostream
657 657 ostream.flush()
658 658 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
659 659 ostream.flush()
660 660
661 661 def _some_str(self, value):
662 662 # Lifted from traceback.py
663 663 try:
664 664 return py3compat.cast_unicode(str(value))
665 665 except:
666 666 return u'<unprintable %s object>' % type(value).__name__
667 667
668 668
669 669 class FrameInfo:
670 670 """
671 671 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
672 672 really long frames.
673 673 """
674 674
675 675 description: Optional[str]
676 676 filename: str
677 677 lineno: int
678 678
679 679 @classmethod
680 680 def _from_stack_data_FrameInfo(cls, frame_info):
681 681 return cls(
682 682 getattr(frame_info, "description", None),
683 683 getattr(frame_info, "filename", None),
684 684 getattr(frame_info, "lineno", None),
685 685 getattr(frame_info, "frame", None),
686 686 getattr(frame_info, "code", None),
687 687 sd=frame_info,
688 688 )
689 689
690 690 def __init__(self, description, filename, lineno, frame, code, sd=None):
691 691 self.description = description
692 692 self.filename = filename
693 693 self.lineno = lineno
694 694 self.frame = frame
695 695 self.code = code
696 696 self._sd = sd
697 697
698 698 # self.lines = []
699 699 if sd is None:
700 700 ix = inspect.getsourcelines(frame)
701 701 self.raw_lines = ix[0]
702 702
703 703 @property
704 704 def variables_in_executing_piece(self):
705 705 if self._sd:
706 706 return self._sd.variables_in_executing_piece
707 707 else:
708 708 return []
709 709
710 710 @property
711 711 def lines(self):
712 712 return self._sd.lines
713 713
714 714 @property
715 715 def executing(self):
716 716 if self._sd:
717 717 return self._sd.executing
718 718 else:
719 719 return None
720 720
721 721
722 722 # ----------------------------------------------------------------------------
723 723 class VerboseTB(TBTools):
724 724 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
725 725 of HTML. Requires inspect and pydoc. Crazy, man.
726 726
727 727 Modified version which optionally strips the topmost entries from the
728 728 traceback, to be used with alternate interpreters (because their own code
729 729 would appear in the traceback)."""
730 730
731 731 _tb_highlight = "bg:ansiyellow"
732 732
733 733 def __init__(
734 734 self,
735 735 color_scheme: str = "Linux",
736 736 call_pdb: bool = False,
737 737 ostream=None,
738 738 tb_offset: int = 0,
739 739 long_header: bool = False,
740 740 include_vars: bool = True,
741 741 check_cache=None,
742 742 debugger_cls=None,
743 743 parent=None,
744 744 config=None,
745 745 ):
746 746 """Specify traceback offset, headers and color scheme.
747 747
748 748 Define how many frames to drop from the tracebacks. Calling it with
749 749 tb_offset=1 allows use of this handler in interpreters which will have
750 750 their own code at the top of the traceback (VerboseTB will first
751 751 remove that frame before printing the traceback info)."""
752 752 TBTools.__init__(
753 753 self,
754 754 color_scheme=color_scheme,
755 755 call_pdb=call_pdb,
756 756 ostream=ostream,
757 757 parent=parent,
758 758 config=config,
759 759 debugger_cls=debugger_cls,
760 760 )
761 761 self.tb_offset = tb_offset
762 762 self.long_header = long_header
763 763 self.include_vars = include_vars
764 764 # By default we use linecache.checkcache, but the user can provide a
765 765 # different check_cache implementation. This was formerly used by the
766 766 # IPython kernel for interactive code, but is no longer necessary.
767 767 if check_cache is None:
768 768 check_cache = linecache.checkcache
769 769 self.check_cache = check_cache
770 770
771 771 self.skip_hidden = True
772 772
773 773 def format_record(self, frame_info: FrameInfo):
774 774 """Format a single stack frame"""
775 775 assert isinstance(frame_info, FrameInfo)
776 776 Colors = self.Colors # just a shorthand + quicker name lookup
777 777 ColorsNormal = Colors.Normal # used a lot
778 778
779 779 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
780 780 return ' %s[... skipping similar frames: %s]%s\n' % (
781 781 Colors.excName, frame_info.description, ColorsNormal)
782 782
783 783 indent = " " * INDENT_SIZE
784 784 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
785 785 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
786 786 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
787 787 Colors.vName,
788 788 Colors.valEm,
789 789 ColorsNormal,
790 790 )
791 791 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
792 792
793 793 link = _format_filename(
794 794 frame_info.filename,
795 795 Colors.filenameEm,
796 796 ColorsNormal,
797 797 lineno=frame_info.lineno,
798 798 )
799 799 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
800 800 if frame_info.executing is not None:
801 801 func = frame_info.executing.code_qualname()
802 802 else:
803 803 func = "?"
804 804 if func == "<module>":
805 805 call = ""
806 806 else:
807 807 # Decide whether to include variable details or not
808 808 var_repr = eqrepr if self.include_vars else nullrepr
809 809 try:
810 810 scope = inspect.formatargvalues(
811 811 args, varargs, varkw, locals_, formatvalue=var_repr
812 812 )
813 813 call = tpl_call.format(file=func, scope=scope)
814 814 except KeyError:
815 815 # This happens in situations like errors inside generator
816 816 # expressions, where local variables are listed in the
817 817 # line, but can't be extracted from the frame. I'm not
818 818 # 100% sure this isn't actually a bug in inspect itself,
819 819 # but since there's no info for us to compute with, the
820 820 # best we can do is report the failure and move on. Here
821 821 # we must *not* call any traceback construction again,
822 822 # because that would mess up use of %debug later on. So we
823 823 # simply report the failure and move on. The only
824 824 # limitation will be that this frame won't have locals
825 825 # listed in the call signature. Quite subtle problem...
826 826 # I can't think of a good way to validate this in a unit
827 827 # test, but running a script consisting of:
828 828 # dict( (k,v.strip()) for (k,v) in range(10) )
829 829 # will illustrate the error, if this exception catch is
830 830 # disabled.
831 831 call = tpl_call_fail % func
832 832
833 833 lvals = ''
834 834 lvals_list = []
835 835 if self.include_vars:
836 836 try:
837 837 # we likely want to fix stackdata at some point, but
838 838 # still need a workaround.
839 839 fibp = frame_info.variables_in_executing_piece
840 840 for var in fibp:
841 841 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
842 842 except Exception:
843 843 lvals_list.append(
844 844 "Exception trying to inspect frame. No more locals available."
845 845 )
846 846 if lvals_list:
847 847 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
848 848
849 849 result = f'{link}{", " if call else ""}{call}\n'
850 850 if frame_info._sd is None:
851 851 assert False
852 852 # fast fallback if file is too long
853 853 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
854 854 link = tpl_link % util_path.compress_user(frame_info.filename)
855 855 level = "%s %s\n" % (link, call)
856 856 _line_format = PyColorize.Parser(
857 857 style=self.color_scheme_table.active_scheme_name, parent=self
858 858 ).format2
859 859 first_line = frame_info.code.co_firstlineno
860 860 current_line = frame_info.lineno[0]
861 861 return "%s%s" % (
862 862 level,
863 863 "".join(
864 864 _simple_format_traceback_lines(
865 865 current_line,
866 866 current_line - first_line,
867 867 frame_info.raw_lines,
868 868 Colors,
869 869 lvals,
870 870 _line_format,
871 871 )
872 872 ),
873 873 )
874 874 # result += "\n".join(frame_info.raw_lines)
875 875 else:
876 876 result += "".join(
877 877 _format_traceback_lines(
878 878 frame_info.lines, Colors, self.has_colors, lvals
879 879 )
880 880 )
881 881 return result
882 882
883 883 def prepare_header(self, etype: str, long_version: bool = False):
884 884 colors = self.Colors # just a shorthand + quicker name lookup
885 885 colorsnormal = colors.Normal # used a lot
886 886 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
887 887 width = min(75, get_terminal_size()[0])
888 888 if long_version:
889 889 # Header with the exception type, python version, and date
890 890 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
891 891 date = time.ctime(time.time())
892 892
893 893 head = "%s%s%s\n%s%s%s\n%s" % (
894 894 colors.topline,
895 895 "-" * width,
896 896 colorsnormal,
897 897 exc,
898 898 " " * (width - len(etype) - len(pyver)),
899 899 pyver,
900 900 date.rjust(width),
901 901 )
902 902 head += (
903 903 "\nA problem occurred executing Python code. Here is the sequence of function"
904 904 "\ncalls leading up to the error, with the most recent (innermost) call last."
905 905 )
906 906 else:
907 907 # Simplified header
908 908 head = "%s%s" % (
909 909 exc,
910 910 "Traceback (most recent call last)".rjust(width - len(etype)),
911 911 )
912 912
913 913 return head
914 914
915 915 def format_exception(self, etype, evalue):
916 916 colors = self.Colors # just a shorthand + quicker name lookup
917 917 colorsnormal = colors.Normal # used a lot
918 918 # Get (safely) a string form of the exception info
919 919 try:
920 920 etype_str, evalue_str = map(str, (etype, evalue))
921 921 except:
922 922 # User exception is improperly defined.
923 923 etype, evalue = str, sys.exc_info()[:2]
924 924 etype_str, evalue_str = map(str, (etype, evalue))
925 925 # ... and format it
926 926 return ['%s%s%s: %s' % (colors.excName, etype_str,
927 927 colorsnormal, py3compat.cast_unicode(evalue_str))]
928 928
929 929 def format_exception_as_a_whole(
930 930 self,
931 931 etype: type,
932 932 evalue: Optional[BaseException],
933 933 etb: Optional[TracebackType],
934 934 number_of_lines_of_context,
935 935 tb_offset: Optional[int],
936 936 ):
937 937 """Formats the header, traceback and exception message for a single exception.
938 938
939 939 This may be called multiple times by Python 3 exception chaining
940 940 (PEP 3134).
941 941 """
942 942 # some locals
943 943 orig_etype = etype
944 944 try:
945 945 etype = etype.__name__
946 946 except AttributeError:
947 947 pass
948 948
949 949 tb_offset = self.tb_offset if tb_offset is None else tb_offset
950 950 assert isinstance(tb_offset, int)
951 951 head = self.prepare_header(etype, self.long_header)
952 952 records = (
953 953 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
954 954 )
955 955
956 956 frames = []
957 957 skipped = 0
958 958 lastrecord = len(records) - 1
959 959 for i, record in enumerate(records):
960 960 if (
961 961 not isinstance(record._sd, stack_data.RepeatedFrames)
962 962 and self.skip_hidden
963 963 ):
964 964 if (
965 965 record.frame.f_locals.get("__tracebackhide__", 0)
966 966 and i != lastrecord
967 967 ):
968 968 skipped += 1
969 969 continue
970 970 if skipped:
971 971 Colors = self.Colors # just a shorthand + quicker name lookup
972 972 ColorsNormal = Colors.Normal # used a lot
973 973 frames.append(
974 974 " %s[... skipping hidden %s frame]%s\n"
975 975 % (Colors.excName, skipped, ColorsNormal)
976 976 )
977 977 skipped = 0
978 978 frames.append(self.format_record(record))
979 979 if skipped:
980 980 Colors = self.Colors # just a shorthand + quicker name lookup
981 981 ColorsNormal = Colors.Normal # used a lot
982 982 frames.append(
983 983 " %s[... skipping hidden %s frame]%s\n"
984 984 % (Colors.excName, skipped, ColorsNormal)
985 985 )
986 986
987 987 formatted_exception = self.format_exception(etype, evalue)
988 988 if records:
989 989 frame_info = records[-1]
990 990 ipinst = get_ipython()
991 991 if ipinst is not None:
992 992 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
993 993
994 994 return [[head] + frames + [''.join(formatted_exception[0])]]
995 995
996 996 def get_records(
997 997 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
998 998 ):
999 999 assert etb is not None
1000 1000 context = number_of_lines_of_context - 1
1001 1001 after = context // 2
1002 1002 before = context - after
1003 1003 if self.has_colors:
1004 1004 style = get_style_by_name("default")
1005 1005 style = stack_data.style_with_executing_node(style, self._tb_highlight)
1006 1006 formatter = Terminal256Formatter(style=style)
1007 1007 else:
1008 1008 formatter = None
1009 1009 options = stack_data.Options(
1010 1010 before=before,
1011 1011 after=after,
1012 1012 pygments_formatter=formatter,
1013 1013 )
1014 1014
1015 1015 # Let's estimate the amount of code we will have to parse/highlight.
1016 1016 cf: Optional[TracebackType] = etb
1017 1017 max_len = 0
1018 1018 tbs = []
1019 1019 while cf is not None:
1020 source_file = inspect.getsourcefile(etb.tb_frame)
1021 lines, first = inspect.getsourcelines(etb.tb_frame)
1020 try:
1021 source_file = inspect.getsourcefile(etb.tb_frame)
1022 lines, first = inspect.getsourcelines(etb.tb_frame)
1023 except OSError:
1024 max_len = float("-inf")
1025 break
1022 1026 max_len = max(max_len, first + len(lines))
1023 1027 tbs.append(cf)
1024 1028 cf = cf.tb_next
1025 1029
1026 1030 if max_len > FAST_THRESHOLD:
1027 1031 FIs = []
1028 1032 for tb in tbs:
1029 1033 frame = tb.tb_frame
1030 1034 lineno = (frame.f_lineno,)
1031 1035 code = frame.f_code
1032 1036 filename = code.co_filename
1033 1037 FIs.append(FrameInfo("Raw frame", filename, lineno, frame, code))
1034 1038 return FIs
1035 1039 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1036 1040 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1037 1041 return res
1038 1042
1039 1043 def structured_traceback(
1040 1044 self,
1041 1045 etype: type,
1042 1046 evalue: Optional[BaseException],
1043 1047 etb: Optional[TracebackType],
1044 1048 tb_offset: Optional[int] = None,
1045 1049 number_of_lines_of_context: int = 5,
1046 1050 ):
1047 1051 """Return a nice text document describing the traceback."""
1048 1052 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1049 1053 tb_offset)
1050 1054
1051 1055 colors = self.Colors # just a shorthand + quicker name lookup
1052 1056 colorsnormal = colors.Normal # used a lot
1053 1057 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1054 1058 structured_traceback_parts = [head]
1055 1059 chained_exceptions_tb_offset = 0
1056 1060 lines_of_context = 3
1057 1061 formatted_exceptions = formatted_exception
1058 1062 exception = self.get_parts_of_chained_exception(evalue)
1059 1063 if exception:
1060 1064 assert evalue is not None
1061 1065 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1062 1066 etype, evalue, etb = exception
1063 1067 else:
1064 1068 evalue = None
1065 1069 chained_exc_ids = set()
1066 1070 while evalue:
1067 1071 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1068 1072 chained_exceptions_tb_offset)
1069 1073 exception = self.get_parts_of_chained_exception(evalue)
1070 1074
1071 1075 if exception and not id(exception[1]) in chained_exc_ids:
1072 1076 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1073 1077 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1074 1078 etype, evalue, etb = exception
1075 1079 else:
1076 1080 evalue = None
1077 1081
1078 1082 # we want to see exceptions in a reversed order:
1079 1083 # the first exception should be on top
1080 1084 for formatted_exception in reversed(formatted_exceptions):
1081 1085 structured_traceback_parts += formatted_exception
1082 1086
1083 1087 return structured_traceback_parts
1084 1088
1085 1089 def debugger(self, force: bool = False):
1086 1090 """Call up the pdb debugger if desired, always clean up the tb
1087 1091 reference.
1088 1092
1089 1093 Keywords:
1090 1094
1091 1095 - force(False): by default, this routine checks the instance call_pdb
1092 1096 flag and does not actually invoke the debugger if the flag is false.
1093 1097 The 'force' option forces the debugger to activate even if the flag
1094 1098 is false.
1095 1099
1096 1100 If the call_pdb flag is set, the pdb interactive debugger is
1097 1101 invoked. In all cases, the self.tb reference to the current traceback
1098 1102 is deleted to prevent lingering references which hamper memory
1099 1103 management.
1100 1104
1101 1105 Note that each call to pdb() does an 'import readline', so if your app
1102 1106 requires a special setup for the readline completers, you'll have to
1103 1107 fix that by hand after invoking the exception handler."""
1104 1108
1105 1109 if force or self.call_pdb:
1106 1110 if self.pdb is None:
1107 1111 self.pdb = self.debugger_cls()
1108 1112 # the system displayhook may have changed, restore the original
1109 1113 # for pdb
1110 1114 display_trap = DisplayTrap(hook=sys.__displayhook__)
1111 1115 with display_trap:
1112 1116 self.pdb.reset()
1113 1117 # Find the right frame so we don't pop up inside ipython itself
1114 1118 if hasattr(self, 'tb') and self.tb is not None:
1115 1119 etb = self.tb
1116 1120 else:
1117 1121 etb = self.tb = sys.last_traceback
1118 1122 while self.tb is not None and self.tb.tb_next is not None:
1119 1123 assert self.tb.tb_next is not None
1120 1124 self.tb = self.tb.tb_next
1121 1125 if etb and etb.tb_next:
1122 1126 etb = etb.tb_next
1123 1127 self.pdb.botframe = etb.tb_frame
1124 1128 self.pdb.interaction(None, etb)
1125 1129
1126 1130 if hasattr(self, 'tb'):
1127 1131 del self.tb
1128 1132
1129 1133 def handler(self, info=None):
1130 1134 (etype, evalue, etb) = info or sys.exc_info()
1131 1135 self.tb = etb
1132 1136 ostream = self.ostream
1133 1137 ostream.flush()
1134 1138 ostream.write(self.text(etype, evalue, etb))
1135 1139 ostream.write('\n')
1136 1140 ostream.flush()
1137 1141
1138 1142 # Changed so an instance can just be called as VerboseTB_inst() and print
1139 1143 # out the right info on its own.
1140 1144 def __call__(self, etype=None, evalue=None, etb=None):
1141 1145 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1142 1146 if etb is None:
1143 1147 self.handler()
1144 1148 else:
1145 1149 self.handler((etype, evalue, etb))
1146 1150 try:
1147 1151 self.debugger()
1148 1152 except KeyboardInterrupt:
1149 1153 print("\nKeyboardInterrupt")
1150 1154
1151 1155
1152 1156 #----------------------------------------------------------------------------
1153 1157 class FormattedTB(VerboseTB, ListTB):
1154 1158 """Subclass ListTB but allow calling with a traceback.
1155 1159
1156 1160 It can thus be used as a sys.excepthook for Python > 2.1.
1157 1161
1158 1162 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1159 1163
1160 1164 Allows a tb_offset to be specified. This is useful for situations where
1161 1165 one needs to remove a number of topmost frames from the traceback (such as
1162 1166 occurs with python programs that themselves execute other python code,
1163 1167 like Python shells). """
1164 1168
1165 1169 mode: str
1166 1170
1167 1171 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1168 1172 ostream=None,
1169 1173 tb_offset=0, long_header=False, include_vars=False,
1170 1174 check_cache=None, debugger_cls=None,
1171 1175 parent=None, config=None):
1172 1176
1173 1177 # NEVER change the order of this list. Put new modes at the end:
1174 1178 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
1175 1179 self.verbose_modes = self.valid_modes[1:3]
1176 1180
1177 1181 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1178 1182 ostream=ostream, tb_offset=tb_offset,
1179 1183 long_header=long_header, include_vars=include_vars,
1180 1184 check_cache=check_cache, debugger_cls=debugger_cls,
1181 1185 parent=parent, config=config)
1182 1186
1183 1187 # Different types of tracebacks are joined with different separators to
1184 1188 # form a single string. They are taken from this dict
1185 1189 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1186 1190 Minimal='')
1187 1191 # set_mode also sets the tb_join_char attribute
1188 1192 self.set_mode(mode)
1189 1193
1190 1194 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1191 1195 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1192 1196 mode = self.mode
1193 1197 if mode in self.verbose_modes:
1194 1198 # Verbose modes need a full traceback
1195 1199 return VerboseTB.structured_traceback(
1196 1200 self, etype, value, tb, tb_offset, number_of_lines_of_context
1197 1201 )
1198 1202 elif mode == 'Minimal':
1199 1203 return ListTB.get_exception_only(self, etype, value)
1200 1204 else:
1201 1205 # We must check the source cache because otherwise we can print
1202 1206 # out-of-date source code.
1203 1207 self.check_cache()
1204 1208 # Now we can extract and format the exception
1205 1209 return ListTB.structured_traceback(
1206 1210 self, etype, value, tb, tb_offset, number_of_lines_of_context
1207 1211 )
1208 1212
1209 1213 def stb2text(self, stb):
1210 1214 """Convert a structured traceback (a list) to a string."""
1211 1215 return self.tb_join_char.join(stb)
1212 1216
1213 1217 def set_mode(self, mode: Optional[str] = None):
1214 1218 """Switch to the desired mode.
1215 1219
1216 1220 If mode is not specified, cycles through the available modes."""
1217 1221
1218 1222 if not mode:
1219 1223 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1220 1224 len(self.valid_modes)
1221 1225 self.mode = self.valid_modes[new_idx]
1222 1226 elif mode not in self.valid_modes:
1223 1227 raise ValueError(
1224 1228 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1225 1229 "Valid modes: " + str(self.valid_modes)
1226 1230 )
1227 1231 else:
1228 1232 assert isinstance(mode, str)
1229 1233 self.mode = mode
1230 1234 # include variable details only in 'Verbose' mode
1231 1235 self.include_vars = (self.mode == self.valid_modes[2])
1232 1236 # Set the join character for generating text tracebacks
1233 1237 self.tb_join_char = self._join_chars[self.mode]
1234 1238
1235 1239 # some convenient shortcuts
1236 1240 def plain(self):
1237 1241 self.set_mode(self.valid_modes[0])
1238 1242
1239 1243 def context(self):
1240 1244 self.set_mode(self.valid_modes[1])
1241 1245
1242 1246 def verbose(self):
1243 1247 self.set_mode(self.valid_modes[2])
1244 1248
1245 1249 def minimal(self):
1246 1250 self.set_mode(self.valid_modes[3])
1247 1251
1248 1252
1249 1253 #----------------------------------------------------------------------------
1250 1254 class AutoFormattedTB(FormattedTB):
1251 1255 """A traceback printer which can be called on the fly.
1252 1256
1253 1257 It will find out about exceptions by itself.
1254 1258
1255 1259 A brief example::
1256 1260
1257 1261 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1258 1262 try:
1259 1263 ...
1260 1264 except:
1261 1265 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1262 1266 """
1263 1267
1264 1268 def __call__(self, etype=None, evalue=None, etb=None,
1265 1269 out=None, tb_offset=None):
1266 1270 """Print out a formatted exception traceback.
1267 1271
1268 1272 Optional arguments:
1269 1273 - out: an open file-like object to direct output to.
1270 1274
1271 1275 - tb_offset: the number of frames to skip over in the stack, on a
1272 1276 per-call basis (this overrides temporarily the instance's tb_offset
1273 1277 given at initialization time."""
1274 1278
1275 1279 if out is None:
1276 1280 out = self.ostream
1277 1281 out.flush()
1278 1282 out.write(self.text(etype, evalue, etb, tb_offset))
1279 1283 out.write('\n')
1280 1284 out.flush()
1281 1285 # FIXME: we should remove the auto pdb behavior from here and leave
1282 1286 # that to the clients.
1283 1287 try:
1284 1288 self.debugger()
1285 1289 except KeyboardInterrupt:
1286 1290 print("\nKeyboardInterrupt")
1287 1291
1288 1292 def structured_traceback(
1289 1293 self,
1290 1294 etype=None,
1291 1295 value=None,
1292 1296 tb=None,
1293 1297 tb_offset=None,
1294 1298 number_of_lines_of_context=5,
1295 1299 ):
1296 1300 etype: type
1297 1301 value: BaseException
1298 1302 # tb: TracebackType or tupleof tb types ?
1299 1303 if etype is None:
1300 1304 etype, value, tb = sys.exc_info()
1301 1305 if isinstance(tb, tuple):
1302 1306 # tb is a tuple if this is a chained exception.
1303 1307 self.tb = tb[0]
1304 1308 else:
1305 1309 self.tb = tb
1306 1310 return FormattedTB.structured_traceback(
1307 1311 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1308 1312
1309 1313
1310 1314 #---------------------------------------------------------------------------
1311 1315
1312 1316 # A simple class to preserve Nathan's original functionality.
1313 1317 class ColorTB(FormattedTB):
1314 1318 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1315 1319
1316 1320 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1317 1321 FormattedTB.__init__(self, color_scheme=color_scheme,
1318 1322 call_pdb=call_pdb, **kwargs)
1319 1323
1320 1324
1321 1325 class SyntaxTB(ListTB):
1322 1326 """Extension which holds some state: the last exception value"""
1323 1327
1324 1328 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1325 1329 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1326 1330 self.last_syntax_error = None
1327 1331
1328 1332 def __call__(self, etype, value, elist):
1329 1333 self.last_syntax_error = value
1330 1334
1331 1335 ListTB.__call__(self, etype, value, elist)
1332 1336
1333 1337 def structured_traceback(self, etype, value, elist, tb_offset=None,
1334 1338 context=5):
1335 1339 # If the source file has been edited, the line in the syntax error can
1336 1340 # be wrong (retrieved from an outdated cache). This replaces it with
1337 1341 # the current value.
1338 1342 if isinstance(value, SyntaxError) \
1339 1343 and isinstance(value.filename, str) \
1340 1344 and isinstance(value.lineno, int):
1341 1345 linecache.checkcache(value.filename)
1342 1346 newtext = linecache.getline(value.filename, value.lineno)
1343 1347 if newtext:
1344 1348 value.text = newtext
1345 1349 self.last_syntax_error = value
1346 1350 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1347 1351 tb_offset=tb_offset, context=context)
1348 1352
1349 1353 def clear_err_state(self):
1350 1354 """Return the current error state and clear it"""
1351 1355 e = self.last_syntax_error
1352 1356 self.last_syntax_error = None
1353 1357 return e
1354 1358
1355 1359 def stb2text(self, stb):
1356 1360 """Convert a structured traceback (a list) to a string."""
1357 1361 return ''.join(stb)
1358 1362
1359 1363
1360 1364 # some internal-use functions
1361 1365 def text_repr(value):
1362 1366 """Hopefully pretty robust repr equivalent."""
1363 1367 # this is pretty horrible but should always return *something*
1364 1368 try:
1365 1369 return pydoc.text.repr(value)
1366 1370 except KeyboardInterrupt:
1367 1371 raise
1368 1372 except:
1369 1373 try:
1370 1374 return repr(value)
1371 1375 except KeyboardInterrupt:
1372 1376 raise
1373 1377 except:
1374 1378 try:
1375 1379 # all still in an except block so we catch
1376 1380 # getattr raising
1377 1381 name = getattr(value, '__name__', None)
1378 1382 if name:
1379 1383 # ick, recursion
1380 1384 return text_repr(name)
1381 1385 klass = getattr(value, '__class__', None)
1382 1386 if klass:
1383 1387 return '%s instance' % text_repr(klass)
1384 1388 except KeyboardInterrupt:
1385 1389 raise
1386 1390 except:
1387 1391 return 'UNRECOVERABLE REPR FAILURE'
1388 1392
1389 1393
1390 1394 def eqrepr(value, repr=text_repr):
1391 1395 return '=%s' % repr(value)
1392 1396
1393 1397
1394 1398 def nullrepr(value, repr=text_repr):
1395 1399 return ''
General Comments 0
You need to be logged in to leave comments. Login now