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