Show More
@@ -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,50 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' % (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 | def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None): |
|
221 | def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None): | |
175 | """ |
|
222 | """ | |
@@ -607,6 +654,61 b' class ListTB(TBTools):' | |||||
607 | return u'<unprintable %s object>' % type(value).__name__ |
|
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 | class VerboseTB(TBTools): |
|
713 | class VerboseTB(TBTools): | |
612 | """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead |
|
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 | self.skip_hidden = True |
|
761 | self.skip_hidden = True | |
660 |
|
762 | |||
661 | def format_record(self, frame_info): |
|
763 | def format_record(self, frame_info:FrameInfo): | |
662 | """Format a single stack frame""" |
|
764 | """Format a single stack frame""" | |
|
765 | assert isinstance(frame_info, FrameInfo) | |||
663 | Colors = self.Colors # just a shorthand + quicker name lookup |
|
766 | Colors = self.Colors # just a shorthand + quicker name lookup | |
664 | ColorsNormal = Colors.Normal # used a lot |
|
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 | return ' %s[... skipping similar frames: %s]%s\n' % ( |
|
770 | return ' %s[... skipping similar frames: %s]%s\n' % ( | |
668 | Colors.excName, frame_info.description, ColorsNormal) |
|
771 | Colors.excName, frame_info.description, ColorsNormal) | |
669 |
|
772 | |||
@@ -684,8 +787,10 b' class VerboseTB(TBTools):' | |||||
684 | lineno=frame_info.lineno, |
|
787 | lineno=frame_info.lineno, | |
685 | ) |
|
788 | ) | |
686 | args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame) |
|
789 | args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame) | |
687 |
|
790 | if frame_info.executing is not None: | ||
688 | func = frame_info.executing.code_qualname() |
|
791 | func = frame_info.executing.code_qualname() | |
|
792 | else: | |||
|
793 | func = '?' | |||
689 | if func == "<module>": |
|
794 | if func == "<module>": | |
690 | call = "" |
|
795 | call = "" | |
691 | else: |
|
796 | else: | |
@@ -732,8 +837,25 b' class VerboseTB(TBTools):' | |||||
732 | lvals = '%s%s' % (indent, em_normal.join(lvals_list)) |
|
837 | lvals = '%s%s' % (indent, em_normal.join(lvals_list)) | |
733 |
|
838 | |||
734 | result = f'{link}{", " if call else ""}{call}\n' |
|
839 | result = f'{link}{", " if call else ""}{call}\n' | |
735 |
|
840 | if frame_info._sd is None: | ||
736 | result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals)) |
|
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 | return result |
|
859 | return result | |
738 |
|
860 | |||
739 | def prepare_header(self, etype, long_version=False): |
|
861 | def prepare_header(self, etype, long_version=False): | |
@@ -802,9 +924,9 b' class VerboseTB(TBTools):' | |||||
802 | frames = [] |
|
924 | frames = [] | |
803 | skipped = 0 |
|
925 | skipped = 0 | |
804 | lastrecord = len(records) - 1 |
|
926 | lastrecord = len(records) - 1 | |
805 | for i, r in enumerate(records): |
|
927 | for i, record in enumerate(records): | |
806 | if not isinstance(r, stack_data.RepeatedFrames) and self.skip_hidden: |
|
928 | if not isinstance(record._sd, stack_data.RepeatedFrames) and self.skip_hidden: | |
807 | if r.frame.f_locals.get("__tracebackhide__", 0) and i != lastrecord: |
|
929 | if record.frame.f_locals.get("__tracebackhide__", 0) and i != lastrecord: | |
808 | skipped += 1 |
|
930 | skipped += 1 | |
809 | continue |
|
931 | continue | |
810 | if skipped: |
|
932 | if skipped: | |
@@ -815,7 +937,7 b' class VerboseTB(TBTools):' | |||||
815 | % (Colors.excName, skipped, ColorsNormal) |
|
937 | % (Colors.excName, skipped, ColorsNormal) | |
816 | ) |
|
938 | ) | |
817 | skipped = 0 |
|
939 | skipped = 0 | |
818 | frames.append(self.format_record(r)) |
|
940 | frames.append(self.format_record(record)) | |
819 | if skipped: |
|
941 | if skipped: | |
820 | Colors = self.Colors # just a shorthand + quicker name lookup |
|
942 | Colors = self.Colors # just a shorthand + quicker name lookup | |
821 | ColorsNormal = Colors.Normal # used a lot |
|
943 | ColorsNormal = Colors.Normal # used a lot | |
@@ -851,7 +973,32 b' class VerboseTB(TBTools):' | |||||
851 | after=after, |
|
973 | after=after, | |
852 | pygments_formatter=formatter, |
|
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 | def structured_traceback( |
|
1003 | def structured_traceback( | |
857 | self, |
|
1004 | self, |
General Comments 0
You need to be logged in to leave comments.
Login now