##// END OF EJS Templates
Backport PR #14010: try to fix tbcode
Matthias Bussonnier -
Show More
@@ -89,20 +89,22 b' Inheritance diagram:'
89 #*****************************************************************************
89 #*****************************************************************************
90
90
91
91
92 import functools
92 import inspect
93 import inspect
93 import linecache
94 import linecache
94 import pydoc
95 import pydoc
95 import sys
96 import sys
96 import time
97 import time
97 import traceback
98 import traceback
99 import types
98 from types import TracebackType
100 from types import TracebackType
99 from typing import Tuple, List, Any, Optional
101 from typing import Any, List, Optional, Tuple
100
102
101 import stack_data
103 import stack_data
102 from stack_data import FrameInfo as SDFrameInfo
103 from pygments.formatters.terminal256 import Terminal256Formatter
104 from pygments.formatters.terminal256 import Terminal256Formatter
104 from pygments.styles import get_style_by_name
105 from pygments.styles import get_style_by_name
105
106
107 import IPython.utils.colorable as colorable
106 # IPython's own modules
108 # IPython's own modules
107 from IPython import get_ipython
109 from IPython import get_ipython
108 from IPython.core import debugger
110 from IPython.core import debugger
@@ -113,8 +115,6 b' from IPython.utils import path as util_path'
113 from IPython.utils import py3compat
115 from IPython.utils import py3compat
114 from IPython.utils.terminal import get_terminal_size
116 from IPython.utils.terminal import get_terminal_size
115
117
116 import IPython.utils.colorable as colorable
117
118 # Globals
118 # Globals
119 # amount of space to put line numbers before verbose tracebacks
119 # amount of space to put line numbers before verbose tracebacks
120 INDENT_SIZE = 8
120 INDENT_SIZE = 8
@@ -135,6 +135,54 b' FAST_THRESHOLD = 10_000'
135 # (SyntaxErrors have to be treated specially because they have no traceback)
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 def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
186 def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
139 """
187 """
140 Format tracebacks lines with pointing arrow, leading numbers...
188 Format tracebacks lines with pointing arrow, leading numbers...
@@ -194,8 +242,8 b' def _simple_format_traceback_lines(lnum, index, lines, Colors, lvals, _line_form'
194 """
242 """
195 numbers_width = INDENT_SIZE - 1
243 numbers_width = INDENT_SIZE - 1
196 res = []
244 res = []
197
198 for i, line in enumerate(lines, lnum - index):
245 for i, line in enumerate(lines, lnum - index):
246 # assert isinstance(line, str)
199 line = py3compat.cast_unicode(line)
247 line = py3compat.cast_unicode(line)
200
248
201 new_line, err = _line_format(line, "str")
249 new_line, err = _line_format(line, "str")
@@ -396,7 +444,7 b' class TBTools(colorable.Colorable):'
396 evalue: Optional[BaseException],
444 evalue: Optional[BaseException],
397 etb: Optional[TracebackType] = None,
445 etb: Optional[TracebackType] = None,
398 tb_offset: Optional[int] = None,
446 tb_offset: Optional[int] = None,
399 context=5,
447 number_of_lines_of_context: int = 5,
400 ):
448 ):
401 """Return a list of traceback frames.
449 """Return a list of traceback frames.
402
450
@@ -497,7 +545,7 b' class ListTB(TBTools):'
497
545
498 exception = self.get_parts_of_chained_exception(evalue)
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 chained_exception_message = (
549 chained_exception_message = (
502 self.prepare_chained_exception_message(evalue.__cause__)[0]
550 self.prepare_chained_exception_message(evalue.__cause__)[0]
503 if evalue is not None
551 if evalue is not None
@@ -509,8 +557,12 b' class ListTB(TBTools):'
509 chained_exceptions_tb_offset = 0
557 chained_exceptions_tb_offset = 0
510 out_list = (
558 out_list = (
511 self.structured_traceback(
559 self.structured_traceback(
512 etype, evalue, (etb, chained_exc_ids),
560 etype,
513 chained_exceptions_tb_offset, context)
561 evalue,
562 (etb, chained_exc_ids), # type: ignore
563 chained_exceptions_tb_offset,
564 context,
565 )
514 + chained_exception_message
566 + chained_exception_message
515 + out_list)
567 + out_list)
516
568
@@ -673,27 +725,41 b' class FrameInfo:'
673 """
725 """
674
726
675 description: Optional[str]
727 description: Optional[str]
676 filename: str
728 filename: Optional[str]
677 lineno: int
729 lineno: Tuple[int]
730 # number of context lines to use
731 context: Optional[int]
678
732
679 @classmethod
733 @classmethod
680 def _from_stack_data_FrameInfo(cls, frame_info):
734 def _from_stack_data_FrameInfo(cls, frame_info):
681 return cls(
735 return cls(
682 getattr(frame_info, "description", None),
736 getattr(frame_info, "description", None),
683 getattr(frame_info, "filename", None),
737 getattr(frame_info, "filename", None), # type: ignore[arg-type]
684 getattr(frame_info, "lineno", None),
738 getattr(frame_info, "lineno", None), # type: ignore[arg-type]
685 getattr(frame_info, "frame", None),
739 getattr(frame_info, "frame", None),
686 getattr(frame_info, "code", None),
740 getattr(frame_info, "code", None),
687 sd=frame_info,
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 self.description = description
756 self.description = description
692 self.filename = filename
757 self.filename = filename
693 self.lineno = lineno
758 self.lineno = lineno
694 self.frame = frame
759 self.frame = frame
695 self.code = code
760 self.code = code
696 self._sd = sd
761 self._sd = sd
762 self.context = context
697
763
698 # self.lines = []
764 # self.lines = []
699 if sd is None:
765 if sd is None:
@@ -848,7 +914,6 b' class VerboseTB(TBTools):'
848
914
849 result = f'{link}{", " if call else ""}{call}\n'
915 result = f'{link}{", " if call else ""}{call}\n'
850 if frame_info._sd is None:
916 if frame_info._sd is None:
851 assert False
852 # fast fallback if file is too long
917 # fast fallback if file is too long
853 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
918 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
854 link = tpl_link % util_path.compress_user(frame_info.filename)
919 link = tpl_link % util_path.compress_user(frame_info.filename)
@@ -858,13 +923,25 b' class VerboseTB(TBTools):'
858 ).format2
923 ).format2
859 first_line = frame_info.code.co_firstlineno
924 first_line = frame_info.code.co_firstlineno
860 current_line = frame_info.lineno[0]
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 return "%s%s" % (
938 return "%s%s" % (
862 level,
939 level,
863 "".join(
940 "".join(
864 _simple_format_traceback_lines(
941 _simple_format_traceback_lines(
865 current_line,
942 current_line,
866 current_line - first_line,
943 index,
867 frame_info.raw_lines,
944 raw_lines,
868 Colors,
945 Colors,
869 lvals,
946 lvals,
870 _line_format,
947 _line_format,
@@ -942,13 +1019,13 b' class VerboseTB(TBTools):'
942 # some locals
1019 # some locals
943 orig_etype = etype
1020 orig_etype = etype
944 try:
1021 try:
945 etype = etype.__name__
1022 etype = etype.__name__ # type: ignore
946 except AttributeError:
1023 except AttributeError:
947 pass
1024 pass
948
1025
949 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1026 tb_offset = self.tb_offset if tb_offset is None else tb_offset
950 assert isinstance(tb_offset, int)
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 records = (
1029 records = (
953 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
1030 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
954 )
1031 )
@@ -1018,23 +1095,34 b' class VerboseTB(TBTools):'
1018 tbs = []
1095 tbs = []
1019 while cf is not None:
1096 while cf is not None:
1020 try:
1097 try:
1021 source_file = inspect.getsourcefile(etb.tb_frame)
1098 mod = inspect.getmodule(cf.tb_frame)
1022 lines, first = inspect.getsourcelines(etb.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 except OSError:
1107 except OSError:
1024 max_len = float("-inf")
1108 max_len = 0
1025 break
1109 max_len = max(max_len, max_len)
1026 max_len = max(max_len, first + len(lines))
1027 tbs.append(cf)
1110 tbs.append(cf)
1028 cf = cf.tb_next
1111 cf = getattr(cf, "tb_next", None)
1029
1112
1030 if max_len > FAST_THRESHOLD:
1113 if max_len > FAST_THRESHOLD:
1031 FIs = []
1114 FIs = []
1032 for tb in tbs:
1115 for tb in tbs:
1033 frame = tb.tb_frame
1116 frame = tb.tb_frame # type: ignore
1034 lineno = (frame.f_lineno,)
1117 lineno = (frame.f_lineno,)
1035 code = frame.f_code
1118 code = frame.f_code
1036 filename = code.co_filename
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 return FIs
1126 return FIs
1039 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1127 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1040 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1128 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
@@ -1044,7 +1132,7 b' class VerboseTB(TBTools):'
1044 self,
1132 self,
1045 etype: type,
1133 etype: type,
1046 evalue: Optional[BaseException],
1134 evalue: Optional[BaseException],
1047 etb: Optional[TracebackType],
1135 etb: Optional[TracebackType] = None,
1048 tb_offset: Optional[int] = None,
1136 tb_offset: Optional[int] = None,
1049 number_of_lines_of_context: int = 5,
1137 number_of_lines_of_context: int = 5,
1050 ):
1138 ):
@@ -1115,8 +1203,8 b' class VerboseTB(TBTools):'
1115 with display_trap:
1203 with display_trap:
1116 self.pdb.reset()
1204 self.pdb.reset()
1117 # Find the right frame so we don't pop up inside ipython itself
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:
1206 if hasattr(self, "tb") and self.tb is not None: # type: ignore[has-type]
1119 etb = self.tb
1207 etb = self.tb # type: ignore[has-type]
1120 else:
1208 else:
1121 etb = self.tb = sys.last_traceback
1209 etb = self.tb = sys.last_traceback
1122 while self.tb is not None and self.tb.tb_next is not None:
1210 while self.tb is not None and self.tb.tb_next is not None:
@@ -1291,24 +1379,23 b' class AutoFormattedTB(FormattedTB):'
1291
1379
1292 def structured_traceback(
1380 def structured_traceback(
1293 self,
1381 self,
1294 etype=None,
1382 etype: type,
1295 value=None,
1383 evalue: Optional[BaseException],
1296 tb=None,
1384 etb: Optional[TracebackType] = None,
1297 tb_offset=None,
1385 tb_offset: Optional[int] = None,
1298 number_of_lines_of_context=5,
1386 number_of_lines_of_context: int = 5,
1299 ):
1387 ):
1300 etype: type
1301 value: BaseException
1302 # tb: TracebackType or tupleof tb types ?
1388 # tb: TracebackType or tupleof tb types ?
1303 if etype is None:
1389 if etype is None:
1304 etype, value, tb = sys.exc_info()
1390 etype, evalue, etb = sys.exc_info()
1305 if isinstance(tb, tuple):
1391 if isinstance(etb, tuple):
1306 # tb is a tuple if this is a chained exception.
1392 # tb is a tuple if this is a chained exception.
1307 self.tb = tb[0]
1393 self.tb = etb[0]
1308 else:
1394 else:
1309 self.tb = tb
1395 self.tb = etb
1310 return FormattedTB.structured_traceback(
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 #---------------------------------------------------------------------------
@@ -1366,7 +1453,7 b' def text_repr(value):'
1366 """Hopefully pretty robust repr equivalent."""
1453 """Hopefully pretty robust repr equivalent."""
1367 # this is pretty horrible but should always return *something*
1454 # this is pretty horrible but should always return *something*
1368 try:
1455 try:
1369 return pydoc.text.repr(value)
1456 return pydoc.text.repr(value) # type: ignore[call-arg]
1370 except KeyboardInterrupt:
1457 except KeyboardInterrupt:
1371 raise
1458 raise
1372 except:
1459 except:
@@ -19,7 +19,7 b' exclude = ['
19 #'IPython/core/interactiveshell.py',
19 #'IPython/core/interactiveshell.py',
20 'IPython/core/magic.py',
20 'IPython/core/magic.py',
21 'IPython/core/profileapp.py',
21 'IPython/core/profileapp.py',
22 'IPython/core/ultratb.py',
22 # 'IPython/core/ultratb.py',
23 'IPython/lib/deepreload.py',
23 'IPython/lib/deepreload.py',
24 'IPython/lib/pretty.py',
24 'IPython/lib/pretty.py',
25 'IPython/sphinxext/ipython_directive.py',
25 'IPython/sphinxext/ipython_directive.py',
General Comments 0
You need to be logged in to leave comments. Login now