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 |
|
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 |
|
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, |
|
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 |
|
|
943 | index, | |
867 |
|
|
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 |
|
|
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 = |
|
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 |
|
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, |
|
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 |
|
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