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