##// END OF EJS Templates
Handle OSError cases where traceback frames occur from built files (#13964)...
Matthias Bussonnier -
r28196:92027083 merge
parent child Browse files
Show More
@@ -1,1395 +1,1399 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,
394 self,
395 etype: type,
395 etype: type,
396 evalue: Optional[BaseException],
396 evalue: Optional[BaseException],
397 etb: Optional[TracebackType] = None,
397 etb: Optional[TracebackType] = None,
398 tb_offset: Optional[int] = None,
398 tb_offset: Optional[int] = None,
399 context=5,
399 context=5,
400 ):
400 ):
401 """Return a list of traceback frames.
401 """Return a list of traceback frames.
402
402
403 Must be implemented by each class.
403 Must be implemented by each class.
404 """
404 """
405 raise NotImplementedError()
405 raise NotImplementedError()
406
406
407
407
408 #---------------------------------------------------------------------------
408 #---------------------------------------------------------------------------
409 class ListTB(TBTools):
409 class ListTB(TBTools):
410 """Print traceback information from a traceback list, with optional color.
410 """Print traceback information from a traceback list, with optional color.
411
411
412 Calling requires 3 arguments: (etype, evalue, elist)
412 Calling requires 3 arguments: (etype, evalue, elist)
413 as would be obtained by::
413 as would be obtained by::
414
414
415 etype, evalue, tb = sys.exc_info()
415 etype, evalue, tb = sys.exc_info()
416 if tb:
416 if tb:
417 elist = traceback.extract_tb(tb)
417 elist = traceback.extract_tb(tb)
418 else:
418 else:
419 elist = None
419 elist = None
420
420
421 It can thus be used by programs which need to process the traceback before
421 It can thus be used by programs which need to process the traceback before
422 printing (such as console replacements based on the code module from the
422 printing (such as console replacements based on the code module from the
423 standard library).
423 standard library).
424
424
425 Because they are meant to be called without a full traceback (only a
425 Because they are meant to be called without a full traceback (only a
426 list), instances of this class can't call the interactive pdb debugger."""
426 list), instances of this class can't call the interactive pdb debugger."""
427
427
428
428
429 def __call__(self, etype, value, elist):
429 def __call__(self, etype, value, elist):
430 self.ostream.flush()
430 self.ostream.flush()
431 self.ostream.write(self.text(etype, value, elist))
431 self.ostream.write(self.text(etype, value, elist))
432 self.ostream.write('\n')
432 self.ostream.write('\n')
433
433
434 def _extract_tb(self, tb):
434 def _extract_tb(self, tb):
435 if tb:
435 if tb:
436 return traceback.extract_tb(tb)
436 return traceback.extract_tb(tb)
437 else:
437 else:
438 return None
438 return None
439
439
440 def structured_traceback(
440 def structured_traceback(
441 self,
441 self,
442 etype: type,
442 etype: type,
443 evalue: Optional[BaseException],
443 evalue: Optional[BaseException],
444 etb: Optional[TracebackType] = None,
444 etb: Optional[TracebackType] = None,
445 tb_offset: Optional[int] = None,
445 tb_offset: Optional[int] = None,
446 context=5,
446 context=5,
447 ):
447 ):
448 """Return a color formatted string with the traceback info.
448 """Return a color formatted string with the traceback info.
449
449
450 Parameters
450 Parameters
451 ----------
451 ----------
452 etype : exception type
452 etype : exception type
453 Type of the exception raised.
453 Type of the exception raised.
454 evalue : object
454 evalue : object
455 Data stored in the exception
455 Data stored in the exception
456 etb : list | TracebackType | None
456 etb : list | TracebackType | None
457 If list: List of frames, see class docstring for details.
457 If list: List of frames, see class docstring for details.
458 If Traceback: Traceback of the exception.
458 If Traceback: Traceback of the exception.
459 tb_offset : int, optional
459 tb_offset : int, optional
460 Number of frames in the traceback to skip. If not given, the
460 Number of frames in the traceback to skip. If not given, the
461 instance evalue is used (set in constructor).
461 instance evalue is used (set in constructor).
462 context : int, optional
462 context : int, optional
463 Number of lines of context information to print.
463 Number of lines of context information to print.
464
464
465 Returns
465 Returns
466 -------
466 -------
467 String with formatted exception.
467 String with formatted exception.
468 """
468 """
469 # This is a workaround to get chained_exc_ids in recursive calls
469 # This is a workaround to get chained_exc_ids in recursive calls
470 # etb should not be a tuple if structured_traceback is not recursive
470 # etb should not be a tuple if structured_traceback is not recursive
471 if isinstance(etb, tuple):
471 if isinstance(etb, tuple):
472 etb, chained_exc_ids = etb
472 etb, chained_exc_ids = etb
473 else:
473 else:
474 chained_exc_ids = set()
474 chained_exc_ids = set()
475
475
476 if isinstance(etb, list):
476 if isinstance(etb, list):
477 elist = etb
477 elist = etb
478 elif etb is not None:
478 elif etb is not None:
479 elist = self._extract_tb(etb)
479 elist = self._extract_tb(etb)
480 else:
480 else:
481 elist = []
481 elist = []
482 tb_offset = self.tb_offset if tb_offset is None else tb_offset
482 tb_offset = self.tb_offset if tb_offset is None else tb_offset
483 assert isinstance(tb_offset, int)
483 assert isinstance(tb_offset, int)
484 Colors = self.Colors
484 Colors = self.Colors
485 out_list = []
485 out_list = []
486 if elist:
486 if elist:
487
487
488 if tb_offset and len(elist) > tb_offset:
488 if tb_offset and len(elist) > tb_offset:
489 elist = elist[tb_offset:]
489 elist = elist[tb_offset:]
490
490
491 out_list.append('Traceback %s(most recent call last)%s:' %
491 out_list.append('Traceback %s(most recent call last)%s:' %
492 (Colors.normalEm, Colors.Normal) + '\n')
492 (Colors.normalEm, Colors.Normal) + '\n')
493 out_list.extend(self._format_list(elist))
493 out_list.extend(self._format_list(elist))
494 # The exception info should be a single entry in the list.
494 # The exception info should be a single entry in the list.
495 lines = ''.join(self._format_exception_only(etype, evalue))
495 lines = ''.join(self._format_exception_only(etype, evalue))
496 out_list.append(lines)
496 out_list.append(lines)
497
497
498 exception = self.get_parts_of_chained_exception(evalue)
498 exception = self.get_parts_of_chained_exception(evalue)
499
499
500 if exception and not id(exception[1]) in chained_exc_ids:
500 if exception and not id(exception[1]) in chained_exc_ids:
501 chained_exception_message = (
501 chained_exception_message = (
502 self.prepare_chained_exception_message(evalue.__cause__)[0]
502 self.prepare_chained_exception_message(evalue.__cause__)[0]
503 if evalue is not None
503 if evalue is not None
504 else ""
504 else ""
505 )
505 )
506 etype, evalue, etb = exception
506 etype, evalue, etb = exception
507 # Trace exception to avoid infinite 'cause' loop
507 # Trace exception to avoid infinite 'cause' loop
508 chained_exc_ids.add(id(exception[1]))
508 chained_exc_ids.add(id(exception[1]))
509 chained_exceptions_tb_offset = 0
509 chained_exceptions_tb_offset = 0
510 out_list = (
510 out_list = (
511 self.structured_traceback(
511 self.structured_traceback(
512 etype, evalue, (etb, chained_exc_ids),
512 etype, evalue, (etb, chained_exc_ids),
513 chained_exceptions_tb_offset, context)
513 chained_exceptions_tb_offset, context)
514 + chained_exception_message
514 + chained_exception_message
515 + out_list)
515 + out_list)
516
516
517 return out_list
517 return out_list
518
518
519 def _format_list(self, extracted_list):
519 def _format_list(self, extracted_list):
520 """Format a list of traceback entry tuples for printing.
520 """Format a list of traceback entry tuples for printing.
521
521
522 Given a list of tuples as returned by extract_tb() or
522 Given a list of tuples as returned by extract_tb() or
523 extract_stack(), return a list of strings ready for printing.
523 extract_stack(), return a list of strings ready for printing.
524 Each string in the resulting list corresponds to the item with the
524 Each string in the resulting list corresponds to the item with the
525 same index in the argument list. Each string ends in a newline;
525 same index in the argument list. Each string ends in a newline;
526 the strings may contain internal newlines as well, for those items
526 the strings may contain internal newlines as well, for those items
527 whose source text line is not None.
527 whose source text line is not None.
528
528
529 Lifted almost verbatim from traceback.py
529 Lifted almost verbatim from traceback.py
530 """
530 """
531
531
532 Colors = self.Colors
532 Colors = self.Colors
533 list = []
533 list = []
534 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
534 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
535 normalCol, nameCol, fileCol, lineCol = (
535 normalCol, nameCol, fileCol, lineCol = (
536 # Emphasize the last entry
536 # Emphasize the last entry
537 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
537 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
538 if ind == len(extracted_list) - 1
538 if ind == len(extracted_list) - 1
539 else (Colors.Normal, Colors.name, Colors.filename, "")
539 else (Colors.Normal, Colors.name, Colors.filename, "")
540 )
540 )
541
541
542 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
542 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
543 item = f"{normalCol} {fns}"
543 item = f"{normalCol} {fns}"
544
544
545 if name != "<module>":
545 if name != "<module>":
546 item += f" in {nameCol}{name}{normalCol}\n"
546 item += f" in {nameCol}{name}{normalCol}\n"
547 else:
547 else:
548 item += "\n"
548 item += "\n"
549 if line:
549 if line:
550 item += f"{lineCol} {line.strip()}{normalCol}\n"
550 item += f"{lineCol} {line.strip()}{normalCol}\n"
551 list.append(item)
551 list.append(item)
552
552
553 return list
553 return list
554
554
555 def _format_exception_only(self, etype, value):
555 def _format_exception_only(self, etype, value):
556 """Format the exception part of a traceback.
556 """Format the exception part of a traceback.
557
557
558 The arguments are the exception type and value such as given by
558 The arguments are the exception type and value such as given by
559 sys.exc_info()[:2]. The return value is a list of strings, each ending
559 sys.exc_info()[:2]. The return value is a list of strings, each ending
560 in a newline. Normally, the list contains a single string; however,
560 in a newline. Normally, the list contains a single string; however,
561 for SyntaxError exceptions, it contains several lines that (when
561 for SyntaxError exceptions, it contains several lines that (when
562 printed) display detailed information about where the syntax error
562 printed) display detailed information about where the syntax error
563 occurred. The message indicating which exception occurred is the
563 occurred. The message indicating which exception occurred is the
564 always last string in the list.
564 always last string in the list.
565
565
566 Also lifted nearly verbatim from traceback.py
566 Also lifted nearly verbatim from traceback.py
567 """
567 """
568 have_filedata = False
568 have_filedata = False
569 Colors = self.Colors
569 Colors = self.Colors
570 list = []
570 list = []
571 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
571 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
572 if value is None:
572 if value is None:
573 # Not sure if this can still happen in Python 2.6 and above
573 # Not sure if this can still happen in Python 2.6 and above
574 list.append(stype + '\n')
574 list.append(stype + '\n')
575 else:
575 else:
576 if issubclass(etype, SyntaxError):
576 if issubclass(etype, SyntaxError):
577 have_filedata = True
577 have_filedata = True
578 if not value.filename: value.filename = "<string>"
578 if not value.filename: value.filename = "<string>"
579 if value.lineno:
579 if value.lineno:
580 lineno = value.lineno
580 lineno = value.lineno
581 textline = linecache.getline(value.filename, value.lineno)
581 textline = linecache.getline(value.filename, value.lineno)
582 else:
582 else:
583 lineno = "unknown"
583 lineno = "unknown"
584 textline = ""
584 textline = ""
585 list.append(
585 list.append(
586 "%s %s%s\n"
586 "%s %s%s\n"
587 % (
587 % (
588 Colors.normalEm,
588 Colors.normalEm,
589 _format_filename(
589 _format_filename(
590 value.filename,
590 value.filename,
591 Colors.filenameEm,
591 Colors.filenameEm,
592 Colors.normalEm,
592 Colors.normalEm,
593 lineno=(None if lineno == "unknown" else lineno),
593 lineno=(None if lineno == "unknown" else lineno),
594 ),
594 ),
595 Colors.Normal,
595 Colors.Normal,
596 )
596 )
597 )
597 )
598 if textline == "":
598 if textline == "":
599 textline = py3compat.cast_unicode(value.text, "utf-8")
599 textline = py3compat.cast_unicode(value.text, "utf-8")
600
600
601 if textline is not None:
601 if textline is not None:
602 i = 0
602 i = 0
603 while i < len(textline) and textline[i].isspace():
603 while i < len(textline) and textline[i].isspace():
604 i += 1
604 i += 1
605 list.append('%s %s%s\n' % (Colors.line,
605 list.append('%s %s%s\n' % (Colors.line,
606 textline.strip(),
606 textline.strip(),
607 Colors.Normal))
607 Colors.Normal))
608 if value.offset is not None:
608 if value.offset is not None:
609 s = ' '
609 s = ' '
610 for c in textline[i:value.offset - 1]:
610 for c in textline[i:value.offset - 1]:
611 if c.isspace():
611 if c.isspace():
612 s += c
612 s += c
613 else:
613 else:
614 s += ' '
614 s += ' '
615 list.append('%s%s^%s\n' % (Colors.caret, s,
615 list.append('%s%s^%s\n' % (Colors.caret, s,
616 Colors.Normal))
616 Colors.Normal))
617
617
618 try:
618 try:
619 s = value.msg
619 s = value.msg
620 except Exception:
620 except Exception:
621 s = self._some_str(value)
621 s = self._some_str(value)
622 if s:
622 if s:
623 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
623 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
624 Colors.Normal, s))
624 Colors.Normal, s))
625 else:
625 else:
626 list.append('%s\n' % stype)
626 list.append('%s\n' % stype)
627
627
628 # sync with user hooks
628 # sync with user hooks
629 if have_filedata:
629 if have_filedata:
630 ipinst = get_ipython()
630 ipinst = get_ipython()
631 if ipinst is not None:
631 if ipinst is not None:
632 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
632 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
633
633
634 return list
634 return list
635
635
636 def get_exception_only(self, etype, value):
636 def get_exception_only(self, etype, value):
637 """Only print the exception type and message, without a traceback.
637 """Only print the exception type and message, without a traceback.
638
638
639 Parameters
639 Parameters
640 ----------
640 ----------
641 etype : exception type
641 etype : exception type
642 value : exception value
642 value : exception value
643 """
643 """
644 return ListTB.structured_traceback(self, etype, value)
644 return ListTB.structured_traceback(self, etype, value)
645
645
646 def show_exception_only(self, etype, evalue):
646 def show_exception_only(self, etype, evalue):
647 """Only print the exception type and message, without a traceback.
647 """Only print the exception type and message, without a traceback.
648
648
649 Parameters
649 Parameters
650 ----------
650 ----------
651 etype : exception type
651 etype : exception type
652 evalue : exception value
652 evalue : exception value
653 """
653 """
654 # This method needs to use __call__ from *this* class, not the one from
654 # This method needs to use __call__ from *this* class, not the one from
655 # a subclass whose signature or behavior may be different
655 # a subclass whose signature or behavior may be different
656 ostream = self.ostream
656 ostream = self.ostream
657 ostream.flush()
657 ostream.flush()
658 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
658 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
659 ostream.flush()
659 ostream.flush()
660
660
661 def _some_str(self, value):
661 def _some_str(self, value):
662 # Lifted from traceback.py
662 # Lifted from traceback.py
663 try:
663 try:
664 return py3compat.cast_unicode(str(value))
664 return py3compat.cast_unicode(str(value))
665 except:
665 except:
666 return u'<unprintable %s object>' % type(value).__name__
666 return u'<unprintable %s object>' % type(value).__name__
667
667
668
668
669 class FrameInfo:
669 class FrameInfo:
670 """
670 """
671 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
671 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
672 really long frames.
672 really long frames.
673 """
673 """
674
674
675 description: Optional[str]
675 description: Optional[str]
676 filename: str
676 filename: str
677 lineno: int
677 lineno: int
678
678
679 @classmethod
679 @classmethod
680 def _from_stack_data_FrameInfo(cls, frame_info):
680 def _from_stack_data_FrameInfo(cls, frame_info):
681 return cls(
681 return cls(
682 getattr(frame_info, "description", None),
682 getattr(frame_info, "description", None),
683 getattr(frame_info, "filename", None),
683 getattr(frame_info, "filename", None),
684 getattr(frame_info, "lineno", None),
684 getattr(frame_info, "lineno", None),
685 getattr(frame_info, "frame", None),
685 getattr(frame_info, "frame", None),
686 getattr(frame_info, "code", None),
686 getattr(frame_info, "code", None),
687 sd=frame_info,
687 sd=frame_info,
688 )
688 )
689
689
690 def __init__(self, description, filename, lineno, frame, code, sd=None):
690 def __init__(self, description, filename, lineno, frame, code, sd=None):
691 self.description = description
691 self.description = description
692 self.filename = filename
692 self.filename = filename
693 self.lineno = lineno
693 self.lineno = lineno
694 self.frame = frame
694 self.frame = frame
695 self.code = code
695 self.code = code
696 self._sd = sd
696 self._sd = sd
697
697
698 # self.lines = []
698 # self.lines = []
699 if sd is None:
699 if sd is None:
700 ix = inspect.getsourcelines(frame)
700 ix = inspect.getsourcelines(frame)
701 self.raw_lines = ix[0]
701 self.raw_lines = ix[0]
702
702
703 @property
703 @property
704 def variables_in_executing_piece(self):
704 def variables_in_executing_piece(self):
705 if self._sd:
705 if self._sd:
706 return self._sd.variables_in_executing_piece
706 return self._sd.variables_in_executing_piece
707 else:
707 else:
708 return []
708 return []
709
709
710 @property
710 @property
711 def lines(self):
711 def lines(self):
712 return self._sd.lines
712 return self._sd.lines
713
713
714 @property
714 @property
715 def executing(self):
715 def executing(self):
716 if self._sd:
716 if self._sd:
717 return self._sd.executing
717 return self._sd.executing
718 else:
718 else:
719 return None
719 return None
720
720
721
721
722 # ----------------------------------------------------------------------------
722 # ----------------------------------------------------------------------------
723 class VerboseTB(TBTools):
723 class VerboseTB(TBTools):
724 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
724 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
725 of HTML. Requires inspect and pydoc. Crazy, man.
725 of HTML. Requires inspect and pydoc. Crazy, man.
726
726
727 Modified version which optionally strips the topmost entries from the
727 Modified version which optionally strips the topmost entries from the
728 traceback, to be used with alternate interpreters (because their own code
728 traceback, to be used with alternate interpreters (because their own code
729 would appear in the traceback)."""
729 would appear in the traceback)."""
730
730
731 _tb_highlight = "bg:ansiyellow"
731 _tb_highlight = "bg:ansiyellow"
732
732
733 def __init__(
733 def __init__(
734 self,
734 self,
735 color_scheme: str = "Linux",
735 color_scheme: str = "Linux",
736 call_pdb: bool = False,
736 call_pdb: bool = False,
737 ostream=None,
737 ostream=None,
738 tb_offset: int = 0,
738 tb_offset: int = 0,
739 long_header: bool = False,
739 long_header: bool = False,
740 include_vars: bool = True,
740 include_vars: bool = True,
741 check_cache=None,
741 check_cache=None,
742 debugger_cls=None,
742 debugger_cls=None,
743 parent=None,
743 parent=None,
744 config=None,
744 config=None,
745 ):
745 ):
746 """Specify traceback offset, headers and color scheme.
746 """Specify traceback offset, headers and color scheme.
747
747
748 Define how many frames to drop from the tracebacks. Calling it with
748 Define how many frames to drop from the tracebacks. Calling it with
749 tb_offset=1 allows use of this handler in interpreters which will have
749 tb_offset=1 allows use of this handler in interpreters which will have
750 their own code at the top of the traceback (VerboseTB will first
750 their own code at the top of the traceback (VerboseTB will first
751 remove that frame before printing the traceback info)."""
751 remove that frame before printing the traceback info)."""
752 TBTools.__init__(
752 TBTools.__init__(
753 self,
753 self,
754 color_scheme=color_scheme,
754 color_scheme=color_scheme,
755 call_pdb=call_pdb,
755 call_pdb=call_pdb,
756 ostream=ostream,
756 ostream=ostream,
757 parent=parent,
757 parent=parent,
758 config=config,
758 config=config,
759 debugger_cls=debugger_cls,
759 debugger_cls=debugger_cls,
760 )
760 )
761 self.tb_offset = tb_offset
761 self.tb_offset = tb_offset
762 self.long_header = long_header
762 self.long_header = long_header
763 self.include_vars = include_vars
763 self.include_vars = include_vars
764 # By default we use linecache.checkcache, but the user can provide a
764 # By default we use linecache.checkcache, but the user can provide a
765 # different check_cache implementation. This was formerly used by the
765 # different check_cache implementation. This was formerly used by the
766 # IPython kernel for interactive code, but is no longer necessary.
766 # IPython kernel for interactive code, but is no longer necessary.
767 if check_cache is None:
767 if check_cache is None:
768 check_cache = linecache.checkcache
768 check_cache = linecache.checkcache
769 self.check_cache = check_cache
769 self.check_cache = check_cache
770
770
771 self.skip_hidden = True
771 self.skip_hidden = True
772
772
773 def format_record(self, frame_info: FrameInfo):
773 def format_record(self, frame_info: FrameInfo):
774 """Format a single stack frame"""
774 """Format a single stack frame"""
775 assert isinstance(frame_info, FrameInfo)
775 assert isinstance(frame_info, FrameInfo)
776 Colors = self.Colors # just a shorthand + quicker name lookup
776 Colors = self.Colors # just a shorthand + quicker name lookup
777 ColorsNormal = Colors.Normal # used a lot
777 ColorsNormal = Colors.Normal # used a lot
778
778
779 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
779 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
780 return ' %s[... skipping similar frames: %s]%s\n' % (
780 return ' %s[... skipping similar frames: %s]%s\n' % (
781 Colors.excName, frame_info.description, ColorsNormal)
781 Colors.excName, frame_info.description, ColorsNormal)
782
782
783 indent = " " * INDENT_SIZE
783 indent = " " * INDENT_SIZE
784 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
784 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
785 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
785 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
786 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
786 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
787 Colors.vName,
787 Colors.vName,
788 Colors.valEm,
788 Colors.valEm,
789 ColorsNormal,
789 ColorsNormal,
790 )
790 )
791 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
791 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
792
792
793 link = _format_filename(
793 link = _format_filename(
794 frame_info.filename,
794 frame_info.filename,
795 Colors.filenameEm,
795 Colors.filenameEm,
796 ColorsNormal,
796 ColorsNormal,
797 lineno=frame_info.lineno,
797 lineno=frame_info.lineno,
798 )
798 )
799 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
799 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
800 if frame_info.executing is not None:
800 if frame_info.executing is not None:
801 func = frame_info.executing.code_qualname()
801 func = frame_info.executing.code_qualname()
802 else:
802 else:
803 func = "?"
803 func = "?"
804 if func == "<module>":
804 if func == "<module>":
805 call = ""
805 call = ""
806 else:
806 else:
807 # Decide whether to include variable details or not
807 # Decide whether to include variable details or not
808 var_repr = eqrepr if self.include_vars else nullrepr
808 var_repr = eqrepr if self.include_vars else nullrepr
809 try:
809 try:
810 scope = inspect.formatargvalues(
810 scope = inspect.formatargvalues(
811 args, varargs, varkw, locals_, formatvalue=var_repr
811 args, varargs, varkw, locals_, formatvalue=var_repr
812 )
812 )
813 call = tpl_call.format(file=func, scope=scope)
813 call = tpl_call.format(file=func, scope=scope)
814 except KeyError:
814 except KeyError:
815 # This happens in situations like errors inside generator
815 # This happens in situations like errors inside generator
816 # expressions, where local variables are listed in the
816 # expressions, where local variables are listed in the
817 # line, but can't be extracted from the frame. I'm not
817 # line, but can't be extracted from the frame. I'm not
818 # 100% sure this isn't actually a bug in inspect itself,
818 # 100% sure this isn't actually a bug in inspect itself,
819 # but since there's no info for us to compute with, the
819 # but since there's no info for us to compute with, the
820 # best we can do is report the failure and move on. Here
820 # best we can do is report the failure and move on. Here
821 # we must *not* call any traceback construction again,
821 # we must *not* call any traceback construction again,
822 # because that would mess up use of %debug later on. So we
822 # because that would mess up use of %debug later on. So we
823 # simply report the failure and move on. The only
823 # simply report the failure and move on. The only
824 # limitation will be that this frame won't have locals
824 # limitation will be that this frame won't have locals
825 # listed in the call signature. Quite subtle problem...
825 # listed in the call signature. Quite subtle problem...
826 # I can't think of a good way to validate this in a unit
826 # I can't think of a good way to validate this in a unit
827 # test, but running a script consisting of:
827 # test, but running a script consisting of:
828 # dict( (k,v.strip()) for (k,v) in range(10) )
828 # dict( (k,v.strip()) for (k,v) in range(10) )
829 # will illustrate the error, if this exception catch is
829 # will illustrate the error, if this exception catch is
830 # disabled.
830 # disabled.
831 call = tpl_call_fail % func
831 call = tpl_call_fail % func
832
832
833 lvals = ''
833 lvals = ''
834 lvals_list = []
834 lvals_list = []
835 if self.include_vars:
835 if self.include_vars:
836 try:
836 try:
837 # we likely want to fix stackdata at some point, but
837 # we likely want to fix stackdata at some point, but
838 # still need a workaround.
838 # still need a workaround.
839 fibp = frame_info.variables_in_executing_piece
839 fibp = frame_info.variables_in_executing_piece
840 for var in fibp:
840 for var in fibp:
841 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
841 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
842 except Exception:
842 except Exception:
843 lvals_list.append(
843 lvals_list.append(
844 "Exception trying to inspect frame. No more locals available."
844 "Exception trying to inspect frame. No more locals available."
845 )
845 )
846 if lvals_list:
846 if lvals_list:
847 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
847 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
848
848
849 result = f'{link}{", " if call else ""}{call}\n'
849 result = f'{link}{", " if call else ""}{call}\n'
850 if frame_info._sd is None:
850 if frame_info._sd is None:
851 assert False
851 assert False
852 # fast fallback if file is too long
852 # fast fallback if file is too long
853 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
853 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
854 link = tpl_link % util_path.compress_user(frame_info.filename)
854 link = tpl_link % util_path.compress_user(frame_info.filename)
855 level = "%s %s\n" % (link, call)
855 level = "%s %s\n" % (link, call)
856 _line_format = PyColorize.Parser(
856 _line_format = PyColorize.Parser(
857 style=self.color_scheme_table.active_scheme_name, parent=self
857 style=self.color_scheme_table.active_scheme_name, parent=self
858 ).format2
858 ).format2
859 first_line = frame_info.code.co_firstlineno
859 first_line = frame_info.code.co_firstlineno
860 current_line = frame_info.lineno[0]
860 current_line = frame_info.lineno[0]
861 return "%s%s" % (
861 return "%s%s" % (
862 level,
862 level,
863 "".join(
863 "".join(
864 _simple_format_traceback_lines(
864 _simple_format_traceback_lines(
865 current_line,
865 current_line,
866 current_line - first_line,
866 current_line - first_line,
867 frame_info.raw_lines,
867 frame_info.raw_lines,
868 Colors,
868 Colors,
869 lvals,
869 lvals,
870 _line_format,
870 _line_format,
871 )
871 )
872 ),
872 ),
873 )
873 )
874 # result += "\n".join(frame_info.raw_lines)
874 # result += "\n".join(frame_info.raw_lines)
875 else:
875 else:
876 result += "".join(
876 result += "".join(
877 _format_traceback_lines(
877 _format_traceback_lines(
878 frame_info.lines, Colors, self.has_colors, lvals
878 frame_info.lines, Colors, self.has_colors, lvals
879 )
879 )
880 )
880 )
881 return result
881 return result
882
882
883 def prepare_header(self, etype: str, long_version: bool = False):
883 def prepare_header(self, etype: str, long_version: bool = False):
884 colors = self.Colors # just a shorthand + quicker name lookup
884 colors = self.Colors # just a shorthand + quicker name lookup
885 colorsnormal = colors.Normal # used a lot
885 colorsnormal = colors.Normal # used a lot
886 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
886 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
887 width = min(75, get_terminal_size()[0])
887 width = min(75, get_terminal_size()[0])
888 if long_version:
888 if long_version:
889 # Header with the exception type, python version, and date
889 # Header with the exception type, python version, and date
890 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
890 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
891 date = time.ctime(time.time())
891 date = time.ctime(time.time())
892
892
893 head = "%s%s%s\n%s%s%s\n%s" % (
893 head = "%s%s%s\n%s%s%s\n%s" % (
894 colors.topline,
894 colors.topline,
895 "-" * width,
895 "-" * width,
896 colorsnormal,
896 colorsnormal,
897 exc,
897 exc,
898 " " * (width - len(etype) - len(pyver)),
898 " " * (width - len(etype) - len(pyver)),
899 pyver,
899 pyver,
900 date.rjust(width),
900 date.rjust(width),
901 )
901 )
902 head += (
902 head += (
903 "\nA problem occurred executing Python code. Here is the sequence of function"
903 "\nA problem occurred executing Python code. Here is the sequence of function"
904 "\ncalls leading up to the error, with the most recent (innermost) call last."
904 "\ncalls leading up to the error, with the most recent (innermost) call last."
905 )
905 )
906 else:
906 else:
907 # Simplified header
907 # Simplified header
908 head = "%s%s" % (
908 head = "%s%s" % (
909 exc,
909 exc,
910 "Traceback (most recent call last)".rjust(width - len(etype)),
910 "Traceback (most recent call last)".rjust(width - len(etype)),
911 )
911 )
912
912
913 return head
913 return head
914
914
915 def format_exception(self, etype, evalue):
915 def format_exception(self, etype, evalue):
916 colors = self.Colors # just a shorthand + quicker name lookup
916 colors = self.Colors # just a shorthand + quicker name lookup
917 colorsnormal = colors.Normal # used a lot
917 colorsnormal = colors.Normal # used a lot
918 # Get (safely) a string form of the exception info
918 # Get (safely) a string form of the exception info
919 try:
919 try:
920 etype_str, evalue_str = map(str, (etype, evalue))
920 etype_str, evalue_str = map(str, (etype, evalue))
921 except:
921 except:
922 # User exception is improperly defined.
922 # User exception is improperly defined.
923 etype, evalue = str, sys.exc_info()[:2]
923 etype, evalue = str, sys.exc_info()[:2]
924 etype_str, evalue_str = map(str, (etype, evalue))
924 etype_str, evalue_str = map(str, (etype, evalue))
925 # ... and format it
925 # ... and format it
926 return ['%s%s%s: %s' % (colors.excName, etype_str,
926 return ['%s%s%s: %s' % (colors.excName, etype_str,
927 colorsnormal, py3compat.cast_unicode(evalue_str))]
927 colorsnormal, py3compat.cast_unicode(evalue_str))]
928
928
929 def format_exception_as_a_whole(
929 def format_exception_as_a_whole(
930 self,
930 self,
931 etype: type,
931 etype: type,
932 evalue: Optional[BaseException],
932 evalue: Optional[BaseException],
933 etb: Optional[TracebackType],
933 etb: Optional[TracebackType],
934 number_of_lines_of_context,
934 number_of_lines_of_context,
935 tb_offset: Optional[int],
935 tb_offset: Optional[int],
936 ):
936 ):
937 """Formats the header, traceback and exception message for a single exception.
937 """Formats the header, traceback and exception message for a single exception.
938
938
939 This may be called multiple times by Python 3 exception chaining
939 This may be called multiple times by Python 3 exception chaining
940 (PEP 3134).
940 (PEP 3134).
941 """
941 """
942 # some locals
942 # some locals
943 orig_etype = etype
943 orig_etype = etype
944 try:
944 try:
945 etype = etype.__name__
945 etype = etype.__name__
946 except AttributeError:
946 except AttributeError:
947 pass
947 pass
948
948
949 tb_offset = self.tb_offset if tb_offset is None else tb_offset
949 tb_offset = self.tb_offset if tb_offset is None else tb_offset
950 assert isinstance(tb_offset, int)
950 assert isinstance(tb_offset, int)
951 head = self.prepare_header(etype, self.long_header)
951 head = self.prepare_header(etype, self.long_header)
952 records = (
952 records = (
953 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
953 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
954 )
954 )
955
955
956 frames = []
956 frames = []
957 skipped = 0
957 skipped = 0
958 lastrecord = len(records) - 1
958 lastrecord = len(records) - 1
959 for i, record in enumerate(records):
959 for i, record in enumerate(records):
960 if (
960 if (
961 not isinstance(record._sd, stack_data.RepeatedFrames)
961 not isinstance(record._sd, stack_data.RepeatedFrames)
962 and self.skip_hidden
962 and self.skip_hidden
963 ):
963 ):
964 if (
964 if (
965 record.frame.f_locals.get("__tracebackhide__", 0)
965 record.frame.f_locals.get("__tracebackhide__", 0)
966 and i != lastrecord
966 and i != lastrecord
967 ):
967 ):
968 skipped += 1
968 skipped += 1
969 continue
969 continue
970 if skipped:
970 if skipped:
971 Colors = self.Colors # just a shorthand + quicker name lookup
971 Colors = self.Colors # just a shorthand + quicker name lookup
972 ColorsNormal = Colors.Normal # used a lot
972 ColorsNormal = Colors.Normal # used a lot
973 frames.append(
973 frames.append(
974 " %s[... skipping hidden %s frame]%s\n"
974 " %s[... skipping hidden %s frame]%s\n"
975 % (Colors.excName, skipped, ColorsNormal)
975 % (Colors.excName, skipped, ColorsNormal)
976 )
976 )
977 skipped = 0
977 skipped = 0
978 frames.append(self.format_record(record))
978 frames.append(self.format_record(record))
979 if skipped:
979 if skipped:
980 Colors = self.Colors # just a shorthand + quicker name lookup
980 Colors = self.Colors # just a shorthand + quicker name lookup
981 ColorsNormal = Colors.Normal # used a lot
981 ColorsNormal = Colors.Normal # used a lot
982 frames.append(
982 frames.append(
983 " %s[... skipping hidden %s frame]%s\n"
983 " %s[... skipping hidden %s frame]%s\n"
984 % (Colors.excName, skipped, ColorsNormal)
984 % (Colors.excName, skipped, ColorsNormal)
985 )
985 )
986
986
987 formatted_exception = self.format_exception(etype, evalue)
987 formatted_exception = self.format_exception(etype, evalue)
988 if records:
988 if records:
989 frame_info = records[-1]
989 frame_info = records[-1]
990 ipinst = get_ipython()
990 ipinst = get_ipython()
991 if ipinst is not None:
991 if ipinst is not None:
992 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
992 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
993
993
994 return [[head] + frames + [''.join(formatted_exception[0])]]
994 return [[head] + frames + [''.join(formatted_exception[0])]]
995
995
996 def get_records(
996 def get_records(
997 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
997 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
998 ):
998 ):
999 assert etb is not None
999 assert etb is not None
1000 context = number_of_lines_of_context - 1
1000 context = number_of_lines_of_context - 1
1001 after = context // 2
1001 after = context // 2
1002 before = context - after
1002 before = context - after
1003 if self.has_colors:
1003 if self.has_colors:
1004 style = get_style_by_name("default")
1004 style = get_style_by_name("default")
1005 style = stack_data.style_with_executing_node(style, self._tb_highlight)
1005 style = stack_data.style_with_executing_node(style, self._tb_highlight)
1006 formatter = Terminal256Formatter(style=style)
1006 formatter = Terminal256Formatter(style=style)
1007 else:
1007 else:
1008 formatter = None
1008 formatter = None
1009 options = stack_data.Options(
1009 options = stack_data.Options(
1010 before=before,
1010 before=before,
1011 after=after,
1011 after=after,
1012 pygments_formatter=formatter,
1012 pygments_formatter=formatter,
1013 )
1013 )
1014
1014
1015 # Let's estimate the amount of code we will have to parse/highlight.
1015 # Let's estimate the amount of code we will have to parse/highlight.
1016 cf: Optional[TracebackType] = etb
1016 cf: Optional[TracebackType] = etb
1017 max_len = 0
1017 max_len = 0
1018 tbs = []
1018 tbs = []
1019 while cf is not None:
1019 while cf is not None:
1020 source_file = inspect.getsourcefile(etb.tb_frame)
1020 try:
1021 lines, first = inspect.getsourcelines(etb.tb_frame)
1021 source_file = inspect.getsourcefile(etb.tb_frame)
1022 lines, first = inspect.getsourcelines(etb.tb_frame)
1023 except OSError:
1024 max_len = float("-inf")
1025 break
1022 max_len = max(max_len, first + len(lines))
1026 max_len = max(max_len, first + len(lines))
1023 tbs.append(cf)
1027 tbs.append(cf)
1024 cf = cf.tb_next
1028 cf = cf.tb_next
1025
1029
1026 if max_len > FAST_THRESHOLD:
1030 if max_len > FAST_THRESHOLD:
1027 FIs = []
1031 FIs = []
1028 for tb in tbs:
1032 for tb in tbs:
1029 frame = tb.tb_frame
1033 frame = tb.tb_frame
1030 lineno = (frame.f_lineno,)
1034 lineno = (frame.f_lineno,)
1031 code = frame.f_code
1035 code = frame.f_code
1032 filename = code.co_filename
1036 filename = code.co_filename
1033 FIs.append(FrameInfo("Raw frame", filename, lineno, frame, code))
1037 FIs.append(FrameInfo("Raw frame", filename, lineno, frame, code))
1034 return FIs
1038 return FIs
1035 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1039 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1036 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1040 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1037 return res
1041 return res
1038
1042
1039 def structured_traceback(
1043 def structured_traceback(
1040 self,
1044 self,
1041 etype: type,
1045 etype: type,
1042 evalue: Optional[BaseException],
1046 evalue: Optional[BaseException],
1043 etb: Optional[TracebackType],
1047 etb: Optional[TracebackType],
1044 tb_offset: Optional[int] = None,
1048 tb_offset: Optional[int] = None,
1045 number_of_lines_of_context: int = 5,
1049 number_of_lines_of_context: int = 5,
1046 ):
1050 ):
1047 """Return a nice text document describing the traceback."""
1051 """Return a nice text document describing the traceback."""
1048 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1052 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1049 tb_offset)
1053 tb_offset)
1050
1054
1051 colors = self.Colors # just a shorthand + quicker name lookup
1055 colors = self.Colors # just a shorthand + quicker name lookup
1052 colorsnormal = colors.Normal # used a lot
1056 colorsnormal = colors.Normal # used a lot
1053 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1057 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1054 structured_traceback_parts = [head]
1058 structured_traceback_parts = [head]
1055 chained_exceptions_tb_offset = 0
1059 chained_exceptions_tb_offset = 0
1056 lines_of_context = 3
1060 lines_of_context = 3
1057 formatted_exceptions = formatted_exception
1061 formatted_exceptions = formatted_exception
1058 exception = self.get_parts_of_chained_exception(evalue)
1062 exception = self.get_parts_of_chained_exception(evalue)
1059 if exception:
1063 if exception:
1060 assert evalue is not None
1064 assert evalue is not None
1061 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1065 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1062 etype, evalue, etb = exception
1066 etype, evalue, etb = exception
1063 else:
1067 else:
1064 evalue = None
1068 evalue = None
1065 chained_exc_ids = set()
1069 chained_exc_ids = set()
1066 while evalue:
1070 while evalue:
1067 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1071 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1068 chained_exceptions_tb_offset)
1072 chained_exceptions_tb_offset)
1069 exception = self.get_parts_of_chained_exception(evalue)
1073 exception = self.get_parts_of_chained_exception(evalue)
1070
1074
1071 if exception and not id(exception[1]) in chained_exc_ids:
1075 if exception and not id(exception[1]) in chained_exc_ids:
1072 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1076 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1073 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1077 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1074 etype, evalue, etb = exception
1078 etype, evalue, etb = exception
1075 else:
1079 else:
1076 evalue = None
1080 evalue = None
1077
1081
1078 # we want to see exceptions in a reversed order:
1082 # we want to see exceptions in a reversed order:
1079 # the first exception should be on top
1083 # the first exception should be on top
1080 for formatted_exception in reversed(formatted_exceptions):
1084 for formatted_exception in reversed(formatted_exceptions):
1081 structured_traceback_parts += formatted_exception
1085 structured_traceback_parts += formatted_exception
1082
1086
1083 return structured_traceback_parts
1087 return structured_traceback_parts
1084
1088
1085 def debugger(self, force: bool = False):
1089 def debugger(self, force: bool = False):
1086 """Call up the pdb debugger if desired, always clean up the tb
1090 """Call up the pdb debugger if desired, always clean up the tb
1087 reference.
1091 reference.
1088
1092
1089 Keywords:
1093 Keywords:
1090
1094
1091 - force(False): by default, this routine checks the instance call_pdb
1095 - force(False): by default, this routine checks the instance call_pdb
1092 flag and does not actually invoke the debugger if the flag is false.
1096 flag and does not actually invoke the debugger if the flag is false.
1093 The 'force' option forces the debugger to activate even if the flag
1097 The 'force' option forces the debugger to activate even if the flag
1094 is false.
1098 is false.
1095
1099
1096 If the call_pdb flag is set, the pdb interactive debugger is
1100 If the call_pdb flag is set, the pdb interactive debugger is
1097 invoked. In all cases, the self.tb reference to the current traceback
1101 invoked. In all cases, the self.tb reference to the current traceback
1098 is deleted to prevent lingering references which hamper memory
1102 is deleted to prevent lingering references which hamper memory
1099 management.
1103 management.
1100
1104
1101 Note that each call to pdb() does an 'import readline', so if your app
1105 Note that each call to pdb() does an 'import readline', so if your app
1102 requires a special setup for the readline completers, you'll have to
1106 requires a special setup for the readline completers, you'll have to
1103 fix that by hand after invoking the exception handler."""
1107 fix that by hand after invoking the exception handler."""
1104
1108
1105 if force or self.call_pdb:
1109 if force or self.call_pdb:
1106 if self.pdb is None:
1110 if self.pdb is None:
1107 self.pdb = self.debugger_cls()
1111 self.pdb = self.debugger_cls()
1108 # the system displayhook may have changed, restore the original
1112 # the system displayhook may have changed, restore the original
1109 # for pdb
1113 # for pdb
1110 display_trap = DisplayTrap(hook=sys.__displayhook__)
1114 display_trap = DisplayTrap(hook=sys.__displayhook__)
1111 with display_trap:
1115 with display_trap:
1112 self.pdb.reset()
1116 self.pdb.reset()
1113 # Find the right frame so we don't pop up inside ipython itself
1117 # Find the right frame so we don't pop up inside ipython itself
1114 if hasattr(self, 'tb') and self.tb is not None:
1118 if hasattr(self, 'tb') and self.tb is not None:
1115 etb = self.tb
1119 etb = self.tb
1116 else:
1120 else:
1117 etb = self.tb = sys.last_traceback
1121 etb = self.tb = sys.last_traceback
1118 while self.tb is not None and self.tb.tb_next is not None:
1122 while self.tb is not None and self.tb.tb_next is not None:
1119 assert self.tb.tb_next is not None
1123 assert self.tb.tb_next is not None
1120 self.tb = self.tb.tb_next
1124 self.tb = self.tb.tb_next
1121 if etb and etb.tb_next:
1125 if etb and etb.tb_next:
1122 etb = etb.tb_next
1126 etb = etb.tb_next
1123 self.pdb.botframe = etb.tb_frame
1127 self.pdb.botframe = etb.tb_frame
1124 self.pdb.interaction(None, etb)
1128 self.pdb.interaction(None, etb)
1125
1129
1126 if hasattr(self, 'tb'):
1130 if hasattr(self, 'tb'):
1127 del self.tb
1131 del self.tb
1128
1132
1129 def handler(self, info=None):
1133 def handler(self, info=None):
1130 (etype, evalue, etb) = info or sys.exc_info()
1134 (etype, evalue, etb) = info or sys.exc_info()
1131 self.tb = etb
1135 self.tb = etb
1132 ostream = self.ostream
1136 ostream = self.ostream
1133 ostream.flush()
1137 ostream.flush()
1134 ostream.write(self.text(etype, evalue, etb))
1138 ostream.write(self.text(etype, evalue, etb))
1135 ostream.write('\n')
1139 ostream.write('\n')
1136 ostream.flush()
1140 ostream.flush()
1137
1141
1138 # Changed so an instance can just be called as VerboseTB_inst() and print
1142 # Changed so an instance can just be called as VerboseTB_inst() and print
1139 # out the right info on its own.
1143 # out the right info on its own.
1140 def __call__(self, etype=None, evalue=None, etb=None):
1144 def __call__(self, etype=None, evalue=None, etb=None):
1141 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1145 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1142 if etb is None:
1146 if etb is None:
1143 self.handler()
1147 self.handler()
1144 else:
1148 else:
1145 self.handler((etype, evalue, etb))
1149 self.handler((etype, evalue, etb))
1146 try:
1150 try:
1147 self.debugger()
1151 self.debugger()
1148 except KeyboardInterrupt:
1152 except KeyboardInterrupt:
1149 print("\nKeyboardInterrupt")
1153 print("\nKeyboardInterrupt")
1150
1154
1151
1155
1152 #----------------------------------------------------------------------------
1156 #----------------------------------------------------------------------------
1153 class FormattedTB(VerboseTB, ListTB):
1157 class FormattedTB(VerboseTB, ListTB):
1154 """Subclass ListTB but allow calling with a traceback.
1158 """Subclass ListTB but allow calling with a traceback.
1155
1159
1156 It can thus be used as a sys.excepthook for Python > 2.1.
1160 It can thus be used as a sys.excepthook for Python > 2.1.
1157
1161
1158 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1162 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1159
1163
1160 Allows a tb_offset to be specified. This is useful for situations where
1164 Allows a tb_offset to be specified. This is useful for situations where
1161 one needs to remove a number of topmost frames from the traceback (such as
1165 one needs to remove a number of topmost frames from the traceback (such as
1162 occurs with python programs that themselves execute other python code,
1166 occurs with python programs that themselves execute other python code,
1163 like Python shells). """
1167 like Python shells). """
1164
1168
1165 mode: str
1169 mode: str
1166
1170
1167 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1171 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1168 ostream=None,
1172 ostream=None,
1169 tb_offset=0, long_header=False, include_vars=False,
1173 tb_offset=0, long_header=False, include_vars=False,
1170 check_cache=None, debugger_cls=None,
1174 check_cache=None, debugger_cls=None,
1171 parent=None, config=None):
1175 parent=None, config=None):
1172
1176
1173 # NEVER change the order of this list. Put new modes at the end:
1177 # NEVER change the order of this list. Put new modes at the end:
1174 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
1178 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
1175 self.verbose_modes = self.valid_modes[1:3]
1179 self.verbose_modes = self.valid_modes[1:3]
1176
1180
1177 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1181 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1178 ostream=ostream, tb_offset=tb_offset,
1182 ostream=ostream, tb_offset=tb_offset,
1179 long_header=long_header, include_vars=include_vars,
1183 long_header=long_header, include_vars=include_vars,
1180 check_cache=check_cache, debugger_cls=debugger_cls,
1184 check_cache=check_cache, debugger_cls=debugger_cls,
1181 parent=parent, config=config)
1185 parent=parent, config=config)
1182
1186
1183 # Different types of tracebacks are joined with different separators to
1187 # Different types of tracebacks are joined with different separators to
1184 # form a single string. They are taken from this dict
1188 # form a single string. They are taken from this dict
1185 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1189 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1186 Minimal='')
1190 Minimal='')
1187 # set_mode also sets the tb_join_char attribute
1191 # set_mode also sets the tb_join_char attribute
1188 self.set_mode(mode)
1192 self.set_mode(mode)
1189
1193
1190 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1194 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1191 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1195 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1192 mode = self.mode
1196 mode = self.mode
1193 if mode in self.verbose_modes:
1197 if mode in self.verbose_modes:
1194 # Verbose modes need a full traceback
1198 # Verbose modes need a full traceback
1195 return VerboseTB.structured_traceback(
1199 return VerboseTB.structured_traceback(
1196 self, etype, value, tb, tb_offset, number_of_lines_of_context
1200 self, etype, value, tb, tb_offset, number_of_lines_of_context
1197 )
1201 )
1198 elif mode == 'Minimal':
1202 elif mode == 'Minimal':
1199 return ListTB.get_exception_only(self, etype, value)
1203 return ListTB.get_exception_only(self, etype, value)
1200 else:
1204 else:
1201 # We must check the source cache because otherwise we can print
1205 # We must check the source cache because otherwise we can print
1202 # out-of-date source code.
1206 # out-of-date source code.
1203 self.check_cache()
1207 self.check_cache()
1204 # Now we can extract and format the exception
1208 # Now we can extract and format the exception
1205 return ListTB.structured_traceback(
1209 return ListTB.structured_traceback(
1206 self, etype, value, tb, tb_offset, number_of_lines_of_context
1210 self, etype, value, tb, tb_offset, number_of_lines_of_context
1207 )
1211 )
1208
1212
1209 def stb2text(self, stb):
1213 def stb2text(self, stb):
1210 """Convert a structured traceback (a list) to a string."""
1214 """Convert a structured traceback (a list) to a string."""
1211 return self.tb_join_char.join(stb)
1215 return self.tb_join_char.join(stb)
1212
1216
1213 def set_mode(self, mode: Optional[str] = None):
1217 def set_mode(self, mode: Optional[str] = None):
1214 """Switch to the desired mode.
1218 """Switch to the desired mode.
1215
1219
1216 If mode is not specified, cycles through the available modes."""
1220 If mode is not specified, cycles through the available modes."""
1217
1221
1218 if not mode:
1222 if not mode:
1219 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1223 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1220 len(self.valid_modes)
1224 len(self.valid_modes)
1221 self.mode = self.valid_modes[new_idx]
1225 self.mode = self.valid_modes[new_idx]
1222 elif mode not in self.valid_modes:
1226 elif mode not in self.valid_modes:
1223 raise ValueError(
1227 raise ValueError(
1224 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1228 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1225 "Valid modes: " + str(self.valid_modes)
1229 "Valid modes: " + str(self.valid_modes)
1226 )
1230 )
1227 else:
1231 else:
1228 assert isinstance(mode, str)
1232 assert isinstance(mode, str)
1229 self.mode = mode
1233 self.mode = mode
1230 # include variable details only in 'Verbose' mode
1234 # include variable details only in 'Verbose' mode
1231 self.include_vars = (self.mode == self.valid_modes[2])
1235 self.include_vars = (self.mode == self.valid_modes[2])
1232 # Set the join character for generating text tracebacks
1236 # Set the join character for generating text tracebacks
1233 self.tb_join_char = self._join_chars[self.mode]
1237 self.tb_join_char = self._join_chars[self.mode]
1234
1238
1235 # some convenient shortcuts
1239 # some convenient shortcuts
1236 def plain(self):
1240 def plain(self):
1237 self.set_mode(self.valid_modes[0])
1241 self.set_mode(self.valid_modes[0])
1238
1242
1239 def context(self):
1243 def context(self):
1240 self.set_mode(self.valid_modes[1])
1244 self.set_mode(self.valid_modes[1])
1241
1245
1242 def verbose(self):
1246 def verbose(self):
1243 self.set_mode(self.valid_modes[2])
1247 self.set_mode(self.valid_modes[2])
1244
1248
1245 def minimal(self):
1249 def minimal(self):
1246 self.set_mode(self.valid_modes[3])
1250 self.set_mode(self.valid_modes[3])
1247
1251
1248
1252
1249 #----------------------------------------------------------------------------
1253 #----------------------------------------------------------------------------
1250 class AutoFormattedTB(FormattedTB):
1254 class AutoFormattedTB(FormattedTB):
1251 """A traceback printer which can be called on the fly.
1255 """A traceback printer which can be called on the fly.
1252
1256
1253 It will find out about exceptions by itself.
1257 It will find out about exceptions by itself.
1254
1258
1255 A brief example::
1259 A brief example::
1256
1260
1257 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1261 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1258 try:
1262 try:
1259 ...
1263 ...
1260 except:
1264 except:
1261 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1265 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1262 """
1266 """
1263
1267
1264 def __call__(self, etype=None, evalue=None, etb=None,
1268 def __call__(self, etype=None, evalue=None, etb=None,
1265 out=None, tb_offset=None):
1269 out=None, tb_offset=None):
1266 """Print out a formatted exception traceback.
1270 """Print out a formatted exception traceback.
1267
1271
1268 Optional arguments:
1272 Optional arguments:
1269 - out: an open file-like object to direct output to.
1273 - out: an open file-like object to direct output to.
1270
1274
1271 - tb_offset: the number of frames to skip over in the stack, on a
1275 - tb_offset: the number of frames to skip over in the stack, on a
1272 per-call basis (this overrides temporarily the instance's tb_offset
1276 per-call basis (this overrides temporarily the instance's tb_offset
1273 given at initialization time."""
1277 given at initialization time."""
1274
1278
1275 if out is None:
1279 if out is None:
1276 out = self.ostream
1280 out = self.ostream
1277 out.flush()
1281 out.flush()
1278 out.write(self.text(etype, evalue, etb, tb_offset))
1282 out.write(self.text(etype, evalue, etb, tb_offset))
1279 out.write('\n')
1283 out.write('\n')
1280 out.flush()
1284 out.flush()
1281 # FIXME: we should remove the auto pdb behavior from here and leave
1285 # FIXME: we should remove the auto pdb behavior from here and leave
1282 # that to the clients.
1286 # that to the clients.
1283 try:
1287 try:
1284 self.debugger()
1288 self.debugger()
1285 except KeyboardInterrupt:
1289 except KeyboardInterrupt:
1286 print("\nKeyboardInterrupt")
1290 print("\nKeyboardInterrupt")
1287
1291
1288 def structured_traceback(
1292 def structured_traceback(
1289 self,
1293 self,
1290 etype=None,
1294 etype=None,
1291 value=None,
1295 value=None,
1292 tb=None,
1296 tb=None,
1293 tb_offset=None,
1297 tb_offset=None,
1294 number_of_lines_of_context=5,
1298 number_of_lines_of_context=5,
1295 ):
1299 ):
1296 etype: type
1300 etype: type
1297 value: BaseException
1301 value: BaseException
1298 # tb: TracebackType or tupleof tb types ?
1302 # tb: TracebackType or tupleof tb types ?
1299 if etype is None:
1303 if etype is None:
1300 etype, value, tb = sys.exc_info()
1304 etype, value, tb = sys.exc_info()
1301 if isinstance(tb, tuple):
1305 if isinstance(tb, tuple):
1302 # tb is a tuple if this is a chained exception.
1306 # tb is a tuple if this is a chained exception.
1303 self.tb = tb[0]
1307 self.tb = tb[0]
1304 else:
1308 else:
1305 self.tb = tb
1309 self.tb = tb
1306 return FormattedTB.structured_traceback(
1310 return FormattedTB.structured_traceback(
1307 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1311 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1308
1312
1309
1313
1310 #---------------------------------------------------------------------------
1314 #---------------------------------------------------------------------------
1311
1315
1312 # A simple class to preserve Nathan's original functionality.
1316 # A simple class to preserve Nathan's original functionality.
1313 class ColorTB(FormattedTB):
1317 class ColorTB(FormattedTB):
1314 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1318 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1315
1319
1316 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1320 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1317 FormattedTB.__init__(self, color_scheme=color_scheme,
1321 FormattedTB.__init__(self, color_scheme=color_scheme,
1318 call_pdb=call_pdb, **kwargs)
1322 call_pdb=call_pdb, **kwargs)
1319
1323
1320
1324
1321 class SyntaxTB(ListTB):
1325 class SyntaxTB(ListTB):
1322 """Extension which holds some state: the last exception value"""
1326 """Extension which holds some state: the last exception value"""
1323
1327
1324 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1328 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1325 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1329 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1326 self.last_syntax_error = None
1330 self.last_syntax_error = None
1327
1331
1328 def __call__(self, etype, value, elist):
1332 def __call__(self, etype, value, elist):
1329 self.last_syntax_error = value
1333 self.last_syntax_error = value
1330
1334
1331 ListTB.__call__(self, etype, value, elist)
1335 ListTB.__call__(self, etype, value, elist)
1332
1336
1333 def structured_traceback(self, etype, value, elist, tb_offset=None,
1337 def structured_traceback(self, etype, value, elist, tb_offset=None,
1334 context=5):
1338 context=5):
1335 # If the source file has been edited, the line in the syntax error can
1339 # If the source file has been edited, the line in the syntax error can
1336 # be wrong (retrieved from an outdated cache). This replaces it with
1340 # be wrong (retrieved from an outdated cache). This replaces it with
1337 # the current value.
1341 # the current value.
1338 if isinstance(value, SyntaxError) \
1342 if isinstance(value, SyntaxError) \
1339 and isinstance(value.filename, str) \
1343 and isinstance(value.filename, str) \
1340 and isinstance(value.lineno, int):
1344 and isinstance(value.lineno, int):
1341 linecache.checkcache(value.filename)
1345 linecache.checkcache(value.filename)
1342 newtext = linecache.getline(value.filename, value.lineno)
1346 newtext = linecache.getline(value.filename, value.lineno)
1343 if newtext:
1347 if newtext:
1344 value.text = newtext
1348 value.text = newtext
1345 self.last_syntax_error = value
1349 self.last_syntax_error = value
1346 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1350 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1347 tb_offset=tb_offset, context=context)
1351 tb_offset=tb_offset, context=context)
1348
1352
1349 def clear_err_state(self):
1353 def clear_err_state(self):
1350 """Return the current error state and clear it"""
1354 """Return the current error state and clear it"""
1351 e = self.last_syntax_error
1355 e = self.last_syntax_error
1352 self.last_syntax_error = None
1356 self.last_syntax_error = None
1353 return e
1357 return e
1354
1358
1355 def stb2text(self, stb):
1359 def stb2text(self, stb):
1356 """Convert a structured traceback (a list) to a string."""
1360 """Convert a structured traceback (a list) to a string."""
1357 return ''.join(stb)
1361 return ''.join(stb)
1358
1362
1359
1363
1360 # some internal-use functions
1364 # some internal-use functions
1361 def text_repr(value):
1365 def text_repr(value):
1362 """Hopefully pretty robust repr equivalent."""
1366 """Hopefully pretty robust repr equivalent."""
1363 # this is pretty horrible but should always return *something*
1367 # this is pretty horrible but should always return *something*
1364 try:
1368 try:
1365 return pydoc.text.repr(value)
1369 return pydoc.text.repr(value)
1366 except KeyboardInterrupt:
1370 except KeyboardInterrupt:
1367 raise
1371 raise
1368 except:
1372 except:
1369 try:
1373 try:
1370 return repr(value)
1374 return repr(value)
1371 except KeyboardInterrupt:
1375 except KeyboardInterrupt:
1372 raise
1376 raise
1373 except:
1377 except:
1374 try:
1378 try:
1375 # all still in an except block so we catch
1379 # all still in an except block so we catch
1376 # getattr raising
1380 # getattr raising
1377 name = getattr(value, '__name__', None)
1381 name = getattr(value, '__name__', None)
1378 if name:
1382 if name:
1379 # ick, recursion
1383 # ick, recursion
1380 return text_repr(name)
1384 return text_repr(name)
1381 klass = getattr(value, '__class__', None)
1385 klass = getattr(value, '__class__', None)
1382 if klass:
1386 if klass:
1383 return '%s instance' % text_repr(klass)
1387 return '%s instance' % text_repr(klass)
1384 except KeyboardInterrupt:
1388 except KeyboardInterrupt:
1385 raise
1389 raise
1386 except:
1390 except:
1387 return 'UNRECOVERABLE REPR FAILURE'
1391 return 'UNRECOVERABLE REPR FAILURE'
1388
1392
1389
1393
1390 def eqrepr(value, repr=text_repr):
1394 def eqrepr(value, repr=text_repr):
1391 return '=%s' % repr(value)
1395 return '=%s' % repr(value)
1392
1396
1393
1397
1394 def nullrepr(value, repr=text_repr):
1398 def nullrepr(value, repr=text_repr):
1395 return ''
1399 return ''
General Comments 0
You need to be logged in to leave comments. Login now