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