##// END OF EJS Templates
FEAT: Fast TB....
Matthias Bussonnier -
Show More
@@ -99,6 +99,7 b' from types import TracebackType'
99 99 from typing import Tuple, List, Any, Optional
100 100
101 101 import stack_data
102 from stack_data import FrameInfo as SDFrameInfo
102 103 from pygments.formatters.terminal256 import Terminal256Formatter
103 104 from pygments.styles import get_style_by_name
104 105
@@ -107,6 +108,7 b' from IPython import get_ipython'
107 108 from IPython.core import debugger
108 109 from IPython.core.display_trap import DisplayTrap
109 110 from IPython.core.excolors import exception_colors
111 from IPython.utils import PyColorize
110 112 from IPython.utils import path as util_path
111 113 from IPython.utils import py3compat
112 114 from IPython.utils.terminal import get_terminal_size
@@ -122,6 +124,7 b' INDENT_SIZE = 8'
122 124 # value is used, but having a module global makes this functionality available
123 125 # to users of ultratb who are NOT running inside ipython.
124 126 DEFAULT_SCHEME = 'NoColor'
127 FAST_THRESHOLD = 10_000
125 128
126 129 # ---------------------------------------------------------------------------
127 130 # Code begins
@@ -170,6 +173,50 b' def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):'
170 173 res.append(lvals + '\n')
171 174 return res
172 175
176 def _simple_format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):
177 """
178 Format tracebacks lines with pointing arrow, leading numbers...
179
180 Parameters
181 ==========
182
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 = []
197
198 for i,line in enumerate(lines, lnum-index):
199 line = py3compat.cast_unicode(line)
200
201 new_line, err = _line_format(line, 'str')
202 if not err:
203 line = new_line
204
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' % (Colors.linenoEm, num,
210 Colors.line, line, Colors.Normal)
211 else:
212 num = '%*s' % (numbers_width, i)
213 line = '%s%s%s %s' % (Colors.lineno, num,
214 Colors.Normal, line)
215
216 res.append(line)
217 if lvals and i == lnum:
218 res.append(lvals + '\n')
219 return res
173 220
174 221 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
175 222 """
@@ -607,6 +654,61 b' class ListTB(TBTools):'
607 654 return u'<unprintable %s object>' % type(value).__name__
608 655
609 656
657 class FrameInfo:
658 """
659 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
660 really long frames.
661 """
662
663 description : Optional[str]
664 filename : str
665 lineno : int
666
667 @classmethod
668 def _from_stack_data_FrameInfo(cls, frame_info):
669 return cls(
670 getattr(frame_info, "description", None),
671 getattr(frame_info, "filename", None),
672 getattr(frame_info, "lineno", None),
673 getattr(frame_info, "frame", None),
674 getattr(frame_info, "code", None),
675 sd=frame_info,
676 )
677
678 def __init__(self, description, filename, lineno, frame, code, sd=None):
679 self.description = description
680 self.filename = filename
681 self.lineno = lineno
682 self.frame = frame
683 self.code = code
684 self._sd = sd
685
686 # self.lines = []
687 if sd is None:
688 ix = inspect.getsourcelines(frame)
689 self.raw_lines = ix[0]
690
691 @property
692 def variables_in_executing_piece(self):
693 if self._sd:
694 return self._sd.variables_in_executing_piece
695 else:
696 return []
697
698 @property
699 def lines(self):
700 return self._sd.lines
701
702 @property
703 def executing(self):
704 if self._sd:
705 return self._sd.executing
706 else:
707 return None
708
709
710
711
610 712 #----------------------------------------------------------------------------
611 713 class VerboseTB(TBTools):
612 714 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
@@ -658,12 +760,13 b' class VerboseTB(TBTools):'
658 760
659 761 self.skip_hidden = True
660 762
661 def format_record(self, frame_info):
763 def format_record(self, frame_info:FrameInfo):
662 764 """Format a single stack frame"""
765 assert isinstance(frame_info, FrameInfo)
663 766 Colors = self.Colors # just a shorthand + quicker name lookup
664 767 ColorsNormal = Colors.Normal # used a lot
665 768
666 if isinstance(frame_info, stack_data.RepeatedFrames):
769 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
667 770 return ' %s[... skipping similar frames: %s]%s\n' % (
668 771 Colors.excName, frame_info.description, ColorsNormal)
669 772
@@ -684,8 +787,10 b' class VerboseTB(TBTools):'
684 787 lineno=frame_info.lineno,
685 788 )
686 789 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
687
688 func = frame_info.executing.code_qualname()
790 if frame_info.executing is not None:
791 func = frame_info.executing.code_qualname()
792 else:
793 func = '?'
689 794 if func == "<module>":
690 795 call = ""
691 796 else:
@@ -732,8 +837,25 b' class VerboseTB(TBTools):'
732 837 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
733 838
734 839 result = f'{link}{", " if call else ""}{call}\n'
735
736 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals))
840 if frame_info._sd is None:
841 assert False
842 # fast fallback if file is too long
843 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
844 link = tpl_link % util_path.compress_user(frame_info.filename)
845 level = '%s %s\n' % (link, call)
846 _line_format = PyColorize.Parser(style=self.color_scheme_table.active_scheme_name, parent=self).format2
847 first_line = frame_info.code.co_firstlineno
848 current_line = frame_info.lineno[0]
849 return '%s%s' % (level, ''.join(
850 _simple_format_traceback_lines(current_line, current_line-first_line, frame_info.raw_lines, Colors, lvals,
851 _line_format)))
852 #result += "\n".join(frame_info.raw_lines)
853 else:
854 result += "".join(
855 _format_traceback_lines(
856 frame_info.lines, Colors, self.has_colors, lvals
857 )
858 )
737 859 return result
738 860
739 861 def prepare_header(self, etype, long_version=False):
@@ -802,9 +924,9 b' class VerboseTB(TBTools):'
802 924 frames = []
803 925 skipped = 0
804 926 lastrecord = len(records) - 1
805 for i, r in enumerate(records):
806 if not isinstance(r, stack_data.RepeatedFrames) and self.skip_hidden:
807 if r.frame.f_locals.get("__tracebackhide__", 0) and i != lastrecord:
927 for i, record in enumerate(records):
928 if not isinstance(record._sd, stack_data.RepeatedFrames) and self.skip_hidden:
929 if record.frame.f_locals.get("__tracebackhide__", 0) and i != lastrecord:
808 930 skipped += 1
809 931 continue
810 932 if skipped:
@@ -815,7 +937,7 b' class VerboseTB(TBTools):'
815 937 % (Colors.excName, skipped, ColorsNormal)
816 938 )
817 939 skipped = 0
818 frames.append(self.format_record(r))
940 frames.append(self.format_record(record))
819 941 if skipped:
820 942 Colors = self.Colors # just a shorthand + quicker name lookup
821 943 ColorsNormal = Colors.Normal # used a lot
@@ -851,7 +973,32 b' class VerboseTB(TBTools):'
851 973 after=after,
852 974 pygments_formatter=formatter,
853 975 )
854 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
976
977 # let's estimate the amount of code we eill have to parse/highlight.
978 cf = etb
979 max_len = 0
980 tbs = []
981 while cf is not None:
982 source_file = inspect.getsourcefile(etb.tb_frame)
983 lines, first = inspect.getsourcelines(etb.tb_frame)
984 max_len = max(max_len, first+len(lines))
985 tbs.append(cf)
986 cf = cf.tb_next
987
988
989
990 if max_len > FAST_THRESHOLD:
991 FIs = []
992 for tb in tbs:
993 frame = tb.tb_frame
994 lineno = frame.f_lineno,
995 code = frame.f_code
996 filename = code.co_filename
997 FIs.append( FrameInfo("Raw frame", filename, lineno, frame, code))
998 return FIs
999 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1000 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1001 return res
855 1002
856 1003 def structured_traceback(
857 1004 self,
General Comments 0
You need to be logged in to leave comments. Login now