##// END OF EJS Templates
Make values public
jsbautista -
Show More
@@ -1,1547 +1,1547
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Verbose and colourful traceback formatting.
3 Verbose and colourful traceback formatting.
4
4
5 **ColorTB**
5 **ColorTB**
6
6
7 I've always found it a bit hard to visually parse tracebacks in Python. The
7 I've always found it a bit hard to visually parse tracebacks in Python. The
8 ColorTB class is a solution to that problem. It colors the different parts of a
8 ColorTB class is a solution to that problem. It colors the different parts of a
9 traceback in a manner similar to what you would expect from a syntax-highlighting
9 traceback in a manner similar to what you would expect from a syntax-highlighting
10 text editor.
10 text editor.
11
11
12 Installation instructions for ColorTB::
12 Installation instructions for ColorTB::
13
13
14 import sys,ultratb
14 import sys,ultratb
15 sys.excepthook = ultratb.ColorTB()
15 sys.excepthook = ultratb.ColorTB()
16
16
17 **VerboseTB**
17 **VerboseTB**
18
18
19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
20 of useful info when a traceback occurs. Ping originally had it spit out HTML
20 of useful info when a traceback occurs. Ping originally had it spit out HTML
21 and intended it for CGI programmers, but why should they have all the fun? I
21 and intended it for CGI programmers, but why should they have all the fun? I
22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
23 but kind of neat, and maybe useful for long-running programs that you believe
23 but kind of neat, and maybe useful for long-running programs that you believe
24 are bug-free. If a crash *does* occur in that type of program you want details.
24 are bug-free. If a crash *does* occur in that type of program you want details.
25 Give it a shot--you'll love it or you'll hate it.
25 Give it a shot--you'll love it or you'll hate it.
26
26
27 .. note::
27 .. note::
28
28
29 The Verbose mode prints the variables currently visible where the exception
29 The Verbose mode prints the variables currently visible where the exception
30 happened (shortening their strings if too long). This can potentially be
30 happened (shortening their strings if too long). This can potentially be
31 very slow, if you happen to have a huge data structure whose string
31 very slow, if you happen to have a huge data structure whose string
32 representation is complex to compute. Your computer may appear to freeze for
32 representation is complex to compute. Your computer may appear to freeze for
33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
34 with Ctrl-C (maybe hitting it more than once).
34 with Ctrl-C (maybe hitting it more than once).
35
35
36 If you encounter this kind of situation often, you may want to use the
36 If you encounter this kind of situation often, you may want to use the
37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
38 variables (but otherwise includes the information and context given by
38 variables (but otherwise includes the information and context given by
39 Verbose).
39 Verbose).
40
40
41 .. note::
41 .. note::
42
42
43 The verbose mode print all variables in the stack, which means it can
43 The verbose mode print all variables in the stack, which means it can
44 potentially leak sensitive information like access keys, or unencrypted
44 potentially leak sensitive information like access keys, or unencrypted
45 password.
45 password.
46
46
47 Installation instructions for VerboseTB::
47 Installation instructions for VerboseTB::
48
48
49 import sys,ultratb
49 import sys,ultratb
50 sys.excepthook = ultratb.VerboseTB()
50 sys.excepthook = ultratb.VerboseTB()
51
51
52 Note: Much of the code in this module was lifted verbatim from the standard
52 Note: Much of the code in this module was lifted verbatim from the standard
53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
54
54
55 Color schemes
55 Color schemes
56 -------------
56 -------------
57
57
58 The colors are defined in the class TBTools through the use of the
58 The colors are defined in the class TBTools through the use of the
59 ColorSchemeTable class. Currently the following exist:
59 ColorSchemeTable class. Currently the following exist:
60
60
61 - NoColor: allows all of this module to be used in any terminal (the color
61 - NoColor: allows all of this module to be used in any terminal (the color
62 escapes are just dummy blank strings).
62 escapes are just dummy blank strings).
63
63
64 - Linux: is meant to look good in a terminal like the Linux console (black
64 - Linux: is meant to look good in a terminal like the Linux console (black
65 or very dark background).
65 or very dark background).
66
66
67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
68 in light background terminals.
68 in light background terminals.
69
69
70 - Neutral: a neutral color scheme that should be readable on both light and
70 - Neutral: a neutral color scheme that should be readable on both light and
71 dark background
71 dark background
72
72
73 You can implement other color schemes easily, the syntax is fairly
73 You can implement other color schemes easily, the syntax is fairly
74 self-explanatory. Please send back new schemes you develop to the author for
74 self-explanatory. Please send back new schemes you develop to the author for
75 possible inclusion in future releases.
75 possible inclusion in future releases.
76
76
77 Inheritance diagram:
77 Inheritance diagram:
78
78
79 .. inheritance-diagram:: IPython.core.ultratb
79 .. inheritance-diagram:: IPython.core.ultratb
80 :parts: 3
80 :parts: 3
81 """
81 """
82
82
83 #*****************************************************************************
83 #*****************************************************************************
84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
86 #
86 #
87 # Distributed under the terms of the BSD License. The full license is in
87 # Distributed under the terms of the BSD License. The full license is in
88 # the file COPYING, distributed as part of this software.
88 # the file COPYING, distributed as part of this software.
89 #*****************************************************************************
89 #*****************************************************************************
90
90
91
91
92 from collections.abc import Sequence
92 from collections.abc import Sequence
93 import functools
93 import functools
94 import inspect
94 import inspect
95 import linecache
95 import linecache
96 import pydoc
96 import pydoc
97 import sys
97 import sys
98 import time
98 import time
99 import traceback
99 import traceback
100 import types
100 import types
101 from types import TracebackType
101 from types import TracebackType
102 from typing import Any, List, Optional, Tuple
102 from typing import Any, List, Optional, Tuple
103
103
104 import stack_data
104 import stack_data
105 from pygments.formatters.terminal256 import Terminal256Formatter
105 from pygments.formatters.terminal256 import Terminal256Formatter
106 from pygments.styles import get_style_by_name
106 from pygments.styles import get_style_by_name
107
107
108 import IPython.utils.colorable as colorable
108 import IPython.utils.colorable as colorable
109 # IPython's own modules
109 # IPython's own modules
110 from IPython import get_ipython
110 from IPython import get_ipython
111 from IPython.core import debugger
111 from IPython.core import debugger
112 from IPython.core.display_trap import DisplayTrap
112 from IPython.core.display_trap import DisplayTrap
113 from IPython.core.excolors import exception_colors
113 from IPython.core.excolors import exception_colors
114 from IPython.utils import PyColorize
114 from IPython.utils import PyColorize
115 from IPython.utils import path as util_path
115 from IPython.utils import path as util_path
116 from IPython.utils import py3compat
116 from IPython.utils import py3compat
117 from IPython.utils.terminal import get_terminal_size
117 from IPython.utils.terminal import get_terminal_size
118
118
119 # Globals
119 # Globals
120 # amount of space to put line numbers before verbose tracebacks
120 # amount of space to put line numbers before verbose tracebacks
121 INDENT_SIZE = 8
121 INDENT_SIZE = 8
122
122
123 # Default color scheme. This is used, for example, by the traceback
123 # Default color scheme. This is used, for example, by the traceback
124 # formatter. When running in an actual IPython instance, the user's rc.colors
124 # formatter. When running in an actual IPython instance, the user's rc.colors
125 # value is used, but having a module global makes this functionality available
125 # value is used, but having a module global makes this functionality available
126 # to users of ultratb who are NOT running inside ipython.
126 # to users of ultratb who are NOT running inside ipython.
127 DEFAULT_SCHEME = 'NoColor'
127 DEFAULT_SCHEME = 'NoColor'
128 FAST_THRESHOLD = 10_000
128 FAST_THRESHOLD = 10_000
129
129
130 # ---------------------------------------------------------------------------
130 # ---------------------------------------------------------------------------
131 # Code begins
131 # Code begins
132
132
133 # Helper function -- largely belongs to VerboseTB, but we need the same
133 # Helper function -- largely belongs to VerboseTB, but we need the same
134 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
134 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
135 # can be recognized properly by ipython.el's py-traceback-line-re
135 # can be recognized properly by ipython.el's py-traceback-line-re
136 # (SyntaxErrors have to be treated specially because they have no traceback)
136 # (SyntaxErrors have to be treated specially because they have no traceback)
137
137
138
138
139 @functools.lru_cache()
139 @functools.lru_cache()
140 def count_lines_in_py_file(filename: str) -> int:
140 def count_lines_in_py_file(filename: str) -> int:
141 """
141 """
142 Given a filename, returns the number of lines in the file
142 Given a filename, returns the number of lines in the file
143 if it ends with the extension ".py". Otherwise, returns 0.
143 if it ends with the extension ".py". Otherwise, returns 0.
144 """
144 """
145 if not filename.endswith(".py"):
145 if not filename.endswith(".py"):
146 return 0
146 return 0
147 else:
147 else:
148 try:
148 try:
149 with open(filename, "r") as file:
149 with open(filename, "r") as file:
150 s = sum(1 for line in file)
150 s = sum(1 for line in file)
151 except UnicodeError:
151 except UnicodeError:
152 return 0
152 return 0
153 return s
153 return s
154
154
155 """
155 """
156 Given a frame object, returns the total number of lines in the file
156 Given a frame object, returns the total number of lines in the file
157 if the filename ends with the extension ".py". Otherwise, returns 0.
157 if the filename ends with the extension ".py". Otherwise, returns 0.
158 """
158 """
159
159
160
160
161 def get_line_number_of_frame(frame: types.FrameType) -> int:
161 def get_line_number_of_frame(frame: types.FrameType) -> int:
162 """
162 """
163 Given a frame object, returns the total number of lines in the file
163 Given a frame object, returns the total number of lines in the file
164 containing the frame's code object, or the number of lines in the
164 containing the frame's code object, or the number of lines in the
165 frame's source code if the file is not available.
165 frame's source code if the file is not available.
166
166
167 Parameters
167 Parameters
168 ----------
168 ----------
169 frame : FrameType
169 frame : FrameType
170 The frame object whose line number is to be determined.
170 The frame object whose line number is to be determined.
171
171
172 Returns
172 Returns
173 -------
173 -------
174 int
174 int
175 The total number of lines in the file containing the frame's
175 The total number of lines in the file containing the frame's
176 code object, or the number of lines in the frame's source code
176 code object, or the number of lines in the frame's source code
177 if the file is not available.
177 if the file is not available.
178 """
178 """
179 filename = frame.f_code.co_filename
179 filename = frame.f_code.co_filename
180 if filename is None:
180 if filename is None:
181 print("No file....")
181 print("No file....")
182 lines, first = inspect.getsourcelines(frame)
182 lines, first = inspect.getsourcelines(frame)
183 return first + len(lines)
183 return first + len(lines)
184 return count_lines_in_py_file(filename)
184 return count_lines_in_py_file(filename)
185
185
186
186
187 def _safe_string(value, what, func=str):
187 def _safe_string(value, what, func=str):
188 # Copied from cpython/Lib/traceback.py
188 # Copied from cpython/Lib/traceback.py
189 try:
189 try:
190 return func(value)
190 return func(value)
191 except:
191 except:
192 return f"<{what} {func.__name__}() failed>"
192 return f"<{what} {func.__name__}() failed>"
193
193
194
194
195 def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
195 def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
196 """
196 """
197 Format tracebacks lines with pointing arrow, leading numbers...
197 Format tracebacks lines with pointing arrow, leading numbers...
198
198
199 Parameters
199 Parameters
200 ----------
200 ----------
201 lines : list[Line]
201 lines : list[Line]
202 Colors
202 Colors
203 ColorScheme used.
203 ColorScheme used.
204 lvals : str
204 lvals : str
205 Values of local variables, already colored, to inject just after the error line.
205 Values of local variables, already colored, to inject just after the error line.
206 """
206 """
207 numbers_width = INDENT_SIZE - 1
207 numbers_width = INDENT_SIZE - 1
208 res = []
208 res = []
209
209
210 for stack_line in lines:
210 for stack_line in lines:
211 if stack_line is stack_data.LINE_GAP:
211 if stack_line is stack_data.LINE_GAP:
212 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
212 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
213 continue
213 continue
214
214
215 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n'
215 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n'
216 lineno = stack_line.lineno
216 lineno = stack_line.lineno
217 if stack_line.is_current:
217 if stack_line.is_current:
218 # This is the line with the error
218 # This is the line with the error
219 pad = numbers_width - len(str(lineno))
219 pad = numbers_width - len(str(lineno))
220 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
220 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
221 start_color = Colors.linenoEm
221 start_color = Colors.linenoEm
222 else:
222 else:
223 num = '%*s' % (numbers_width, lineno)
223 num = '%*s' % (numbers_width, lineno)
224 start_color = Colors.lineno
224 start_color = Colors.lineno
225
225
226 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line)
226 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line)
227
227
228 res.append(line)
228 res.append(line)
229 if lvals and stack_line.is_current:
229 if lvals and stack_line.is_current:
230 res.append(lvals + '\n')
230 res.append(lvals + '\n')
231 return res
231 return res
232
232
233 def _simple_format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):
233 def _simple_format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):
234 """
234 """
235 Format tracebacks lines with pointing arrow, leading numbers...
235 Format tracebacks lines with pointing arrow, leading numbers...
236
236
237 Parameters
237 Parameters
238 ==========
238 ==========
239
239
240 lnum: int
240 lnum: int
241 number of the target line of code.
241 number of the target line of code.
242 index: int
242 index: int
243 which line in the list should be highlighted.
243 which line in the list should be highlighted.
244 lines: list[string]
244 lines: list[string]
245 Colors:
245 Colors:
246 ColorScheme used.
246 ColorScheme used.
247 lvals: bytes
247 lvals: bytes
248 Values of local variables, already colored, to inject just after the error line.
248 Values of local variables, already colored, to inject just after the error line.
249 _line_format: f (str) -> (str, bool)
249 _line_format: f (str) -> (str, bool)
250 return (colorized version of str, failure to do so)
250 return (colorized version of str, failure to do so)
251 """
251 """
252 numbers_width = INDENT_SIZE - 1
252 numbers_width = INDENT_SIZE - 1
253 res = []
253 res = []
254 for i, line in enumerate(lines, lnum - index):
254 for i, line in enumerate(lines, lnum - index):
255 # assert isinstance(line, str)
255 # assert isinstance(line, str)
256 line = py3compat.cast_unicode(line)
256 line = py3compat.cast_unicode(line)
257
257
258 new_line, err = _line_format(line, "str")
258 new_line, err = _line_format(line, "str")
259 if not err:
259 if not err:
260 line = new_line
260 line = new_line
261
261
262 if i == lnum:
262 if i == lnum:
263 # This is the line with the error
263 # This is the line with the error
264 pad = numbers_width - len(str(i))
264 pad = numbers_width - len(str(i))
265 num = "%s%s" % (debugger.make_arrow(pad), str(lnum))
265 num = "%s%s" % (debugger.make_arrow(pad), str(lnum))
266 line = "%s%s%s %s%s" % (
266 line = "%s%s%s %s%s" % (
267 Colors.linenoEm,
267 Colors.linenoEm,
268 num,
268 num,
269 Colors.line,
269 Colors.line,
270 line,
270 line,
271 Colors.Normal,
271 Colors.Normal,
272 )
272 )
273 else:
273 else:
274 num = "%*s" % (numbers_width, i)
274 num = "%*s" % (numbers_width, i)
275 line = "%s%s%s %s" % (Colors.lineno, num, Colors.Normal, line)
275 line = "%s%s%s %s" % (Colors.lineno, num, Colors.Normal, line)
276
276
277 res.append(line)
277 res.append(line)
278 if lvals and i == lnum:
278 if lvals and i == lnum:
279 res.append(lvals + "\n")
279 res.append(lvals + "\n")
280 return res
280 return res
281
281
282
282
283 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
283 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
284 """
284 """
285 Format filename lines with custom formatting from caching compiler or `File *.py` by default
285 Format filename lines with custom formatting from caching compiler or `File *.py` by default
286
286
287 Parameters
287 Parameters
288 ----------
288 ----------
289 file : str
289 file : str
290 ColorFilename
290 ColorFilename
291 ColorScheme's filename coloring to be used.
291 ColorScheme's filename coloring to be used.
292 ColorNormal
292 ColorNormal
293 ColorScheme's normal coloring to be used.
293 ColorScheme's normal coloring to be used.
294 """
294 """
295 ipinst = get_ipython()
295 ipinst = get_ipython()
296 if (
296 if (
297 ipinst is not None
297 ipinst is not None
298 and (data := ipinst.compile.format_code_name(file)) is not None
298 and (data := ipinst.compile.format_code_name(file)) is not None
299 ):
299 ):
300 label, name = data
300 label, name = data
301 if lineno is None:
301 if lineno is None:
302 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
302 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
303 else:
303 else:
304 tpl_link = (
304 tpl_link = (
305 f"{{label}} {ColorFilename}{{name}}, line {{lineno}}{ColorNormal}"
305 f"{{label}} {ColorFilename}{{name}}, line {{lineno}}{ColorNormal}"
306 )
306 )
307 else:
307 else:
308 label = "File"
308 label = "File"
309 name = util_path.compress_user(
309 name = util_path.compress_user(
310 py3compat.cast_unicode(file, util_path.fs_encoding)
310 py3compat.cast_unicode(file, util_path.fs_encoding)
311 )
311 )
312 if lineno is None:
312 if lineno is None:
313 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
313 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
314 else:
314 else:
315 # can we make this the more friendly ", line {{lineno}}", or do we need to preserve the formatting with the colon?
315 # can we make this the more friendly ", line {{lineno}}", or do we need to preserve the formatting with the colon?
316 tpl_link = f"{{label}} {ColorFilename}{{name}}:{{lineno}}{ColorNormal}"
316 tpl_link = f"{{label}} {ColorFilename}{{name}}:{{lineno}}{ColorNormal}"
317
317
318 return tpl_link.format(label=label, name=name, lineno=lineno)
318 return tpl_link.format(label=label, name=name, lineno=lineno)
319
319
320 #---------------------------------------------------------------------------
320 #---------------------------------------------------------------------------
321 # Module classes
321 # Module classes
322 class TBTools(colorable.Colorable):
322 class TBTools(colorable.Colorable):
323 """Basic tools used by all traceback printer classes."""
323 """Basic tools used by all traceback printer classes."""
324
324
325 # Number of frames to skip when reporting tracebacks
325 # Number of frames to skip when reporting tracebacks
326 tb_offset = 0
326 tb_offset = 0
327
327
328 def __init__(
328 def __init__(
329 self,
329 self,
330 color_scheme="NoColor",
330 color_scheme="NoColor",
331 call_pdb=False,
331 call_pdb=False,
332 ostream=None,
332 ostream=None,
333 parent=None,
333 parent=None,
334 config=None,
334 config=None,
335 *,
335 *,
336 debugger_cls=None,
336 debugger_cls=None,
337 ):
337 ):
338 # Whether to call the interactive pdb debugger after printing
338 # Whether to call the interactive pdb debugger after printing
339 # tracebacks or not
339 # tracebacks or not
340 super(TBTools, self).__init__(parent=parent, config=config)
340 super(TBTools, self).__init__(parent=parent, config=config)
341 self.call_pdb = call_pdb
341 self.call_pdb = call_pdb
342
342
343 # Output stream to write to. Note that we store the original value in
343 # Output stream to write to. Note that we store the original value in
344 # a private attribute and then make the public ostream a property, so
344 # a private attribute and then make the public ostream a property, so
345 # that we can delay accessing sys.stdout until runtime. The way
345 # that we can delay accessing sys.stdout until runtime. The way
346 # things are written now, the sys.stdout object is dynamically managed
346 # things are written now, the sys.stdout object is dynamically managed
347 # so a reference to it should NEVER be stored statically. This
347 # so a reference to it should NEVER be stored statically. This
348 # property approach confines this detail to a single location, and all
348 # property approach confines this detail to a single location, and all
349 # subclasses can simply access self.ostream for writing.
349 # subclasses can simply access self.ostream for writing.
350 self._ostream = ostream
350 self._ostream = ostream
351
351
352 # Create color table
352 # Create color table
353 self.color_scheme_table = exception_colors()
353 self.color_scheme_table = exception_colors()
354
354
355 self.set_colors(color_scheme)
355 self.set_colors(color_scheme)
356 self.old_scheme = color_scheme # save initial value for toggles
356 self.old_scheme = color_scheme # save initial value for toggles
357 self.debugger_cls = debugger_cls or debugger.Pdb
357 self.debugger_cls = debugger_cls or debugger.Pdb
358
358
359 if call_pdb:
359 if call_pdb:
360 self.pdb = self.debugger_cls()
360 self.pdb = self.debugger_cls()
361 else:
361 else:
362 self.pdb = None
362 self.pdb = None
363
363
364 def _get_ostream(self):
364 def _get_ostream(self):
365 """Output stream that exceptions are written to.
365 """Output stream that exceptions are written to.
366
366
367 Valid values are:
367 Valid values are:
368
368
369 - None: the default, which means that IPython will dynamically resolve
369 - None: the default, which means that IPython will dynamically resolve
370 to sys.stdout. This ensures compatibility with most tools, including
370 to sys.stdout. This ensures compatibility with most tools, including
371 Windows (where plain stdout doesn't recognize ANSI escapes).
371 Windows (where plain stdout doesn't recognize ANSI escapes).
372
372
373 - Any object with 'write' and 'flush' attributes.
373 - Any object with 'write' and 'flush' attributes.
374 """
374 """
375 return sys.stdout if self._ostream is None else self._ostream
375 return sys.stdout if self._ostream is None else self._ostream
376
376
377 def _set_ostream(self, val):
377 def _set_ostream(self, val):
378 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
378 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
379 self._ostream = val
379 self._ostream = val
380
380
381 ostream = property(_get_ostream, _set_ostream)
381 ostream = property(_get_ostream, _set_ostream)
382
382
383 @staticmethod
383 @staticmethod
384 def _get_chained_exception(exception_value):
384 def _get_chained_exception(exception_value):
385 cause = getattr(exception_value, "__cause__", None)
385 cause = getattr(exception_value, "__cause__", None)
386 if cause:
386 if cause:
387 return cause
387 return cause
388 if getattr(exception_value, "__suppress_context__", False):
388 if getattr(exception_value, "__suppress_context__", False):
389 return None
389 return None
390 return getattr(exception_value, "__context__", None)
390 return getattr(exception_value, "__context__", None)
391
391
392 def get_parts_of_chained_exception(
392 def get_parts_of_chained_exception(
393 self, evalue
393 self, evalue
394 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
394 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
395 chained_evalue = self._get_chained_exception(evalue)
395 chained_evalue = self._get_chained_exception(evalue)
396
396
397 if chained_evalue:
397 if chained_evalue:
398 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
398 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
399 return None
399 return None
400
400
401 def prepare_chained_exception_message(self, cause) -> List[Any]:
401 def prepare_chained_exception_message(self, cause) -> List[Any]:
402 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
402 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
403 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
403 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
404
404
405 if cause:
405 if cause:
406 message = [[direct_cause]]
406 message = [[direct_cause]]
407 else:
407 else:
408 message = [[exception_during_handling]]
408 message = [[exception_during_handling]]
409 return message
409 return message
410
410
411 @property
411 @property
412 def has_colors(self) -> bool:
412 def has_colors(self) -> bool:
413 return self.color_scheme_table.active_scheme_name.lower() != "nocolor"
413 return self.color_scheme_table.active_scheme_name.lower() != "nocolor"
414
414
415 def set_colors(self, *args, **kw):
415 def set_colors(self, *args, **kw):
416 """Shorthand access to the color table scheme selector method."""
416 """Shorthand access to the color table scheme selector method."""
417
417
418 # Set own color table
418 # Set own color table
419 self.color_scheme_table.set_active_scheme(*args, **kw)
419 self.color_scheme_table.set_active_scheme(*args, **kw)
420 # for convenience, set Colors to the active scheme
420 # for convenience, set Colors to the active scheme
421 self.Colors = self.color_scheme_table.active_colors
421 self.Colors = self.color_scheme_table.active_colors
422 # Also set colors of debugger
422 # Also set colors of debugger
423 if hasattr(self, 'pdb') and self.pdb is not None:
423 if hasattr(self, 'pdb') and self.pdb is not None:
424 self.pdb.set_colors(*args, **kw)
424 self.pdb.set_colors(*args, **kw)
425
425
426 def color_toggle(self):
426 def color_toggle(self):
427 """Toggle between the currently active color scheme and NoColor."""
427 """Toggle between the currently active color scheme and NoColor."""
428
428
429 if self.color_scheme_table.active_scheme_name == 'NoColor':
429 if self.color_scheme_table.active_scheme_name == 'NoColor':
430 self.color_scheme_table.set_active_scheme(self.old_scheme)
430 self.color_scheme_table.set_active_scheme(self.old_scheme)
431 self.Colors = self.color_scheme_table.active_colors
431 self.Colors = self.color_scheme_table.active_colors
432 else:
432 else:
433 self.old_scheme = self.color_scheme_table.active_scheme_name
433 self.old_scheme = self.color_scheme_table.active_scheme_name
434 self.color_scheme_table.set_active_scheme('NoColor')
434 self.color_scheme_table.set_active_scheme('NoColor')
435 self.Colors = self.color_scheme_table.active_colors
435 self.Colors = self.color_scheme_table.active_colors
436
436
437 def stb2text(self, stb):
437 def stb2text(self, stb):
438 """Convert a structured traceback (a list) to a string."""
438 """Convert a structured traceback (a list) to a string."""
439 return '\n'.join(stb)
439 return '\n'.join(stb)
440
440
441 def text(self, etype, value, tb, tb_offset: Optional[int] = None, context=5):
441 def text(self, etype, value, tb, tb_offset: Optional[int] = None, context=5):
442 """Return formatted traceback.
442 """Return formatted traceback.
443
443
444 Subclasses may override this if they add extra arguments.
444 Subclasses may override this if they add extra arguments.
445 """
445 """
446 tb_list = self.structured_traceback(etype, value, tb,
446 tb_list = self.structured_traceback(etype, value, tb,
447 tb_offset, context)
447 tb_offset, context)
448 return self.stb2text(tb_list)
448 return self.stb2text(tb_list)
449
449
450 def structured_traceback(
450 def structured_traceback(
451 self,
451 self,
452 etype: type,
452 etype: type,
453 evalue: Optional[BaseException],
453 evalue: Optional[BaseException],
454 etb: Optional[TracebackType] = None,
454 etb: Optional[TracebackType] = None,
455 tb_offset: Optional[int] = None,
455 tb_offset: Optional[int] = None,
456 number_of_lines_of_context: int = 5,
456 number_of_lines_of_context: int = 5,
457 ):
457 ):
458 """Return a list of traceback frames.
458 """Return a list of traceback frames.
459
459
460 Must be implemented by each class.
460 Must be implemented by each class.
461 """
461 """
462 raise NotImplementedError()
462 raise NotImplementedError()
463
463
464
464
465 #---------------------------------------------------------------------------
465 #---------------------------------------------------------------------------
466 class ListTB(TBTools):
466 class ListTB(TBTools):
467 """Print traceback information from a traceback list, with optional color.
467 """Print traceback information from a traceback list, with optional color.
468
468
469 Calling requires 3 arguments: (etype, evalue, elist)
469 Calling requires 3 arguments: (etype, evalue, elist)
470 as would be obtained by::
470 as would be obtained by::
471
471
472 etype, evalue, tb = sys.exc_info()
472 etype, evalue, tb = sys.exc_info()
473 if tb:
473 if tb:
474 elist = traceback.extract_tb(tb)
474 elist = traceback.extract_tb(tb)
475 else:
475 else:
476 elist = None
476 elist = None
477
477
478 It can thus be used by programs which need to process the traceback before
478 It can thus be used by programs which need to process the traceback before
479 printing (such as console replacements based on the code module from the
479 printing (such as console replacements based on the code module from the
480 standard library).
480 standard library).
481
481
482 Because they are meant to be called without a full traceback (only a
482 Because they are meant to be called without a full traceback (only a
483 list), instances of this class can't call the interactive pdb debugger."""
483 list), instances of this class can't call the interactive pdb debugger."""
484
484
485
485
486 def __call__(self, etype, value, elist):
486 def __call__(self, etype, value, elist):
487 self.ostream.flush()
487 self.ostream.flush()
488 self.ostream.write(self.text(etype, value, elist))
488 self.ostream.write(self.text(etype, value, elist))
489 self.ostream.write('\n')
489 self.ostream.write('\n')
490
490
491 def _extract_tb(self, tb):
491 def _extract_tb(self, tb):
492 if tb:
492 if tb:
493 return traceback.extract_tb(tb)
493 return traceback.extract_tb(tb)
494 else:
494 else:
495 return None
495 return None
496
496
497 def structured_traceback(
497 def structured_traceback(
498 self,
498 self,
499 etype: type,
499 etype: type,
500 evalue: Optional[BaseException],
500 evalue: Optional[BaseException],
501 etb: Optional[TracebackType] = None,
501 etb: Optional[TracebackType] = None,
502 tb_offset: Optional[int] = None,
502 tb_offset: Optional[int] = None,
503 context=5,
503 context=5,
504 ):
504 ):
505 """Return a color formatted string with the traceback info.
505 """Return a color formatted string with the traceback info.
506
506
507 Parameters
507 Parameters
508 ----------
508 ----------
509 etype : exception type
509 etype : exception type
510 Type of the exception raised.
510 Type of the exception raised.
511 evalue : object
511 evalue : object
512 Data stored in the exception
512 Data stored in the exception
513 etb : list | TracebackType | None
513 etb : list | TracebackType | None
514 If list: List of frames, see class docstring for details.
514 If list: List of frames, see class docstring for details.
515 If Traceback: Traceback of the exception.
515 If Traceback: Traceback of the exception.
516 tb_offset : int, optional
516 tb_offset : int, optional
517 Number of frames in the traceback to skip. If not given, the
517 Number of frames in the traceback to skip. If not given, the
518 instance evalue is used (set in constructor).
518 instance evalue is used (set in constructor).
519 context : int, optional
519 context : int, optional
520 Number of lines of context information to print.
520 Number of lines of context information to print.
521
521
522 Returns
522 Returns
523 -------
523 -------
524 String with formatted exception.
524 String with formatted exception.
525 """
525 """
526 # This is a workaround to get chained_exc_ids in recursive calls
526 # This is a workaround to get chained_exc_ids in recursive calls
527 # etb should not be a tuple if structured_traceback is not recursive
527 # etb should not be a tuple if structured_traceback is not recursive
528 if isinstance(etb, tuple):
528 if isinstance(etb, tuple):
529 etb, chained_exc_ids = etb
529 etb, chained_exc_ids = etb
530 else:
530 else:
531 chained_exc_ids = set()
531 chained_exc_ids = set()
532
532
533 if isinstance(etb, list):
533 if isinstance(etb, list):
534 elist = etb
534 elist = etb
535 elif etb is not None:
535 elif etb is not None:
536 elist = self._extract_tb(etb)
536 elist = self._extract_tb(etb)
537 else:
537 else:
538 elist = []
538 elist = []
539 tb_offset = self.tb_offset if tb_offset is None else tb_offset
539 tb_offset = self.tb_offset if tb_offset is None else tb_offset
540 assert isinstance(tb_offset, int)
540 assert isinstance(tb_offset, int)
541 Colors = self.Colors
541 Colors = self.Colors
542 out_list = []
542 out_list = []
543 if elist:
543 if elist:
544
544
545 if tb_offset and len(elist) > tb_offset:
545 if tb_offset and len(elist) > tb_offset:
546 elist = elist[tb_offset:]
546 elist = elist[tb_offset:]
547
547
548 out_list.append('Traceback %s(most recent call last)%s:' %
548 out_list.append('Traceback %s(most recent call last)%s:' %
549 (Colors.normalEm, Colors.Normal) + '\n')
549 (Colors.normalEm, Colors.Normal) + '\n')
550 out_list.extend(self._format_list(elist))
550 out_list.extend(self._format_list(elist))
551 # The exception info should be a single entry in the list.
551 # The exception info should be a single entry in the list.
552 lines = ''.join(self._format_exception_only(etype, evalue))
552 lines = ''.join(self._format_exception_only(etype, evalue))
553 out_list.append(lines)
553 out_list.append(lines)
554
554
555 # Find chained exceptions if we have a traceback (not for exception-only mode)
555 # Find chained exceptions if we have a traceback (not for exception-only mode)
556 if etb is not None:
556 if etb is not None:
557 exception = self.get_parts_of_chained_exception(evalue)
557 exception = self.get_parts_of_chained_exception(evalue)
558
558
559 if exception and (id(exception[1]) not in chained_exc_ids):
559 if exception and (id(exception[1]) not in chained_exc_ids):
560 chained_exception_message = (
560 chained_exception_message = (
561 self.prepare_chained_exception_message(evalue.__cause__)[0]
561 self.prepare_chained_exception_message(evalue.__cause__)[0]
562 if evalue is not None
562 if evalue is not None
563 else ""
563 else ""
564 )
564 )
565 etype, evalue, etb = exception
565 etype, evalue, etb = exception
566 # Trace exception to avoid infinite 'cause' loop
566 # Trace exception to avoid infinite 'cause' loop
567 chained_exc_ids.add(id(exception[1]))
567 chained_exc_ids.add(id(exception[1]))
568 chained_exceptions_tb_offset = 0
568 chained_exceptions_tb_offset = 0
569 out_list = (
569 out_list = (
570 self.structured_traceback(
570 self.structured_traceback(
571 etype,
571 etype,
572 evalue,
572 evalue,
573 (etb, chained_exc_ids), # type: ignore
573 (etb, chained_exc_ids), # type: ignore
574 chained_exceptions_tb_offset,
574 chained_exceptions_tb_offset,
575 context,
575 context,
576 )
576 )
577 + chained_exception_message
577 + chained_exception_message
578 + out_list
578 + out_list
579 )
579 )
580
580
581 return out_list
581 return out_list
582
582
583 def _format_list(self, extracted_list):
583 def _format_list(self, extracted_list):
584 """Format a list of traceback entry tuples for printing.
584 """Format a list of traceback entry tuples for printing.
585
585
586 Given a list of tuples as returned by extract_tb() or
586 Given a list of tuples as returned by extract_tb() or
587 extract_stack(), return a list of strings ready for printing.
587 extract_stack(), return a list of strings ready for printing.
588 Each string in the resulting list corresponds to the item with the
588 Each string in the resulting list corresponds to the item with the
589 same index in the argument list. Each string ends in a newline;
589 same index in the argument list. Each string ends in a newline;
590 the strings may contain internal newlines as well, for those items
590 the strings may contain internal newlines as well, for those items
591 whose source text line is not None.
591 whose source text line is not None.
592
592
593 Lifted almost verbatim from traceback.py
593 Lifted almost verbatim from traceback.py
594 """
594 """
595
595
596 Colors = self.Colors
596 Colors = self.Colors
597 output_list = []
597 output_list = []
598 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
598 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
599 normalCol, nameCol, fileCol, lineCol = (
599 normalCol, nameCol, fileCol, lineCol = (
600 # Emphasize the last entry
600 # Emphasize the last entry
601 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
601 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
602 if ind == len(extracted_list) - 1
602 if ind == len(extracted_list) - 1
603 else (Colors.Normal, Colors.name, Colors.filename, "")
603 else (Colors.Normal, Colors.name, Colors.filename, "")
604 )
604 )
605
605
606 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
606 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
607 item = f"{normalCol} {fns}"
607 item = f"{normalCol} {fns}"
608
608
609 if name != "<module>":
609 if name != "<module>":
610 item += f" in {nameCol}{name}{normalCol}\n"
610 item += f" in {nameCol}{name}{normalCol}\n"
611 else:
611 else:
612 item += "\n"
612 item += "\n"
613 if line:
613 if line:
614 item += f"{lineCol} {line.strip()}{normalCol}\n"
614 item += f"{lineCol} {line.strip()}{normalCol}\n"
615 output_list.append(item)
615 output_list.append(item)
616
616
617 return output_list
617 return output_list
618
618
619 def _format_exception_only(self, etype, value):
619 def _format_exception_only(self, etype, value):
620 """Format the exception part of a traceback.
620 """Format the exception part of a traceback.
621
621
622 The arguments are the exception type and value such as given by
622 The arguments are the exception type and value such as given by
623 sys.exc_info()[:2]. The return value is a list of strings, each ending
623 sys.exc_info()[:2]. The return value is a list of strings, each ending
624 in a newline. Normally, the list contains a single string; however,
624 in a newline. Normally, the list contains a single string; however,
625 for SyntaxError exceptions, it contains several lines that (when
625 for SyntaxError exceptions, it contains several lines that (when
626 printed) display detailed information about where the syntax error
626 printed) display detailed information about where the syntax error
627 occurred. The message indicating which exception occurred is the
627 occurred. The message indicating which exception occurred is the
628 always last string in the list.
628 always last string in the list.
629
629
630 Also lifted nearly verbatim from traceback.py
630 Also lifted nearly verbatim from traceback.py
631 """
631 """
632 have_filedata = False
632 have_filedata = False
633 Colors = self.Colors
633 Colors = self.Colors
634 output_list = []
634 output_list = []
635 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
635 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
636 if value is None:
636 if value is None:
637 # Not sure if this can still happen in Python 2.6 and above
637 # Not sure if this can still happen in Python 2.6 and above
638 output_list.append(stype + "\n")
638 output_list.append(stype + "\n")
639 else:
639 else:
640 if issubclass(etype, SyntaxError):
640 if issubclass(etype, SyntaxError):
641 have_filedata = True
641 have_filedata = True
642 if not value.filename: value.filename = "<string>"
642 if not value.filename: value.filename = "<string>"
643 if value.lineno:
643 if value.lineno:
644 lineno = value.lineno
644 lineno = value.lineno
645 textline = linecache.getline(value.filename, value.lineno)
645 textline = linecache.getline(value.filename, value.lineno)
646 else:
646 else:
647 lineno = "unknown"
647 lineno = "unknown"
648 textline = ""
648 textline = ""
649 output_list.append(
649 output_list.append(
650 "%s %s%s\n"
650 "%s %s%s\n"
651 % (
651 % (
652 Colors.normalEm,
652 Colors.normalEm,
653 _format_filename(
653 _format_filename(
654 value.filename,
654 value.filename,
655 Colors.filenameEm,
655 Colors.filenameEm,
656 Colors.normalEm,
656 Colors.normalEm,
657 lineno=(None if lineno == "unknown" else lineno),
657 lineno=(None if lineno == "unknown" else lineno),
658 ),
658 ),
659 Colors.Normal,
659 Colors.Normal,
660 )
660 )
661 )
661 )
662 if textline == "":
662 if textline == "":
663 textline = py3compat.cast_unicode(value.text, "utf-8")
663 textline = py3compat.cast_unicode(value.text, "utf-8")
664
664
665 if textline is not None:
665 if textline is not None:
666 i = 0
666 i = 0
667 while i < len(textline) and textline[i].isspace():
667 while i < len(textline) and textline[i].isspace():
668 i += 1
668 i += 1
669 output_list.append(
669 output_list.append(
670 "%s %s%s\n" % (Colors.line, textline.strip(), Colors.Normal)
670 "%s %s%s\n" % (Colors.line, textline.strip(), Colors.Normal)
671 )
671 )
672 if value.offset is not None:
672 if value.offset is not None:
673 s = ' '
673 s = ' '
674 for c in textline[i:value.offset - 1]:
674 for c in textline[i:value.offset - 1]:
675 if c.isspace():
675 if c.isspace():
676 s += c
676 s += c
677 else:
677 else:
678 s += " "
678 s += " "
679 output_list.append(
679 output_list.append(
680 "%s%s^%s\n" % (Colors.caret, s, Colors.Normal)
680 "%s%s^%s\n" % (Colors.caret, s, Colors.Normal)
681 )
681 )
682
682
683 try:
683 try:
684 s = value.msg
684 s = value.msg
685 except Exception:
685 except Exception:
686 s = self._some_str(value)
686 s = self._some_str(value)
687 if s:
687 if s:
688 output_list.append(
688 output_list.append(
689 "%s%s:%s %s\n" % (stype, Colors.excName, Colors.Normal, s)
689 "%s%s:%s %s\n" % (stype, Colors.excName, Colors.Normal, s)
690 )
690 )
691 else:
691 else:
692 output_list.append("%s\n" % stype)
692 output_list.append("%s\n" % stype)
693
693
694 # PEP-678 notes
694 # PEP-678 notes
695 output_list.extend(f"{x}\n" for x in getattr(value, "__notes__", []))
695 output_list.extend(f"{x}\n" for x in getattr(value, "__notes__", []))
696
696
697 # sync with user hooks
697 # sync with user hooks
698 if have_filedata:
698 if have_filedata:
699 ipinst = get_ipython()
699 ipinst = get_ipython()
700 if ipinst is not None:
700 if ipinst is not None:
701 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
701 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
702
702
703 return output_list
703 return output_list
704
704
705 def get_exception_only(self, etype, value):
705 def get_exception_only(self, etype, value):
706 """Only print the exception type and message, without a traceback.
706 """Only print the exception type and message, without a traceback.
707
707
708 Parameters
708 Parameters
709 ----------
709 ----------
710 etype : exception type
710 etype : exception type
711 value : exception value
711 value : exception value
712 """
712 """
713 return ListTB.structured_traceback(self, etype, value)
713 return ListTB.structured_traceback(self, etype, value)
714
714
715 def show_exception_only(self, etype, evalue):
715 def show_exception_only(self, etype, evalue):
716 """Only print the exception type and message, without a traceback.
716 """Only print the exception type and message, without a traceback.
717
717
718 Parameters
718 Parameters
719 ----------
719 ----------
720 etype : exception type
720 etype : exception type
721 evalue : exception value
721 evalue : exception value
722 """
722 """
723 # This method needs to use __call__ from *this* class, not the one from
723 # This method needs to use __call__ from *this* class, not the one from
724 # a subclass whose signature or behavior may be different
724 # a subclass whose signature or behavior may be different
725 ostream = self.ostream
725 ostream = self.ostream
726 ostream.flush()
726 ostream.flush()
727 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
727 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
728 ostream.flush()
728 ostream.flush()
729
729
730 def _some_str(self, value):
730 def _some_str(self, value):
731 # Lifted from traceback.py
731 # Lifted from traceback.py
732 try:
732 try:
733 return py3compat.cast_unicode(str(value))
733 return py3compat.cast_unicode(str(value))
734 except:
734 except:
735 return u'<unprintable %s object>' % type(value).__name__
735 return u'<unprintable %s object>' % type(value).__name__
736
736
737
737
738 class FrameInfo:
738 class FrameInfo:
739 """
739 """
740 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
740 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
741 really long frames.
741 really long frames.
742 """
742 """
743
743
744 description: Optional[str]
744 description: Optional[str]
745 filename: Optional[str]
745 filename: Optional[str]
746 lineno: Tuple[int]
746 lineno: Tuple[int]
747 # number of context lines to use
747 # number of context lines to use
748 context: Optional[int]
748 context: Optional[int]
749 raw_lines: List[str]
749 raw_lines: List[str]
750
750
751 @classmethod
751 @classmethod
752 def _from_stack_data_FrameInfo(cls, frame_info):
752 def _from_stack_data_FrameInfo(cls, frame_info):
753 return cls(
753 return cls(
754 getattr(frame_info, "description", None),
754 getattr(frame_info, "description", None),
755 getattr(frame_info, "filename", None), # type: ignore[arg-type]
755 getattr(frame_info, "filename", None), # type: ignore[arg-type]
756 getattr(frame_info, "lineno", None), # type: ignore[arg-type]
756 getattr(frame_info, "lineno", None), # type: ignore[arg-type]
757 getattr(frame_info, "frame", None),
757 getattr(frame_info, "frame", None),
758 getattr(frame_info, "code", None),
758 getattr(frame_info, "code", None),
759 sd=frame_info,
759 sd=frame_info,
760 context=None,
760 context=None,
761 )
761 )
762
762
763 def __init__(
763 def __init__(
764 self,
764 self,
765 description: Optional[str],
765 description: Optional[str],
766 filename: str,
766 filename: str,
767 lineno: Tuple[int],
767 lineno: Tuple[int],
768 frame,
768 frame,
769 code,
769 code,
770 *,
770 *,
771 sd=None,
771 sd=None,
772 context=None,
772 context=None,
773 ):
773 ):
774 self.description = description
774 self.description = description
775 self.filename = filename
775 self.filename = filename
776 self.lineno = lineno
776 self.lineno = lineno
777 self.frame = frame
777 self.frame = frame
778 self.code = code
778 self.code = code
779 self._sd = sd
779 self._sd = sd
780 self.context = context
780 self.context = context
781
781
782 # self.lines = []
782 # self.lines = []
783 if sd is None:
783 if sd is None:
784 try:
784 try:
785 # return a list of source lines and a starting line number
785 # return a list of source lines and a starting line number
786 self.raw_lines = inspect.getsourcelines(frame)[0]
786 self.raw_lines = inspect.getsourcelines(frame)[0]
787 except OSError:
787 except OSError:
788 self.raw_lines = [
788 self.raw_lines = [
789 "'Could not get source, probably due dynamically evaluated source code.'"
789 "'Could not get source, probably due dynamically evaluated source code.'"
790 ]
790 ]
791
791
792 @property
792 @property
793 def variables_in_executing_piece(self):
793 def variables_in_executing_piece(self):
794 if self._sd:
794 if self._sd:
795 return self._sd.variables_in_executing_piece
795 return self._sd.variables_in_executing_piece
796 else:
796 else:
797 return []
797 return []
798
798
799 @property
799 @property
800 def lines(self):
800 def lines(self):
801 from executing.executing import NotOneValueFound
801 from executing.executing import NotOneValueFound
802
802
803 try:
803 try:
804 return self._sd.lines
804 return self._sd.lines
805 except NotOneValueFound:
805 except NotOneValueFound:
806
806
807 class Dummy:
807 class Dummy:
808 lineno = 0
808 lineno = 0
809 is_current = False
809 is_current = False
810
810
811 def render(self, *, pygmented):
811 def render(self, *, pygmented):
812 return "<Error retrieving source code with stack_data see ipython/ipython#13598>"
812 return "<Error retrieving source code with stack_data see ipython/ipython#13598>"
813
813
814 return [Dummy()]
814 return [Dummy()]
815
815
816 @property
816 @property
817 def executing(self):
817 def executing(self):
818 if self._sd:
818 if self._sd:
819 return self._sd.executing
819 return self._sd.executing
820 else:
820 else:
821 return None
821 return None
822
822
823
823
824 # ----------------------------------------------------------------------------
824 # ----------------------------------------------------------------------------
825 class VerboseTB(TBTools):
825 class VerboseTB(TBTools):
826 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
826 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
827 of HTML. Requires inspect and pydoc. Crazy, man.
827 of HTML. Requires inspect and pydoc. Crazy, man.
828
828
829 Modified version which optionally strips the topmost entries from the
829 Modified version which optionally strips the topmost entries from the
830 traceback, to be used with alternate interpreters (because their own code
830 traceback, to be used with alternate interpreters (because their own code
831 would appear in the traceback)."""
831 would appear in the traceback)."""
832
832
833 _tb_highlight = "bg:ansiyellow"
833 tb_highlight = "bg:ansiyellow"
834 _tb_highlight_style = "default"
834 tb_highlight_style = "default"
835
835
836 def __init__(
836 def __init__(
837 self,
837 self,
838 color_scheme: str = "Linux",
838 color_scheme: str = "Linux",
839 call_pdb: bool = False,
839 call_pdb: bool = False,
840 ostream=None,
840 ostream=None,
841 tb_offset: int = 0,
841 tb_offset: int = 0,
842 long_header: bool = False,
842 long_header: bool = False,
843 include_vars: bool = True,
843 include_vars: bool = True,
844 check_cache=None,
844 check_cache=None,
845 debugger_cls=None,
845 debugger_cls=None,
846 parent=None,
846 parent=None,
847 config=None,
847 config=None,
848 ):
848 ):
849 """Specify traceback offset, headers and color scheme.
849 """Specify traceback offset, headers and color scheme.
850
850
851 Define how many frames to drop from the tracebacks. Calling it with
851 Define how many frames to drop from the tracebacks. Calling it with
852 tb_offset=1 allows use of this handler in interpreters which will have
852 tb_offset=1 allows use of this handler in interpreters which will have
853 their own code at the top of the traceback (VerboseTB will first
853 their own code at the top of the traceback (VerboseTB will first
854 remove that frame before printing the traceback info)."""
854 remove that frame before printing the traceback info)."""
855 TBTools.__init__(
855 TBTools.__init__(
856 self,
856 self,
857 color_scheme=color_scheme,
857 color_scheme=color_scheme,
858 call_pdb=call_pdb,
858 call_pdb=call_pdb,
859 ostream=ostream,
859 ostream=ostream,
860 parent=parent,
860 parent=parent,
861 config=config,
861 config=config,
862 debugger_cls=debugger_cls,
862 debugger_cls=debugger_cls,
863 )
863 )
864 self.tb_offset = tb_offset
864 self.tb_offset = tb_offset
865 self.long_header = long_header
865 self.long_header = long_header
866 self.include_vars = include_vars
866 self.include_vars = include_vars
867 # By default we use linecache.checkcache, but the user can provide a
867 # By default we use linecache.checkcache, but the user can provide a
868 # different check_cache implementation. This was formerly used by the
868 # different check_cache implementation. This was formerly used by the
869 # IPython kernel for interactive code, but is no longer necessary.
869 # IPython kernel for interactive code, but is no longer necessary.
870 if check_cache is None:
870 if check_cache is None:
871 check_cache = linecache.checkcache
871 check_cache = linecache.checkcache
872 self.check_cache = check_cache
872 self.check_cache = check_cache
873
873
874 self.skip_hidden = True
874 self.skip_hidden = True
875
875
876 def format_record(self, frame_info: FrameInfo):
876 def format_record(self, frame_info: FrameInfo):
877 """Format a single stack frame"""
877 """Format a single stack frame"""
878 assert isinstance(frame_info, FrameInfo)
878 assert isinstance(frame_info, FrameInfo)
879 Colors = self.Colors # just a shorthand + quicker name lookup
879 Colors = self.Colors # just a shorthand + quicker name lookup
880 ColorsNormal = Colors.Normal # used a lot
880 ColorsNormal = Colors.Normal # used a lot
881
881
882 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
882 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
883 return ' %s[... skipping similar frames: %s]%s\n' % (
883 return ' %s[... skipping similar frames: %s]%s\n' % (
884 Colors.excName, frame_info.description, ColorsNormal)
884 Colors.excName, frame_info.description, ColorsNormal)
885
885
886 indent = " " * INDENT_SIZE
886 indent = " " * INDENT_SIZE
887 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
887 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
888 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
888 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
889 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
889 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
890 Colors.vName,
890 Colors.vName,
891 Colors.valEm,
891 Colors.valEm,
892 ColorsNormal,
892 ColorsNormal,
893 )
893 )
894 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
894 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
895
895
896 link = _format_filename(
896 link = _format_filename(
897 frame_info.filename,
897 frame_info.filename,
898 Colors.filenameEm,
898 Colors.filenameEm,
899 ColorsNormal,
899 ColorsNormal,
900 lineno=frame_info.lineno,
900 lineno=frame_info.lineno,
901 )
901 )
902 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
902 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
903 if frame_info.executing is not None:
903 if frame_info.executing is not None:
904 func = frame_info.executing.code_qualname()
904 func = frame_info.executing.code_qualname()
905 else:
905 else:
906 func = "?"
906 func = "?"
907 if func == "<module>":
907 if func == "<module>":
908 call = ""
908 call = ""
909 else:
909 else:
910 # Decide whether to include variable details or not
910 # Decide whether to include variable details or not
911 var_repr = eqrepr if self.include_vars else nullrepr
911 var_repr = eqrepr if self.include_vars else nullrepr
912 try:
912 try:
913 scope = inspect.formatargvalues(
913 scope = inspect.formatargvalues(
914 args, varargs, varkw, locals_, formatvalue=var_repr
914 args, varargs, varkw, locals_, formatvalue=var_repr
915 )
915 )
916 call = tpl_call.format(file=func, scope=scope)
916 call = tpl_call.format(file=func, scope=scope)
917 except KeyError:
917 except KeyError:
918 # This happens in situations like errors inside generator
918 # This happens in situations like errors inside generator
919 # expressions, where local variables are listed in the
919 # expressions, where local variables are listed in the
920 # line, but can't be extracted from the frame. I'm not
920 # line, but can't be extracted from the frame. I'm not
921 # 100% sure this isn't actually a bug in inspect itself,
921 # 100% sure this isn't actually a bug in inspect itself,
922 # but since there's no info for us to compute with, the
922 # but since there's no info for us to compute with, the
923 # best we can do is report the failure and move on. Here
923 # best we can do is report the failure and move on. Here
924 # we must *not* call any traceback construction again,
924 # we must *not* call any traceback construction again,
925 # because that would mess up use of %debug later on. So we
925 # because that would mess up use of %debug later on. So we
926 # simply report the failure and move on. The only
926 # simply report the failure and move on. The only
927 # limitation will be that this frame won't have locals
927 # limitation will be that this frame won't have locals
928 # listed in the call signature. Quite subtle problem...
928 # listed in the call signature. Quite subtle problem...
929 # I can't think of a good way to validate this in a unit
929 # I can't think of a good way to validate this in a unit
930 # test, but running a script consisting of:
930 # test, but running a script consisting of:
931 # dict( (k,v.strip()) for (k,v) in range(10) )
931 # dict( (k,v.strip()) for (k,v) in range(10) )
932 # will illustrate the error, if this exception catch is
932 # will illustrate the error, if this exception catch is
933 # disabled.
933 # disabled.
934 call = tpl_call_fail % func
934 call = tpl_call_fail % func
935
935
936 lvals = ''
936 lvals = ''
937 lvals_list = []
937 lvals_list = []
938 if self.include_vars:
938 if self.include_vars:
939 try:
939 try:
940 # we likely want to fix stackdata at some point, but
940 # we likely want to fix stackdata at some point, but
941 # still need a workaround.
941 # still need a workaround.
942 fibp = frame_info.variables_in_executing_piece
942 fibp = frame_info.variables_in_executing_piece
943 for var in fibp:
943 for var in fibp:
944 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
944 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
945 except Exception:
945 except Exception:
946 lvals_list.append(
946 lvals_list.append(
947 "Exception trying to inspect frame. No more locals available."
947 "Exception trying to inspect frame. No more locals available."
948 )
948 )
949 if lvals_list:
949 if lvals_list:
950 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
950 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
951
951
952 result = f'{link}{", " if call else ""}{call}\n'
952 result = f'{link}{", " if call else ""}{call}\n'
953 if frame_info._sd is None:
953 if frame_info._sd is None:
954 # fast fallback if file is too long
954 # fast fallback if file is too long
955 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
955 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
956 link = tpl_link % util_path.compress_user(frame_info.filename)
956 link = tpl_link % util_path.compress_user(frame_info.filename)
957 level = "%s %s\n" % (link, call)
957 level = "%s %s\n" % (link, call)
958 _line_format = PyColorize.Parser(
958 _line_format = PyColorize.Parser(
959 style=self.color_scheme_table.active_scheme_name, parent=self
959 style=self.color_scheme_table.active_scheme_name, parent=self
960 ).format2
960 ).format2
961 first_line = frame_info.code.co_firstlineno
961 first_line = frame_info.code.co_firstlineno
962 current_line = frame_info.lineno[0]
962 current_line = frame_info.lineno[0]
963 raw_lines = frame_info.raw_lines
963 raw_lines = frame_info.raw_lines
964 index = current_line - first_line
964 index = current_line - first_line
965
965
966 if index >= frame_info.context:
966 if index >= frame_info.context:
967 start = max(index - frame_info.context, 0)
967 start = max(index - frame_info.context, 0)
968 stop = index + frame_info.context
968 stop = index + frame_info.context
969 index = frame_info.context
969 index = frame_info.context
970 else:
970 else:
971 start = 0
971 start = 0
972 stop = index + frame_info.context
972 stop = index + frame_info.context
973 raw_lines = raw_lines[start:stop]
973 raw_lines = raw_lines[start:stop]
974
974
975 return "%s%s" % (
975 return "%s%s" % (
976 level,
976 level,
977 "".join(
977 "".join(
978 _simple_format_traceback_lines(
978 _simple_format_traceback_lines(
979 current_line,
979 current_line,
980 index,
980 index,
981 raw_lines,
981 raw_lines,
982 Colors,
982 Colors,
983 lvals,
983 lvals,
984 _line_format,
984 _line_format,
985 )
985 )
986 ),
986 ),
987 )
987 )
988 # result += "\n".join(frame_info.raw_lines)
988 # result += "\n".join(frame_info.raw_lines)
989 else:
989 else:
990 result += "".join(
990 result += "".join(
991 _format_traceback_lines(
991 _format_traceback_lines(
992 frame_info.lines, Colors, self.has_colors, lvals
992 frame_info.lines, Colors, self.has_colors, lvals
993 )
993 )
994 )
994 )
995 return result
995 return result
996
996
997 def prepare_header(self, etype: str, long_version: bool = False):
997 def prepare_header(self, etype: str, long_version: bool = False):
998 colors = self.Colors # just a shorthand + quicker name lookup
998 colors = self.Colors # just a shorthand + quicker name lookup
999 colorsnormal = colors.Normal # used a lot
999 colorsnormal = colors.Normal # used a lot
1000 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
1000 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
1001 width = min(75, get_terminal_size()[0])
1001 width = min(75, get_terminal_size()[0])
1002 if long_version:
1002 if long_version:
1003 # Header with the exception type, python version, and date
1003 # Header with the exception type, python version, and date
1004 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1004 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1005 date = time.ctime(time.time())
1005 date = time.ctime(time.time())
1006
1006
1007 head = "%s%s%s\n%s%s%s\n%s" % (
1007 head = "%s%s%s\n%s%s%s\n%s" % (
1008 colors.topline,
1008 colors.topline,
1009 "-" * width,
1009 "-" * width,
1010 colorsnormal,
1010 colorsnormal,
1011 exc,
1011 exc,
1012 " " * (width - len(etype) - len(pyver)),
1012 " " * (width - len(etype) - len(pyver)),
1013 pyver,
1013 pyver,
1014 date.rjust(width),
1014 date.rjust(width),
1015 )
1015 )
1016 head += (
1016 head += (
1017 "\nA problem occurred executing Python code. Here is the sequence of function"
1017 "\nA problem occurred executing Python code. Here is the sequence of function"
1018 "\ncalls leading up to the error, with the most recent (innermost) call last."
1018 "\ncalls leading up to the error, with the most recent (innermost) call last."
1019 )
1019 )
1020 else:
1020 else:
1021 # Simplified header
1021 # Simplified header
1022 head = "%s%s" % (
1022 head = "%s%s" % (
1023 exc,
1023 exc,
1024 "Traceback (most recent call last)".rjust(width - len(etype)),
1024 "Traceback (most recent call last)".rjust(width - len(etype)),
1025 )
1025 )
1026
1026
1027 return head
1027 return head
1028
1028
1029 def format_exception(self, etype, evalue):
1029 def format_exception(self, etype, evalue):
1030 colors = self.Colors # just a shorthand + quicker name lookup
1030 colors = self.Colors # just a shorthand + quicker name lookup
1031 colorsnormal = colors.Normal # used a lot
1031 colorsnormal = colors.Normal # used a lot
1032 # Get (safely) a string form of the exception info
1032 # Get (safely) a string form of the exception info
1033 try:
1033 try:
1034 etype_str, evalue_str = map(str, (etype, evalue))
1034 etype_str, evalue_str = map(str, (etype, evalue))
1035 except:
1035 except:
1036 # User exception is improperly defined.
1036 # User exception is improperly defined.
1037 etype, evalue = str, sys.exc_info()[:2]
1037 etype, evalue = str, sys.exc_info()[:2]
1038 etype_str, evalue_str = map(str, (etype, evalue))
1038 etype_str, evalue_str = map(str, (etype, evalue))
1039
1039
1040 # PEP-678 notes
1040 # PEP-678 notes
1041 notes = getattr(evalue, "__notes__", [])
1041 notes = getattr(evalue, "__notes__", [])
1042 if not isinstance(notes, Sequence) or isinstance(notes, (str, bytes)):
1042 if not isinstance(notes, Sequence) or isinstance(notes, (str, bytes)):
1043 notes = [_safe_string(notes, "__notes__", func=repr)]
1043 notes = [_safe_string(notes, "__notes__", func=repr)]
1044
1044
1045 # ... and format it
1045 # ... and format it
1046 return [
1046 return [
1047 "{}{}{}: {}".format(
1047 "{}{}{}: {}".format(
1048 colors.excName,
1048 colors.excName,
1049 etype_str,
1049 etype_str,
1050 colorsnormal,
1050 colorsnormal,
1051 py3compat.cast_unicode(evalue_str),
1051 py3compat.cast_unicode(evalue_str),
1052 ),
1052 ),
1053 *(
1053 *(
1054 "{}{}".format(
1054 "{}{}".format(
1055 colorsnormal, _safe_string(py3compat.cast_unicode(n), "note")
1055 colorsnormal, _safe_string(py3compat.cast_unicode(n), "note")
1056 )
1056 )
1057 for n in notes
1057 for n in notes
1058 ),
1058 ),
1059 ]
1059 ]
1060
1060
1061 def format_exception_as_a_whole(
1061 def format_exception_as_a_whole(
1062 self,
1062 self,
1063 etype: type,
1063 etype: type,
1064 evalue: Optional[BaseException],
1064 evalue: Optional[BaseException],
1065 etb: Optional[TracebackType],
1065 etb: Optional[TracebackType],
1066 number_of_lines_of_context,
1066 number_of_lines_of_context,
1067 tb_offset: Optional[int],
1067 tb_offset: Optional[int],
1068 ):
1068 ):
1069 """Formats the header, traceback and exception message for a single exception.
1069 """Formats the header, traceback and exception message for a single exception.
1070
1070
1071 This may be called multiple times by Python 3 exception chaining
1071 This may be called multiple times by Python 3 exception chaining
1072 (PEP 3134).
1072 (PEP 3134).
1073 """
1073 """
1074 # some locals
1074 # some locals
1075 orig_etype = etype
1075 orig_etype = etype
1076 try:
1076 try:
1077 etype = etype.__name__ # type: ignore
1077 etype = etype.__name__ # type: ignore
1078 except AttributeError:
1078 except AttributeError:
1079 pass
1079 pass
1080
1080
1081 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1081 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1082 assert isinstance(tb_offset, int)
1082 assert isinstance(tb_offset, int)
1083 head = self.prepare_header(str(etype), self.long_header)
1083 head = self.prepare_header(str(etype), self.long_header)
1084 records = (
1084 records = (
1085 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
1085 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
1086 )
1086 )
1087
1087
1088 frames = []
1088 frames = []
1089 skipped = 0
1089 skipped = 0
1090 lastrecord = len(records) - 1
1090 lastrecord = len(records) - 1
1091 for i, record in enumerate(records):
1091 for i, record in enumerate(records):
1092 if (
1092 if (
1093 not isinstance(record._sd, stack_data.RepeatedFrames)
1093 not isinstance(record._sd, stack_data.RepeatedFrames)
1094 and self.skip_hidden
1094 and self.skip_hidden
1095 ):
1095 ):
1096 if (
1096 if (
1097 record.frame.f_locals.get("__tracebackhide__", 0)
1097 record.frame.f_locals.get("__tracebackhide__", 0)
1098 and i != lastrecord
1098 and i != lastrecord
1099 ):
1099 ):
1100 skipped += 1
1100 skipped += 1
1101 continue
1101 continue
1102 if skipped:
1102 if skipped:
1103 Colors = self.Colors # just a shorthand + quicker name lookup
1103 Colors = self.Colors # just a shorthand + quicker name lookup
1104 ColorsNormal = Colors.Normal # used a lot
1104 ColorsNormal = Colors.Normal # used a lot
1105 frames.append(
1105 frames.append(
1106 " %s[... skipping hidden %s frame]%s\n"
1106 " %s[... skipping hidden %s frame]%s\n"
1107 % (Colors.excName, skipped, ColorsNormal)
1107 % (Colors.excName, skipped, ColorsNormal)
1108 )
1108 )
1109 skipped = 0
1109 skipped = 0
1110 frames.append(self.format_record(record))
1110 frames.append(self.format_record(record))
1111 if skipped:
1111 if skipped:
1112 Colors = self.Colors # just a shorthand + quicker name lookup
1112 Colors = self.Colors # just a shorthand + quicker name lookup
1113 ColorsNormal = Colors.Normal # used a lot
1113 ColorsNormal = Colors.Normal # used a lot
1114 frames.append(
1114 frames.append(
1115 " %s[... skipping hidden %s frame]%s\n"
1115 " %s[... skipping hidden %s frame]%s\n"
1116 % (Colors.excName, skipped, ColorsNormal)
1116 % (Colors.excName, skipped, ColorsNormal)
1117 )
1117 )
1118
1118
1119 formatted_exception = self.format_exception(etype, evalue)
1119 formatted_exception = self.format_exception(etype, evalue)
1120 if records:
1120 if records:
1121 frame_info = records[-1]
1121 frame_info = records[-1]
1122 ipinst = get_ipython()
1122 ipinst = get_ipython()
1123 if ipinst is not None:
1123 if ipinst is not None:
1124 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
1124 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
1125
1125
1126 return [[head] + frames + formatted_exception]
1126 return [[head] + frames + formatted_exception]
1127
1127
1128 def get_records(
1128 def get_records(
1129 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
1129 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
1130 ):
1130 ):
1131 assert etb is not None
1131 assert etb is not None
1132 context = number_of_lines_of_context - 1
1132 context = number_of_lines_of_context - 1
1133 after = context // 2
1133 after = context // 2
1134 before = context - after
1134 before = context - after
1135 if self.has_colors:
1135 if self.has_colors:
1136 style = get_style_by_name(self._tb_highlight_style)
1136 style = get_style_by_name(self.tb_highlight_style)
1137 style = stack_data.style_with_executing_node(style, self._tb_highlight)
1137 style = stack_data.style_with_executing_node(style, self.tb_highlight)
1138 formatter = Terminal256Formatter(style=style)
1138 formatter = Terminal256Formatter(style=style)
1139 else:
1139 else:
1140 formatter = None
1140 formatter = None
1141 options = stack_data.Options(
1141 options = stack_data.Options(
1142 before=before,
1142 before=before,
1143 after=after,
1143 after=after,
1144 pygments_formatter=formatter,
1144 pygments_formatter=formatter,
1145 )
1145 )
1146
1146
1147 # Let's estimate the amount of code we will have to parse/highlight.
1147 # Let's estimate the amount of code we will have to parse/highlight.
1148 cf: Optional[TracebackType] = etb
1148 cf: Optional[TracebackType] = etb
1149 max_len = 0
1149 max_len = 0
1150 tbs = []
1150 tbs = []
1151 while cf is not None:
1151 while cf is not None:
1152 try:
1152 try:
1153 mod = inspect.getmodule(cf.tb_frame)
1153 mod = inspect.getmodule(cf.tb_frame)
1154 if mod is not None:
1154 if mod is not None:
1155 mod_name = mod.__name__
1155 mod_name = mod.__name__
1156 root_name, *_ = mod_name.split(".")
1156 root_name, *_ = mod_name.split(".")
1157 if root_name == "IPython":
1157 if root_name == "IPython":
1158 cf = cf.tb_next
1158 cf = cf.tb_next
1159 continue
1159 continue
1160 max_len = get_line_number_of_frame(cf.tb_frame)
1160 max_len = get_line_number_of_frame(cf.tb_frame)
1161
1161
1162 except OSError:
1162 except OSError:
1163 max_len = 0
1163 max_len = 0
1164 max_len = max(max_len, max_len)
1164 max_len = max(max_len, max_len)
1165 tbs.append(cf)
1165 tbs.append(cf)
1166 cf = getattr(cf, "tb_next", None)
1166 cf = getattr(cf, "tb_next", None)
1167
1167
1168 if max_len > FAST_THRESHOLD:
1168 if max_len > FAST_THRESHOLD:
1169 FIs = []
1169 FIs = []
1170 for tb in tbs:
1170 for tb in tbs:
1171 frame = tb.tb_frame # type: ignore
1171 frame = tb.tb_frame # type: ignore
1172 lineno = (frame.f_lineno,)
1172 lineno = (frame.f_lineno,)
1173 code = frame.f_code
1173 code = frame.f_code
1174 filename = code.co_filename
1174 filename = code.co_filename
1175 # TODO: Here we need to use before/after/
1175 # TODO: Here we need to use before/after/
1176 FIs.append(
1176 FIs.append(
1177 FrameInfo(
1177 FrameInfo(
1178 "Raw frame", filename, lineno, frame, code, context=context
1178 "Raw frame", filename, lineno, frame, code, context=context
1179 )
1179 )
1180 )
1180 )
1181 return FIs
1181 return FIs
1182 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1182 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1183 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1183 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1184 return res
1184 return res
1185
1185
1186 def structured_traceback(
1186 def structured_traceback(
1187 self,
1187 self,
1188 etype: type,
1188 etype: type,
1189 evalue: Optional[BaseException],
1189 evalue: Optional[BaseException],
1190 etb: Optional[TracebackType] = None,
1190 etb: Optional[TracebackType] = None,
1191 tb_offset: Optional[int] = None,
1191 tb_offset: Optional[int] = None,
1192 number_of_lines_of_context: int = 5,
1192 number_of_lines_of_context: int = 5,
1193 ):
1193 ):
1194 """Return a nice text document describing the traceback."""
1194 """Return a nice text document describing the traceback."""
1195 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1195 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1196 tb_offset)
1196 tb_offset)
1197
1197
1198 colors = self.Colors # just a shorthand + quicker name lookup
1198 colors = self.Colors # just a shorthand + quicker name lookup
1199 colorsnormal = colors.Normal # used a lot
1199 colorsnormal = colors.Normal # used a lot
1200 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1200 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1201 structured_traceback_parts = [head]
1201 structured_traceback_parts = [head]
1202 chained_exceptions_tb_offset = 0
1202 chained_exceptions_tb_offset = 0
1203 lines_of_context = 3
1203 lines_of_context = 3
1204 formatted_exceptions = formatted_exception
1204 formatted_exceptions = formatted_exception
1205 exception = self.get_parts_of_chained_exception(evalue)
1205 exception = self.get_parts_of_chained_exception(evalue)
1206 if exception:
1206 if exception:
1207 assert evalue is not None
1207 assert evalue is not None
1208 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1208 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1209 etype, evalue, etb = exception
1209 etype, evalue, etb = exception
1210 else:
1210 else:
1211 evalue = None
1211 evalue = None
1212 chained_exc_ids = set()
1212 chained_exc_ids = set()
1213 while evalue:
1213 while evalue:
1214 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1214 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1215 chained_exceptions_tb_offset)
1215 chained_exceptions_tb_offset)
1216 exception = self.get_parts_of_chained_exception(evalue)
1216 exception = self.get_parts_of_chained_exception(evalue)
1217
1217
1218 if exception and not id(exception[1]) in chained_exc_ids:
1218 if exception and not id(exception[1]) in chained_exc_ids:
1219 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1219 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1220 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1220 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1221 etype, evalue, etb = exception
1221 etype, evalue, etb = exception
1222 else:
1222 else:
1223 evalue = None
1223 evalue = None
1224
1224
1225 # we want to see exceptions in a reversed order:
1225 # we want to see exceptions in a reversed order:
1226 # the first exception should be on top
1226 # the first exception should be on top
1227 for formatted_exception in reversed(formatted_exceptions):
1227 for formatted_exception in reversed(formatted_exceptions):
1228 structured_traceback_parts += formatted_exception
1228 structured_traceback_parts += formatted_exception
1229
1229
1230 return structured_traceback_parts
1230 return structured_traceback_parts
1231
1231
1232 def debugger(self, force: bool = False):
1232 def debugger(self, force: bool = False):
1233 """Call up the pdb debugger if desired, always clean up the tb
1233 """Call up the pdb debugger if desired, always clean up the tb
1234 reference.
1234 reference.
1235
1235
1236 Keywords:
1236 Keywords:
1237
1237
1238 - force(False): by default, this routine checks the instance call_pdb
1238 - force(False): by default, this routine checks the instance call_pdb
1239 flag and does not actually invoke the debugger if the flag is false.
1239 flag and does not actually invoke the debugger if the flag is false.
1240 The 'force' option forces the debugger to activate even if the flag
1240 The 'force' option forces the debugger to activate even if the flag
1241 is false.
1241 is false.
1242
1242
1243 If the call_pdb flag is set, the pdb interactive debugger is
1243 If the call_pdb flag is set, the pdb interactive debugger is
1244 invoked. In all cases, the self.tb reference to the current traceback
1244 invoked. In all cases, the self.tb reference to the current traceback
1245 is deleted to prevent lingering references which hamper memory
1245 is deleted to prevent lingering references which hamper memory
1246 management.
1246 management.
1247
1247
1248 Note that each call to pdb() does an 'import readline', so if your app
1248 Note that each call to pdb() does an 'import readline', so if your app
1249 requires a special setup for the readline completers, you'll have to
1249 requires a special setup for the readline completers, you'll have to
1250 fix that by hand after invoking the exception handler."""
1250 fix that by hand after invoking the exception handler."""
1251
1251
1252 if force or self.call_pdb:
1252 if force or self.call_pdb:
1253 if self.pdb is None:
1253 if self.pdb is None:
1254 self.pdb = self.debugger_cls()
1254 self.pdb = self.debugger_cls()
1255 # the system displayhook may have changed, restore the original
1255 # the system displayhook may have changed, restore the original
1256 # for pdb
1256 # for pdb
1257 display_trap = DisplayTrap(hook=sys.__displayhook__)
1257 display_trap = DisplayTrap(hook=sys.__displayhook__)
1258 with display_trap:
1258 with display_trap:
1259 self.pdb.reset()
1259 self.pdb.reset()
1260 # Find the right frame so we don't pop up inside ipython itself
1260 # Find the right frame so we don't pop up inside ipython itself
1261 if hasattr(self, "tb") and self.tb is not None: # type: ignore[has-type]
1261 if hasattr(self, "tb") and self.tb is not None: # type: ignore[has-type]
1262 etb = self.tb # type: ignore[has-type]
1262 etb = self.tb # type: ignore[has-type]
1263 else:
1263 else:
1264 etb = self.tb = sys.last_traceback
1264 etb = self.tb = sys.last_traceback
1265 while self.tb is not None and self.tb.tb_next is not None:
1265 while self.tb is not None and self.tb.tb_next is not None:
1266 assert self.tb.tb_next is not None
1266 assert self.tb.tb_next is not None
1267 self.tb = self.tb.tb_next
1267 self.tb = self.tb.tb_next
1268 if etb and etb.tb_next:
1268 if etb and etb.tb_next:
1269 etb = etb.tb_next
1269 etb = etb.tb_next
1270 self.pdb.botframe = etb.tb_frame
1270 self.pdb.botframe = etb.tb_frame
1271 # last_value should be deprecated, but last-exc sometimme not set
1271 # last_value should be deprecated, but last-exc sometimme not set
1272 # please check why later and remove the getattr.
1272 # please check why later and remove the getattr.
1273 exc = sys.last_value if sys.version_info < (3, 12) else getattr(sys, "last_exc", sys.last_value) # type: ignore[attr-defined]
1273 exc = sys.last_value if sys.version_info < (3, 12) else getattr(sys, "last_exc", sys.last_value) # type: ignore[attr-defined]
1274 if exc:
1274 if exc:
1275 self.pdb.interaction(None, exc)
1275 self.pdb.interaction(None, exc)
1276 else:
1276 else:
1277 self.pdb.interaction(None, etb)
1277 self.pdb.interaction(None, etb)
1278
1278
1279 if hasattr(self, 'tb'):
1279 if hasattr(self, 'tb'):
1280 del self.tb
1280 del self.tb
1281
1281
1282 def handler(self, info=None):
1282 def handler(self, info=None):
1283 (etype, evalue, etb) = info or sys.exc_info()
1283 (etype, evalue, etb) = info or sys.exc_info()
1284 self.tb = etb
1284 self.tb = etb
1285 ostream = self.ostream
1285 ostream = self.ostream
1286 ostream.flush()
1286 ostream.flush()
1287 ostream.write(self.text(etype, evalue, etb))
1287 ostream.write(self.text(etype, evalue, etb))
1288 ostream.write('\n')
1288 ostream.write('\n')
1289 ostream.flush()
1289 ostream.flush()
1290
1290
1291 # Changed so an instance can just be called as VerboseTB_inst() and print
1291 # Changed so an instance can just be called as VerboseTB_inst() and print
1292 # out the right info on its own.
1292 # out the right info on its own.
1293 def __call__(self, etype=None, evalue=None, etb=None):
1293 def __call__(self, etype=None, evalue=None, etb=None):
1294 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1294 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1295 if etb is None:
1295 if etb is None:
1296 self.handler()
1296 self.handler()
1297 else:
1297 else:
1298 self.handler((etype, evalue, etb))
1298 self.handler((etype, evalue, etb))
1299 try:
1299 try:
1300 self.debugger()
1300 self.debugger()
1301 except KeyboardInterrupt:
1301 except KeyboardInterrupt:
1302 print("\nKeyboardInterrupt")
1302 print("\nKeyboardInterrupt")
1303
1303
1304
1304
1305 #----------------------------------------------------------------------------
1305 #----------------------------------------------------------------------------
1306 class FormattedTB(VerboseTB, ListTB):
1306 class FormattedTB(VerboseTB, ListTB):
1307 """Subclass ListTB but allow calling with a traceback.
1307 """Subclass ListTB but allow calling with a traceback.
1308
1308
1309 It can thus be used as a sys.excepthook for Python > 2.1.
1309 It can thus be used as a sys.excepthook for Python > 2.1.
1310
1310
1311 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1311 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1312
1312
1313 Allows a tb_offset to be specified. This is useful for situations where
1313 Allows a tb_offset to be specified. This is useful for situations where
1314 one needs to remove a number of topmost frames from the traceback (such as
1314 one needs to remove a number of topmost frames from the traceback (such as
1315 occurs with python programs that themselves execute other python code,
1315 occurs with python programs that themselves execute other python code,
1316 like Python shells). """
1316 like Python shells). """
1317
1317
1318 mode: str
1318 mode: str
1319
1319
1320 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1320 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1321 ostream=None,
1321 ostream=None,
1322 tb_offset=0, long_header=False, include_vars=False,
1322 tb_offset=0, long_header=False, include_vars=False,
1323 check_cache=None, debugger_cls=None,
1323 check_cache=None, debugger_cls=None,
1324 parent=None, config=None):
1324 parent=None, config=None):
1325
1325
1326 # NEVER change the order of this list. Put new modes at the end:
1326 # NEVER change the order of this list. Put new modes at the end:
1327 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
1327 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
1328 self.verbose_modes = self.valid_modes[1:3]
1328 self.verbose_modes = self.valid_modes[1:3]
1329
1329
1330 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1330 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1331 ostream=ostream, tb_offset=tb_offset,
1331 ostream=ostream, tb_offset=tb_offset,
1332 long_header=long_header, include_vars=include_vars,
1332 long_header=long_header, include_vars=include_vars,
1333 check_cache=check_cache, debugger_cls=debugger_cls,
1333 check_cache=check_cache, debugger_cls=debugger_cls,
1334 parent=parent, config=config)
1334 parent=parent, config=config)
1335
1335
1336 # Different types of tracebacks are joined with different separators to
1336 # Different types of tracebacks are joined with different separators to
1337 # form a single string. They are taken from this dict
1337 # form a single string. They are taken from this dict
1338 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1338 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1339 Minimal='')
1339 Minimal='')
1340 # set_mode also sets the tb_join_char attribute
1340 # set_mode also sets the tb_join_char attribute
1341 self.set_mode(mode)
1341 self.set_mode(mode)
1342
1342
1343 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1343 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1344 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1344 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1345 mode = self.mode
1345 mode = self.mode
1346 if mode in self.verbose_modes:
1346 if mode in self.verbose_modes:
1347 # Verbose modes need a full traceback
1347 # Verbose modes need a full traceback
1348 return VerboseTB.structured_traceback(
1348 return VerboseTB.structured_traceback(
1349 self, etype, value, tb, tb_offset, number_of_lines_of_context
1349 self, etype, value, tb, tb_offset, number_of_lines_of_context
1350 )
1350 )
1351 elif mode == 'Minimal':
1351 elif mode == 'Minimal':
1352 return ListTB.get_exception_only(self, etype, value)
1352 return ListTB.get_exception_only(self, etype, value)
1353 else:
1353 else:
1354 # We must check the source cache because otherwise we can print
1354 # We must check the source cache because otherwise we can print
1355 # out-of-date source code.
1355 # out-of-date source code.
1356 self.check_cache()
1356 self.check_cache()
1357 # Now we can extract and format the exception
1357 # Now we can extract and format the exception
1358 return ListTB.structured_traceback(
1358 return ListTB.structured_traceback(
1359 self, etype, value, tb, tb_offset, number_of_lines_of_context
1359 self, etype, value, tb, tb_offset, number_of_lines_of_context
1360 )
1360 )
1361
1361
1362 def stb2text(self, stb):
1362 def stb2text(self, stb):
1363 """Convert a structured traceback (a list) to a string."""
1363 """Convert a structured traceback (a list) to a string."""
1364 return self.tb_join_char.join(stb)
1364 return self.tb_join_char.join(stb)
1365
1365
1366 def set_mode(self, mode: Optional[str] = None):
1366 def set_mode(self, mode: Optional[str] = None):
1367 """Switch to the desired mode.
1367 """Switch to the desired mode.
1368
1368
1369 If mode is not specified, cycles through the available modes."""
1369 If mode is not specified, cycles through the available modes."""
1370
1370
1371 if not mode:
1371 if not mode:
1372 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1372 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1373 len(self.valid_modes)
1373 len(self.valid_modes)
1374 self.mode = self.valid_modes[new_idx]
1374 self.mode = self.valid_modes[new_idx]
1375 elif mode not in self.valid_modes:
1375 elif mode not in self.valid_modes:
1376 raise ValueError(
1376 raise ValueError(
1377 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1377 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1378 "Valid modes: " + str(self.valid_modes)
1378 "Valid modes: " + str(self.valid_modes)
1379 )
1379 )
1380 else:
1380 else:
1381 assert isinstance(mode, str)
1381 assert isinstance(mode, str)
1382 self.mode = mode
1382 self.mode = mode
1383 # include variable details only in 'Verbose' mode
1383 # include variable details only in 'Verbose' mode
1384 self.include_vars = (self.mode == self.valid_modes[2])
1384 self.include_vars = (self.mode == self.valid_modes[2])
1385 # Set the join character for generating text tracebacks
1385 # Set the join character for generating text tracebacks
1386 self.tb_join_char = self._join_chars[self.mode]
1386 self.tb_join_char = self._join_chars[self.mode]
1387
1387
1388 # some convenient shortcuts
1388 # some convenient shortcuts
1389 def plain(self):
1389 def plain(self):
1390 self.set_mode(self.valid_modes[0])
1390 self.set_mode(self.valid_modes[0])
1391
1391
1392 def context(self):
1392 def context(self):
1393 self.set_mode(self.valid_modes[1])
1393 self.set_mode(self.valid_modes[1])
1394
1394
1395 def verbose(self):
1395 def verbose(self):
1396 self.set_mode(self.valid_modes[2])
1396 self.set_mode(self.valid_modes[2])
1397
1397
1398 def minimal(self):
1398 def minimal(self):
1399 self.set_mode(self.valid_modes[3])
1399 self.set_mode(self.valid_modes[3])
1400
1400
1401
1401
1402 #----------------------------------------------------------------------------
1402 #----------------------------------------------------------------------------
1403 class AutoFormattedTB(FormattedTB):
1403 class AutoFormattedTB(FormattedTB):
1404 """A traceback printer which can be called on the fly.
1404 """A traceback printer which can be called on the fly.
1405
1405
1406 It will find out about exceptions by itself.
1406 It will find out about exceptions by itself.
1407
1407
1408 A brief example::
1408 A brief example::
1409
1409
1410 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1410 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1411 try:
1411 try:
1412 ...
1412 ...
1413 except:
1413 except:
1414 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1414 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1415 """
1415 """
1416
1416
1417 def __call__(self, etype=None, evalue=None, etb=None,
1417 def __call__(self, etype=None, evalue=None, etb=None,
1418 out=None, tb_offset=None):
1418 out=None, tb_offset=None):
1419 """Print out a formatted exception traceback.
1419 """Print out a formatted exception traceback.
1420
1420
1421 Optional arguments:
1421 Optional arguments:
1422 - out: an open file-like object to direct output to.
1422 - out: an open file-like object to direct output to.
1423
1423
1424 - tb_offset: the number of frames to skip over in the stack, on a
1424 - tb_offset: the number of frames to skip over in the stack, on a
1425 per-call basis (this overrides temporarily the instance's tb_offset
1425 per-call basis (this overrides temporarily the instance's tb_offset
1426 given at initialization time."""
1426 given at initialization time."""
1427
1427
1428 if out is None:
1428 if out is None:
1429 out = self.ostream
1429 out = self.ostream
1430 out.flush()
1430 out.flush()
1431 out.write(self.text(etype, evalue, etb, tb_offset))
1431 out.write(self.text(etype, evalue, etb, tb_offset))
1432 out.write('\n')
1432 out.write('\n')
1433 out.flush()
1433 out.flush()
1434 # FIXME: we should remove the auto pdb behavior from here and leave
1434 # FIXME: we should remove the auto pdb behavior from here and leave
1435 # that to the clients.
1435 # that to the clients.
1436 try:
1436 try:
1437 self.debugger()
1437 self.debugger()
1438 except KeyboardInterrupt:
1438 except KeyboardInterrupt:
1439 print("\nKeyboardInterrupt")
1439 print("\nKeyboardInterrupt")
1440
1440
1441 def structured_traceback(
1441 def structured_traceback(
1442 self,
1442 self,
1443 etype: type,
1443 etype: type,
1444 evalue: Optional[BaseException],
1444 evalue: Optional[BaseException],
1445 etb: Optional[TracebackType] = None,
1445 etb: Optional[TracebackType] = None,
1446 tb_offset: Optional[int] = None,
1446 tb_offset: Optional[int] = None,
1447 number_of_lines_of_context: int = 5,
1447 number_of_lines_of_context: int = 5,
1448 ):
1448 ):
1449 # tb: TracebackType or tupleof tb types ?
1449 # tb: TracebackType or tupleof tb types ?
1450 if etype is None:
1450 if etype is None:
1451 etype, evalue, etb = sys.exc_info()
1451 etype, evalue, etb = sys.exc_info()
1452 if isinstance(etb, tuple):
1452 if isinstance(etb, tuple):
1453 # tb is a tuple if this is a chained exception.
1453 # tb is a tuple if this is a chained exception.
1454 self.tb = etb[0]
1454 self.tb = etb[0]
1455 else:
1455 else:
1456 self.tb = etb
1456 self.tb = etb
1457 return FormattedTB.structured_traceback(
1457 return FormattedTB.structured_traceback(
1458 self, etype, evalue, etb, tb_offset, number_of_lines_of_context
1458 self, etype, evalue, etb, tb_offset, number_of_lines_of_context
1459 )
1459 )
1460
1460
1461
1461
1462 #---------------------------------------------------------------------------
1462 #---------------------------------------------------------------------------
1463
1463
1464 # A simple class to preserve Nathan's original functionality.
1464 # A simple class to preserve Nathan's original functionality.
1465 class ColorTB(FormattedTB):
1465 class ColorTB(FormattedTB):
1466 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1466 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1467
1467
1468 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1468 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1469 FormattedTB.__init__(self, color_scheme=color_scheme,
1469 FormattedTB.__init__(self, color_scheme=color_scheme,
1470 call_pdb=call_pdb, **kwargs)
1470 call_pdb=call_pdb, **kwargs)
1471
1471
1472
1472
1473 class SyntaxTB(ListTB):
1473 class SyntaxTB(ListTB):
1474 """Extension which holds some state: the last exception value"""
1474 """Extension which holds some state: the last exception value"""
1475
1475
1476 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1476 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1477 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1477 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1478 self.last_syntax_error = None
1478 self.last_syntax_error = None
1479
1479
1480 def __call__(self, etype, value, elist):
1480 def __call__(self, etype, value, elist):
1481 self.last_syntax_error = value
1481 self.last_syntax_error = value
1482
1482
1483 ListTB.__call__(self, etype, value, elist)
1483 ListTB.__call__(self, etype, value, elist)
1484
1484
1485 def structured_traceback(self, etype, value, elist, tb_offset=None,
1485 def structured_traceback(self, etype, value, elist, tb_offset=None,
1486 context=5):
1486 context=5):
1487 # If the source file has been edited, the line in the syntax error can
1487 # If the source file has been edited, the line in the syntax error can
1488 # be wrong (retrieved from an outdated cache). This replaces it with
1488 # be wrong (retrieved from an outdated cache). This replaces it with
1489 # the current value.
1489 # the current value.
1490 if isinstance(value, SyntaxError) \
1490 if isinstance(value, SyntaxError) \
1491 and isinstance(value.filename, str) \
1491 and isinstance(value.filename, str) \
1492 and isinstance(value.lineno, int):
1492 and isinstance(value.lineno, int):
1493 linecache.checkcache(value.filename)
1493 linecache.checkcache(value.filename)
1494 newtext = linecache.getline(value.filename, value.lineno)
1494 newtext = linecache.getline(value.filename, value.lineno)
1495 if newtext:
1495 if newtext:
1496 value.text = newtext
1496 value.text = newtext
1497 self.last_syntax_error = value
1497 self.last_syntax_error = value
1498 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1498 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1499 tb_offset=tb_offset, context=context)
1499 tb_offset=tb_offset, context=context)
1500
1500
1501 def clear_err_state(self):
1501 def clear_err_state(self):
1502 """Return the current error state and clear it"""
1502 """Return the current error state and clear it"""
1503 e = self.last_syntax_error
1503 e = self.last_syntax_error
1504 self.last_syntax_error = None
1504 self.last_syntax_error = None
1505 return e
1505 return e
1506
1506
1507 def stb2text(self, stb):
1507 def stb2text(self, stb):
1508 """Convert a structured traceback (a list) to a string."""
1508 """Convert a structured traceback (a list) to a string."""
1509 return ''.join(stb)
1509 return ''.join(stb)
1510
1510
1511
1511
1512 # some internal-use functions
1512 # some internal-use functions
1513 def text_repr(value):
1513 def text_repr(value):
1514 """Hopefully pretty robust repr equivalent."""
1514 """Hopefully pretty robust repr equivalent."""
1515 # this is pretty horrible but should always return *something*
1515 # this is pretty horrible but should always return *something*
1516 try:
1516 try:
1517 return pydoc.text.repr(value) # type: ignore[call-arg]
1517 return pydoc.text.repr(value) # type: ignore[call-arg]
1518 except KeyboardInterrupt:
1518 except KeyboardInterrupt:
1519 raise
1519 raise
1520 except:
1520 except:
1521 try:
1521 try:
1522 return repr(value)
1522 return repr(value)
1523 except KeyboardInterrupt:
1523 except KeyboardInterrupt:
1524 raise
1524 raise
1525 except:
1525 except:
1526 try:
1526 try:
1527 # all still in an except block so we catch
1527 # all still in an except block so we catch
1528 # getattr raising
1528 # getattr raising
1529 name = getattr(value, '__name__', None)
1529 name = getattr(value, '__name__', None)
1530 if name:
1530 if name:
1531 # ick, recursion
1531 # ick, recursion
1532 return text_repr(name)
1532 return text_repr(name)
1533 klass = getattr(value, '__class__', None)
1533 klass = getattr(value, '__class__', None)
1534 if klass:
1534 if klass:
1535 return '%s instance' % text_repr(klass)
1535 return '%s instance' % text_repr(klass)
1536 except KeyboardInterrupt:
1536 except KeyboardInterrupt:
1537 raise
1537 raise
1538 except:
1538 except:
1539 return 'UNRECOVERABLE REPR FAILURE'
1539 return 'UNRECOVERABLE REPR FAILURE'
1540
1540
1541
1541
1542 def eqrepr(value, repr=text_repr):
1542 def eqrepr(value, repr=text_repr):
1543 return '=%s' % repr(value)
1543 return '=%s' % repr(value)
1544
1544
1545
1545
1546 def nullrepr(value, repr=text_repr):
1546 def nullrepr(value, repr=text_repr):
1547 return ''
1547 return ''
General Comments 0
You need to be logged in to leave comments. Login now