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