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