##// END OF EJS Templates
Resurrect fast (non-highlighted) traceback code for long files. (#13947)
Matthias Bussonnier -
r28127:3edbe3bd merge
parent child Browse files
Show More
@@ -3036,7 +3036,6 b' class IPCompleter(Completer):'
3036 skip_matchers: Set[str],
3036 skip_matchers: Set[str],
3037 abort_if_offset_changes: bool,
3037 abort_if_offset_changes: bool,
3038 ):
3038 ):
3039
3040 sortable: List[AnyMatcherCompletion] = []
3039 sortable: List[AnyMatcherCompletion] = []
3041 ordered: List[AnyMatcherCompletion] = []
3040 ordered: List[AnyMatcherCompletion] = []
3042 most_recent_fragment = None
3041 most_recent_fragment = None
@@ -591,7 +591,7 b' class Pdb(OldPdb):'
591 """
591 """
592 if not args.strip():
592 if not args.strip():
593 print("current predicates:")
593 print("current predicates:")
594 for (p, v) in self._predicates.items():
594 for p, v in self._predicates.items():
595 print(" ", p, ":", v)
595 print(" ", p, ":", v)
596 return
596 return
597 type_value = args.strip().split(" ")
597 type_value = args.strip().split(" ")
@@ -838,7 +838,6 b' class Pdb(OldPdb):'
838 return False
838 return False
839
839
840 def stop_here(self, frame):
840 def stop_here(self, frame):
841
842 if self._is_in_decorator_internal_and_should_skip(frame) is True:
841 if self._is_in_decorator_internal_and_should_skip(frame) is True:
843 return False
842 return False
844
843
@@ -471,7 +471,6 b' def test_decorator_skip_with_breakpoint():'
471
471
472 ### we need a filename, so we need to exec the full block with a filename
472 ### we need a filename, so we need to exec the full block with a filename
473 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
473 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
474
475 name = tf.name[:-3].split("/")[-1]
474 name = tf.name[:-3].split("/")[-1]
476 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
475 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
477 tf.flush()
476 tf.flush()
@@ -1094,7 +1094,6 b' def test_script_err():'
1094
1094
1095
1095
1096 def test_script_out_err():
1096 def test_script_out_err():
1097
1098 ip = get_ipython()
1097 ip = get_ipython()
1099 ip.run_cell_magic(
1098 ip.run_cell_magic(
1100 "script",
1099 "script",
@@ -193,7 +193,6 b' class TestPylabSwitch(object):'
193 matplotlib.rcParamsOrig = self._saved_rcParamsOrig
193 matplotlib.rcParamsOrig = self._saved_rcParamsOrig
194
194
195 def test_qt(self):
195 def test_qt(self):
196
197 s = self.Shell()
196 s = self.Shell()
198 gui, backend = s.enable_matplotlib(None)
197 gui, backend = s.enable_matplotlib(None)
199 assert gui == "qt"
198 assert gui == "qt"
@@ -99,6 +99,7 b' from types import TracebackType'
99 from typing import Tuple, List, Any, Optional
99 from typing import Tuple, List, Any, Optional
100
100
101 import stack_data
101 import stack_data
102 from stack_data import FrameInfo as SDFrameInfo
102 from pygments.formatters.terminal256 import Terminal256Formatter
103 from pygments.formatters.terminal256 import Terminal256Formatter
103 from pygments.styles import get_style_by_name
104 from pygments.styles import get_style_by_name
104
105
@@ -107,6 +108,7 b' from IPython import get_ipython'
107 from IPython.core import debugger
108 from IPython.core import debugger
108 from IPython.core.display_trap import DisplayTrap
109 from IPython.core.display_trap import DisplayTrap
109 from IPython.core.excolors import exception_colors
110 from IPython.core.excolors import exception_colors
111 from IPython.utils import PyColorize
110 from IPython.utils import path as util_path
112 from IPython.utils import path as util_path
111 from IPython.utils import py3compat
113 from IPython.utils import py3compat
112 from IPython.utils.terminal import get_terminal_size
114 from IPython.utils.terminal import get_terminal_size
@@ -122,6 +124,7 b' INDENT_SIZE = 8'
122 # value is used, but having a module global makes this functionality available
124 # value is used, but having a module global makes this functionality available
123 # to users of ultratb who are NOT running inside ipython.
125 # to users of ultratb who are NOT running inside ipython.
124 DEFAULT_SCHEME = 'NoColor'
126 DEFAULT_SCHEME = 'NoColor'
127 FAST_THRESHOLD = 10_000
125
128
126 # ---------------------------------------------------------------------------
129 # ---------------------------------------------------------------------------
127 # Code begins
130 # Code begins
@@ -170,6 +173,55 b' def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):'
170 res.append(lvals + '\n')
173 res.append(lvals + '\n')
171 return res
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" % (
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)
219
220 res.append(line)
221 if lvals and i == lnum:
222 res.append(lvals + "\n")
223 return res
224
173
225
174 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
226 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
175 """
227 """
@@ -283,7 +335,6 b' class TBTools(colorable.Colorable):'
283 def get_parts_of_chained_exception(
335 def get_parts_of_chained_exception(
284 self, evalue
336 self, evalue
285 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
337 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
286
287 chained_evalue = self._get_chained_exception(evalue)
338 chained_evalue = self._get_chained_exception(evalue)
288
339
289 if chained_evalue:
340 if chained_evalue:
@@ -607,7 +658,60 b' class ListTB(TBTools):'
607 return u'<unprintable %s object>' % type(value).__name__
658 return u'<unprintable %s object>' % type(value).__name__
608
659
609
660
610 #----------------------------------------------------------------------------
661 class FrameInfo:
662 """
663 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
664 really long frames.
665 """
666
667 description: Optional[str]
668 filename: str
669 lineno: int
670
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 )
681
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
689
690 # self.lines = []
691 if sd is None:
692 ix = inspect.getsourcelines(frame)
693 self.raw_lines = ix[0]
694
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 []
701
702 @property
703 def lines(self):
704 return self._sd.lines
705
706 @property
707 def executing(self):
708 if self._sd:
709 return self._sd.executing
710 else:
711 return None
712
713
714 # ----------------------------------------------------------------------------
611 class VerboseTB(TBTools):
715 class VerboseTB(TBTools):
612 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
716 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
613 of HTML. Requires inspect and pydoc. Crazy, man.
717 of HTML. Requires inspect and pydoc. Crazy, man.
@@ -658,12 +762,13 b' class VerboseTB(TBTools):'
658
762
659 self.skip_hidden = True
763 self.skip_hidden = True
660
764
661 def format_record(self, frame_info):
765 def format_record(self, frame_info: FrameInfo):
662 """Format a single stack frame"""
766 """Format a single stack frame"""
767 assert isinstance(frame_info, FrameInfo)
663 Colors = self.Colors # just a shorthand + quicker name lookup
768 Colors = self.Colors # just a shorthand + quicker name lookup
664 ColorsNormal = Colors.Normal # used a lot
769 ColorsNormal = Colors.Normal # used a lot
665
770
666 if isinstance(frame_info, stack_data.RepeatedFrames):
771 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
667 return ' %s[... skipping similar frames: %s]%s\n' % (
772 return ' %s[... skipping similar frames: %s]%s\n' % (
668 Colors.excName, frame_info.description, ColorsNormal)
773 Colors.excName, frame_info.description, ColorsNormal)
669
774
@@ -684,8 +789,10 b' class VerboseTB(TBTools):'
684 lineno=frame_info.lineno,
789 lineno=frame_info.lineno,
685 )
790 )
686 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
791 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
687
792 if frame_info.executing is not None:
688 func = frame_info.executing.code_qualname()
793 func = frame_info.executing.code_qualname()
794 else:
795 func = "?"
689 if func == "<module>":
796 if func == "<module>":
690 call = ""
797 call = ""
691 else:
798 else:
@@ -732,8 +839,37 b' class VerboseTB(TBTools):'
732 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
839 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
733
840
734 result = f'{link}{", " if call else ""}{call}\n'
841 result = f'{link}{", " if call else ""}{call}\n'
735
842 if frame_info._sd is None:
736 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals))
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 )
737 return result
873 return result
738
874
739 def prepare_header(self, etype, long_version=False):
875 def prepare_header(self, etype, long_version=False):
@@ -802,9 +938,15 b' class VerboseTB(TBTools):'
802 frames = []
938 frames = []
803 skipped = 0
939 skipped = 0
804 lastrecord = len(records) - 1
940 lastrecord = len(records) - 1
805 for i, r in enumerate(records):
941 for i, record in enumerate(records):
806 if not isinstance(r, stack_data.RepeatedFrames) and self.skip_hidden:
942 if (
807 if r.frame.f_locals.get("__tracebackhide__", 0) and i != lastrecord:
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 ):
808 skipped += 1
950 skipped += 1
809 continue
951 continue
810 if skipped:
952 if skipped:
@@ -815,7 +957,7 b' class VerboseTB(TBTools):'
815 % (Colors.excName, skipped, ColorsNormal)
957 % (Colors.excName, skipped, ColorsNormal)
816 )
958 )
817 skipped = 0
959 skipped = 0
818 frames.append(self.format_record(r))
960 frames.append(self.format_record(record))
819 if skipped:
961 if skipped:
820 Colors = self.Colors # just a shorthand + quicker name lookup
962 Colors = self.Colors # just a shorthand + quicker name lookup
821 ColorsNormal = Colors.Normal # used a lot
963 ColorsNormal = Colors.Normal # used a lot
@@ -851,7 +993,30 b' class VerboseTB(TBTools):'
851 after=after,
993 after=after,
852 pygments_formatter=formatter,
994 pygments_formatter=formatter,
853 )
995 )
854 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
996
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
1007
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
855
1020
856 def structured_traceback(
1021 def structured_traceback(
857 self,
1022 self,
@@ -1102,9 +1267,14 b' class AutoFormattedTB(FormattedTB):'
1102 except KeyboardInterrupt:
1267 except KeyboardInterrupt:
1103 print("\nKeyboardInterrupt")
1268 print("\nKeyboardInterrupt")
1104
1269
1105 def structured_traceback(self, etype=None, value=None, tb=None,
1270 def structured_traceback(
1106 tb_offset=None, number_of_lines_of_context=5):
1271 self,
1107
1272 etype=None,
1273 value=None,
1274 tb=None,
1275 tb_offset=None,
1276 number_of_lines_of_context=5,
1277 ):
1108 etype: type
1278 etype: type
1109 value: BaseException
1279 value: BaseException
1110 # tb: TracebackType or tupleof tb types ?
1280 # tb: TracebackType or tupleof tb types ?
@@ -66,7 +66,6 b' class TerminalPdb(Pdb):'
66 # setup history only when we start pdb
66 # setup history only when we start pdb
67 if self.shell.debugger_history is None:
67 if self.shell.debugger_history is None:
68 if self.shell.debugger_history_file is not None:
68 if self.shell.debugger_history_file is not None:
69
70 p = Path(self.shell.debugger_history_file).expanduser()
69 p = Path(self.shell.debugger_history_file).expanduser()
71 if not p.exists():
70 if not p.exists():
72 p.touch()
71 p.touch()
@@ -626,7 +626,6 b' class IPDoctestModule(pytest.Module):'
626 if _is_mocked(obj):
626 if _is_mocked(obj):
627 return
627 return
628 with _patch_unwrap_mock_aware():
628 with _patch_unwrap_mock_aware():
629
630 # Type ignored because this is a private function.
629 # Type ignored because this is a private function.
631 super()._find( # type:ignore[misc]
630 super()._find( # type:ignore[misc]
632 tests, obj, name, module, source_lines, globs, seen
631 tests, obj, name, module, source_lines, globs, seen
General Comments 0
You need to be logged in to leave comments. Login now