##// END OF EJS Templates
remove empty __init_
Matthias Bussonnier -
Show More
@@ -1,1210 +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 = 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 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
364 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
365 ostream=ostream, parent=parent,config=config)
366
363
367 def __call__(self, etype, value, elist):
364 def __call__(self, etype, value, elist):
368 self.ostream.flush()
365 self.ostream.flush()
369 self.ostream.write(self.text(etype, value, elist))
366 self.ostream.write(self.text(etype, value, elist))
370 self.ostream.write('\n')
367 self.ostream.write('\n')
371
368
372 def _extract_tb(self, tb):
369 def _extract_tb(self, tb):
373 if tb:
370 if tb:
374 return traceback.extract_tb(tb)
371 return traceback.extract_tb(tb)
375 else:
372 else:
376 return None
373 return None
377
374
378 def structured_traceback(
375 def structured_traceback(
379 self,
376 self,
380 etype: type,
377 etype: type,
381 evalue: BaseException,
378 evalue: BaseException,
382 etb: Optional[TracebackType] = None,
379 etb: Optional[TracebackType] = None,
383 tb_offset: Optional[int] = None,
380 tb_offset: Optional[int] = None,
384 context=5,
381 context=5,
385 ):
382 ):
386 """Return a color formatted string with the traceback info.
383 """Return a color formatted string with the traceback info.
387
384
388 Parameters
385 Parameters
389 ----------
386 ----------
390 etype : exception type
387 etype : exception type
391 Type of the exception raised.
388 Type of the exception raised.
392 evalue : object
389 evalue : object
393 Data stored in the exception
390 Data stored in the exception
394 etb : list | TracebackType | None
391 etb : list | TracebackType | None
395 If list: List of frames, see class docstring for details.
392 If list: List of frames, see class docstring for details.
396 If Traceback: Traceback of the exception.
393 If Traceback: Traceback of the exception.
397 tb_offset : int, optional
394 tb_offset : int, optional
398 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
399 instance evalue is used (set in constructor).
396 instance evalue is used (set in constructor).
400 context : int, optional
397 context : int, optional
401 Number of lines of context information to print.
398 Number of lines of context information to print.
402
399
403 Returns
400 Returns
404 -------
401 -------
405 String with formatted exception.
402 String with formatted exception.
406 """
403 """
407 # 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
408 # 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
409 if isinstance(etb, tuple):
406 if isinstance(etb, tuple):
410 etb, chained_exc_ids = etb
407 etb, chained_exc_ids = etb
411 else:
408 else:
412 chained_exc_ids = set()
409 chained_exc_ids = set()
413
410
414 if isinstance(etb, list):
411 if isinstance(etb, list):
415 elist = etb
412 elist = etb
416 elif etb is not None:
413 elif etb is not None:
417 elist = self._extract_tb(etb)
414 elist = self._extract_tb(etb)
418 else:
415 else:
419 elist = []
416 elist = []
420 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
421 assert isinstance(tb_offset, int)
418 assert isinstance(tb_offset, int)
422 Colors = self.Colors
419 Colors = self.Colors
423 out_list = []
420 out_list = []
424 if elist:
421 if elist:
425
422
426 if tb_offset and len(elist) > tb_offset:
423 if tb_offset and len(elist) > tb_offset:
427 elist = elist[tb_offset:]
424 elist = elist[tb_offset:]
428
425
429 out_list.append('Traceback %s(most recent call last)%s:' %
426 out_list.append('Traceback %s(most recent call last)%s:' %
430 (Colors.normalEm, Colors.Normal) + '\n')
427 (Colors.normalEm, Colors.Normal) + '\n')
431 out_list.extend(self._format_list(elist))
428 out_list.extend(self._format_list(elist))
432 # The exception info should be a single entry in the list.
429 # The exception info should be a single entry in the list.
433 lines = ''.join(self._format_exception_only(etype, evalue))
430 lines = ''.join(self._format_exception_only(etype, evalue))
434 out_list.append(lines)
431 out_list.append(lines)
435
432
436 exception = self.get_parts_of_chained_exception(evalue)
433 exception = self.get_parts_of_chained_exception(evalue)
437
434
438 if exception and not id(exception[1]) in chained_exc_ids:
435 if exception and not id(exception[1]) in chained_exc_ids:
439 chained_exception_message = self.prepare_chained_exception_message(
436 chained_exception_message = self.prepare_chained_exception_message(
440 evalue.__cause__)[0]
437 evalue.__cause__)[0]
441 etype, evalue, etb = exception
438 etype, evalue, etb = exception
442 # Trace exception to avoid infinite 'cause' loop
439 # Trace exception to avoid infinite 'cause' loop
443 chained_exc_ids.add(id(exception[1]))
440 chained_exc_ids.add(id(exception[1]))
444 chained_exceptions_tb_offset = 0
441 chained_exceptions_tb_offset = 0
445 out_list = (
442 out_list = (
446 self.structured_traceback(
443 self.structured_traceback(
447 etype, evalue, (etb, chained_exc_ids),
444 etype, evalue, (etb, chained_exc_ids),
448 chained_exceptions_tb_offset, context)
445 chained_exceptions_tb_offset, context)
449 + chained_exception_message
446 + chained_exception_message
450 + out_list)
447 + out_list)
451
448
452 return out_list
449 return out_list
453
450
454 def _format_list(self, extracted_list):
451 def _format_list(self, extracted_list):
455 """Format a list of traceback entry tuples for printing.
452 """Format a list of traceback entry tuples for printing.
456
453
457 Given a list of tuples as returned by extract_tb() or
454 Given a list of tuples as returned by extract_tb() or
458 extract_stack(), return a list of strings ready for printing.
455 extract_stack(), return a list of strings ready for printing.
459 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
460 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;
461 the strings may contain internal newlines as well, for those items
458 the strings may contain internal newlines as well, for those items
462 whose source text line is not None.
459 whose source text line is not None.
463
460
464 Lifted almost verbatim from traceback.py
461 Lifted almost verbatim from traceback.py
465 """
462 """
466
463
467 Colors = self.Colors
464 Colors = self.Colors
468 list = []
465 list = []
469 for filename, lineno, name, line in extracted_list[:-1]:
466 for filename, lineno, name, line in extracted_list[:-1]:
470 item = " %s in %s%s%s\n" % (
467 item = " %s in %s%s%s\n" % (
471 _format_filename(
468 _format_filename(
472 filename, Colors.filename, Colors.Normal, lineno=lineno
469 filename, Colors.filename, Colors.Normal, lineno=lineno
473 ),
470 ),
474 Colors.name,
471 Colors.name,
475 name,
472 name,
476 Colors.Normal,
473 Colors.Normal,
477 )
474 )
478 if line:
475 if line:
479 item += ' %s\n' % line.strip()
476 item += ' %s\n' % line.strip()
480 list.append(item)
477 list.append(item)
481 # Emphasize the last entry
478 # Emphasize the last entry
482 filename, lineno, name, line = extracted_list[-1]
479 filename, lineno, name, line = extracted_list[-1]
483 item = "%s %s in %s%s%s%s\n" % (
480 item = "%s %s in %s%s%s%s\n" % (
484 Colors.normalEm,
481 Colors.normalEm,
485 _format_filename(
482 _format_filename(
486 filename, Colors.filenameEm, Colors.normalEm, lineno=lineno
483 filename, Colors.filenameEm, Colors.normalEm, lineno=lineno
487 ),
484 ),
488 Colors.nameEm,
485 Colors.nameEm,
489 name,
486 name,
490 Colors.normalEm,
487 Colors.normalEm,
491 Colors.Normal,
488 Colors.Normal,
492 )
489 )
493 if line:
490 if line:
494 item += '%s %s%s\n' % (Colors.line, line.strip(),
491 item += '%s %s%s\n' % (Colors.line, line.strip(),
495 Colors.Normal)
492 Colors.Normal)
496 list.append(item)
493 list.append(item)
497 return list
494 return list
498
495
499 def _format_exception_only(self, etype, value):
496 def _format_exception_only(self, etype, value):
500 """Format the exception part of a traceback.
497 """Format the exception part of a traceback.
501
498
502 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
503 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
504 in a newline. Normally, the list contains a single string; however,
501 in a newline. Normally, the list contains a single string; however,
505 for SyntaxError exceptions, it contains several lines that (when
502 for SyntaxError exceptions, it contains several lines that (when
506 printed) display detailed information about where the syntax error
503 printed) display detailed information about where the syntax error
507 occurred. The message indicating which exception occurred is the
504 occurred. The message indicating which exception occurred is the
508 always last string in the list.
505 always last string in the list.
509
506
510 Also lifted nearly verbatim from traceback.py
507 Also lifted nearly verbatim from traceback.py
511 """
508 """
512 have_filedata = False
509 have_filedata = False
513 Colors = self.Colors
510 Colors = self.Colors
514 list = []
511 list = []
515 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
512 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
516 if value is None:
513 if value is None:
517 # 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
518 list.append(stype + '\n')
515 list.append(stype + '\n')
519 else:
516 else:
520 if issubclass(etype, SyntaxError):
517 if issubclass(etype, SyntaxError):
521 have_filedata = True
518 have_filedata = True
522 if not value.filename: value.filename = "<string>"
519 if not value.filename: value.filename = "<string>"
523 if value.lineno:
520 if value.lineno:
524 lineno = value.lineno
521 lineno = value.lineno
525 textline = linecache.getline(value.filename, value.lineno)
522 textline = linecache.getline(value.filename, value.lineno)
526 else:
523 else:
527 lineno = "unknown"
524 lineno = "unknown"
528 textline = ""
525 textline = ""
529 list.append(
526 list.append(
530 "%s %s%s\n"
527 "%s %s%s\n"
531 % (
528 % (
532 Colors.normalEm,
529 Colors.normalEm,
533 _format_filename(
530 _format_filename(
534 value.filename,
531 value.filename,
535 Colors.filenameEm,
532 Colors.filenameEm,
536 Colors.normalEm,
533 Colors.normalEm,
537 lineno=(None if lineno == "unknown" else lineno),
534 lineno=(None if lineno == "unknown" else lineno),
538 ),
535 ),
539 Colors.Normal,
536 Colors.Normal,
540 )
537 )
541 )
538 )
542 if textline == "":
539 if textline == "":
543 textline = py3compat.cast_unicode(value.text, "utf-8")
540 textline = py3compat.cast_unicode(value.text, "utf-8")
544
541
545 if textline is not None:
542 if textline is not None:
546 i = 0
543 i = 0
547 while i < len(textline) and textline[i].isspace():
544 while i < len(textline) and textline[i].isspace():
548 i += 1
545 i += 1
549 list.append('%s %s%s\n' % (Colors.line,
546 list.append('%s %s%s\n' % (Colors.line,
550 textline.strip(),
547 textline.strip(),
551 Colors.Normal))
548 Colors.Normal))
552 if value.offset is not None:
549 if value.offset is not None:
553 s = ' '
550 s = ' '
554 for c in textline[i:value.offset - 1]:
551 for c in textline[i:value.offset - 1]:
555 if c.isspace():
552 if c.isspace():
556 s += c
553 s += c
557 else:
554 else:
558 s += ' '
555 s += ' '
559 list.append('%s%s^%s\n' % (Colors.caret, s,
556 list.append('%s%s^%s\n' % (Colors.caret, s,
560 Colors.Normal))
557 Colors.Normal))
561
558
562 try:
559 try:
563 s = value.msg
560 s = value.msg
564 except Exception:
561 except Exception:
565 s = self._some_str(value)
562 s = self._some_str(value)
566 if s:
563 if s:
567 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
564 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
568 Colors.Normal, s))
565 Colors.Normal, s))
569 else:
566 else:
570 list.append('%s\n' % stype)
567 list.append('%s\n' % stype)
571
568
572 # sync with user hooks
569 # sync with user hooks
573 if have_filedata:
570 if have_filedata:
574 ipinst = get_ipython()
571 ipinst = get_ipython()
575 if ipinst is not None:
572 if ipinst is not None:
576 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
573 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
577
574
578 return list
575 return list
579
576
580 def get_exception_only(self, etype, value):
577 def get_exception_only(self, etype, value):
581 """Only print the exception type and message, without a traceback.
578 """Only print the exception type and message, without a traceback.
582
579
583 Parameters
580 Parameters
584 ----------
581 ----------
585 etype : exception type
582 etype : exception type
586 value : exception value
583 value : exception value
587 """
584 """
588 return ListTB.structured_traceback(self, etype, value)
585 return ListTB.structured_traceback(self, etype, value)
589
586
590 def show_exception_only(self, etype, evalue):
587 def show_exception_only(self, etype, evalue):
591 """Only print the exception type and message, without a traceback.
588 """Only print the exception type and message, without a traceback.
592
589
593 Parameters
590 Parameters
594 ----------
591 ----------
595 etype : exception type
592 etype : exception type
596 evalue : exception value
593 evalue : exception value
597 """
594 """
598 # 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
599 # a subclass whose signature or behavior may be different
596 # a subclass whose signature or behavior may be different
600 ostream = self.ostream
597 ostream = self.ostream
601 ostream.flush()
598 ostream.flush()
602 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
599 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
603 ostream.flush()
600 ostream.flush()
604
601
605 def _some_str(self, value):
602 def _some_str(self, value):
606 # Lifted from traceback.py
603 # Lifted from traceback.py
607 try:
604 try:
608 return py3compat.cast_unicode(str(value))
605 return py3compat.cast_unicode(str(value))
609 except:
606 except:
610 return u'<unprintable %s object>' % type(value).__name__
607 return u'<unprintable %s object>' % type(value).__name__
611
608
612
609
613 #----------------------------------------------------------------------------
610 #----------------------------------------------------------------------------
614 class VerboseTB(TBTools):
611 class VerboseTB(TBTools):
615 """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
616 of HTML. Requires inspect and pydoc. Crazy, man.
613 of HTML. Requires inspect and pydoc. Crazy, man.
617
614
618 Modified version which optionally strips the topmost entries from the
615 Modified version which optionally strips the topmost entries from the
619 traceback, to be used with alternate interpreters (because their own code
616 traceback, to be used with alternate interpreters (because their own code
620 would appear in the traceback)."""
617 would appear in the traceback)."""
621
618
622 def __init__(
619 def __init__(
623 self,
620 self,
624 color_scheme: str = "Linux",
621 color_scheme: str = "Linux",
625 call_pdb: bool = False,
622 call_pdb: bool = False,
626 ostream=None,
623 ostream=None,
627 tb_offset: int = 0,
624 tb_offset: int = 0,
628 long_header: bool = False,
625 long_header: bool = False,
629 include_vars: bool = True,
626 include_vars: bool = True,
630 check_cache=None,
627 check_cache=None,
631 debugger_cls=None,
628 debugger_cls=None,
632 parent=None,
629 parent=None,
633 config=None,
630 config=None,
634 ):
631 ):
635 """Specify traceback offset, headers and color scheme.
632 """Specify traceback offset, headers and color scheme.
636
633
637 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
638 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
639 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
640 remove that frame before printing the traceback info)."""
637 remove that frame before printing the traceback info)."""
641 TBTools.__init__(
638 TBTools.__init__(
642 self,
639 self,
643 color_scheme=color_scheme,
640 color_scheme=color_scheme,
644 call_pdb=call_pdb,
641 call_pdb=call_pdb,
645 ostream=ostream,
642 ostream=ostream,
646 parent=parent,
643 parent=parent,
647 config=config,
644 config=config,
648 debugger_cls=debugger_cls,
645 debugger_cls=debugger_cls,
649 )
646 )
650 self.tb_offset = tb_offset
647 self.tb_offset = tb_offset
651 self.long_header = long_header
648 self.long_header = long_header
652 self.include_vars = include_vars
649 self.include_vars = include_vars
653 # 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
654 # different check_cache implementation. This is used by the IPython
651 # different check_cache implementation. This is used by the IPython
655 # kernel to provide tracebacks for interactive code that is cached,
652 # kernel to provide tracebacks for interactive code that is cached,
656 # by a compiler instance that flushes the linecache but preserves its
653 # by a compiler instance that flushes the linecache but preserves its
657 # own code cache.
654 # own code cache.
658 if check_cache is None:
655 if check_cache is None:
659 check_cache = linecache.checkcache
656 check_cache = linecache.checkcache
660 self.check_cache = check_cache
657 self.check_cache = check_cache
661
658
662 self.skip_hidden = True
659 self.skip_hidden = True
663
660
664 def format_record(self, frame_info):
661 def format_record(self, frame_info):
665 """Format a single stack frame"""
662 """Format a single stack frame"""
666 Colors = self.Colors # just a shorthand + quicker name lookup
663 Colors = self.Colors # just a shorthand + quicker name lookup
667 ColorsNormal = Colors.Normal # used a lot
664 ColorsNormal = Colors.Normal # used a lot
668
665
669 if isinstance(frame_info, stack_data.RepeatedFrames):
666 if isinstance(frame_info, stack_data.RepeatedFrames):
670 return ' %s[... skipping similar frames: %s]%s\n' % (
667 return ' %s[... skipping similar frames: %s]%s\n' % (
671 Colors.excName, frame_info.description, ColorsNormal)
668 Colors.excName, frame_info.description, ColorsNormal)
672
669
673 indent = " " * INDENT_SIZE
670 indent = " " * INDENT_SIZE
674 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
671 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
675 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
672 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
676 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
673 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
677 Colors.vName,
674 Colors.vName,
678 Colors.valEm,
675 Colors.valEm,
679 ColorsNormal,
676 ColorsNormal,
680 )
677 )
681 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
678 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
682
679
683 link = _format_filename(
680 link = _format_filename(
684 frame_info.filename,
681 frame_info.filename,
685 Colors.filenameEm,
682 Colors.filenameEm,
686 ColorsNormal,
683 ColorsNormal,
687 lineno=frame_info.lineno,
684 lineno=frame_info.lineno,
688 )
685 )
689 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
686 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
690
687
691 func = frame_info.executing.code_qualname()
688 func = frame_info.executing.code_qualname()
692 if func == "<module>":
689 if func == "<module>":
693 call = tpl_call.format(file=func, scope="")
690 call = tpl_call.format(file=func, scope="")
694 else:
691 else:
695 # Decide whether to include variable details or not
692 # Decide whether to include variable details or not
696 var_repr = eqrepr if self.include_vars else nullrepr
693 var_repr = eqrepr if self.include_vars else nullrepr
697 try:
694 try:
698 scope = inspect.formatargvalues(
695 scope = inspect.formatargvalues(
699 args, varargs, varkw, locals_, formatvalue=var_repr
696 args, varargs, varkw, locals_, formatvalue=var_repr
700 )
697 )
701 call = tpl_call.format(file=func, scope=scope)
698 call = tpl_call.format(file=func, scope=scope)
702 except KeyError:
699 except KeyError:
703 # This happens in situations like errors inside generator
700 # This happens in situations like errors inside generator
704 # expressions, where local variables are listed in the
701 # expressions, where local variables are listed in the
705 # 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
706 # 100% sure this isn't actually a bug in inspect itself,
703 # 100% sure this isn't actually a bug in inspect itself,
707 # 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
708 # 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
709 # we must *not* call any traceback construction again,
706 # we must *not* call any traceback construction again,
710 # 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
711 # simply report the failure and move on. The only
708 # simply report the failure and move on. The only
712 # limitation will be that this frame won't have locals
709 # limitation will be that this frame won't have locals
713 # listed in the call signature. Quite subtle problem...
710 # listed in the call signature. Quite subtle problem...
714 # 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
715 # test, but running a script consisting of:
712 # test, but running a script consisting of:
716 # dict( (k,v.strip()) for (k,v) in range(10) )
713 # dict( (k,v.strip()) for (k,v) in range(10) )
717 # will illustrate the error, if this exception catch is
714 # will illustrate the error, if this exception catch is
718 # disabled.
715 # disabled.
719 call = tpl_call_fail % func
716 call = tpl_call_fail % func
720
717
721 lvals = ''
718 lvals = ''
722 lvals_list = []
719 lvals_list = []
723 if self.include_vars:
720 if self.include_vars:
724 try:
721 try:
725 # we likely want to fix stackdata at some point, but
722 # we likely want to fix stackdata at some point, but
726 # still need a workaround.
723 # still need a workaround.
727 fibp = frame_info.variables_in_executing_piece
724 fibp = frame_info.variables_in_executing_piece
728 for var in fibp:
725 for var in fibp:
729 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
726 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
730 except Exception:
727 except Exception:
731 lvals_list.append(
728 lvals_list.append(
732 "Exception trying to inspect frame. No more locals available."
729 "Exception trying to inspect frame. No more locals available."
733 )
730 )
734 if lvals_list:
731 if lvals_list:
735 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
732 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
736
733
737 result = "%s, %s\n" % (link, call)
734 result = "%s, %s\n" % (link, call)
738
735
739 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))
740 return result
737 return result
741
738
742 def prepare_header(self, etype, long_version=False):
739 def prepare_header(self, etype, long_version=False):
743 colors = self.Colors # just a shorthand + quicker name lookup
740 colors = self.Colors # just a shorthand + quicker name lookup
744 colorsnormal = colors.Normal # used a lot
741 colorsnormal = colors.Normal # used a lot
745 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
742 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
746 width = min(75, get_terminal_size()[0])
743 width = min(75, get_terminal_size()[0])
747 if long_version:
744 if long_version:
748 # Header with the exception type, python version, and date
745 # Header with the exception type, python version, and date
749 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
746 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
750 date = time.ctime(time.time())
747 date = time.ctime(time.time())
751
748
752 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,
753 exc, ' ' * (width - len(str(etype)) - len(pyver)),
750 exc, ' ' * (width - len(str(etype)) - len(pyver)),
754 pyver, date.rjust(width) )
751 pyver, date.rjust(width) )
755 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" \
756 "\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."
757 else:
754 else:
758 # Simplified header
755 # Simplified header
759 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
756 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
760 rjust(width - len(str(etype))) )
757 rjust(width - len(str(etype))) )
761
758
762 return head
759 return head
763
760
764 def format_exception(self, etype, evalue):
761 def format_exception(self, etype, evalue):
765 colors = self.Colors # just a shorthand + quicker name lookup
762 colors = self.Colors # just a shorthand + quicker name lookup
766 colorsnormal = colors.Normal # used a lot
763 colorsnormal = colors.Normal # used a lot
767 # Get (safely) a string form of the exception info
764 # Get (safely) a string form of the exception info
768 try:
765 try:
769 etype_str, evalue_str = map(str, (etype, evalue))
766 etype_str, evalue_str = map(str, (etype, evalue))
770 except:
767 except:
771 # User exception is improperly defined.
768 # User exception is improperly defined.
772 etype, evalue = str, sys.exc_info()[:2]
769 etype, evalue = str, sys.exc_info()[:2]
773 etype_str, evalue_str = map(str, (etype, evalue))
770 etype_str, evalue_str = map(str, (etype, evalue))
774 # ... and format it
771 # ... and format it
775 return ['%s%s%s: %s' % (colors.excName, etype_str,
772 return ['%s%s%s: %s' % (colors.excName, etype_str,
776 colorsnormal, py3compat.cast_unicode(evalue_str))]
773 colorsnormal, py3compat.cast_unicode(evalue_str))]
777
774
778 def format_exception_as_a_whole(
775 def format_exception_as_a_whole(
779 self,
776 self,
780 etype: type,
777 etype: type,
781 evalue: BaseException,
778 evalue: BaseException,
782 etb: TracebackType,
779 etb: TracebackType,
783 number_of_lines_of_context,
780 number_of_lines_of_context,
784 tb_offset: Optional[int],
781 tb_offset: Optional[int],
785 ):
782 ):
786 """Formats the header, traceback and exception message for a single exception.
783 """Formats the header, traceback and exception message for a single exception.
787
784
788 This may be called multiple times by Python 3 exception chaining
785 This may be called multiple times by Python 3 exception chaining
789 (PEP 3134).
786 (PEP 3134).
790 """
787 """
791 assert etb is not None
788 assert etb is not None
792 # some locals
789 # some locals
793 orig_etype = etype
790 orig_etype = etype
794 try:
791 try:
795 etype = etype.__name__
792 etype = etype.__name__
796 except AttributeError:
793 except AttributeError:
797 pass
794 pass
798
795
799 tb_offset = self.tb_offset if tb_offset is None else tb_offset
796 tb_offset = self.tb_offset if tb_offset is None else tb_offset
800 assert isinstance(tb_offset, int)
797 assert isinstance(tb_offset, int)
801 head = self.prepare_header(etype, self.long_header)
798 head = self.prepare_header(etype, self.long_header)
802 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
799 records = self.get_records(etb, number_of_lines_of_context, tb_offset)
803
800
804 frames = []
801 frames = []
805 skipped = 0
802 skipped = 0
806 lastrecord = len(records) - 1
803 lastrecord = len(records) - 1
807 for i, r in enumerate(records):
804 for i, r in enumerate(records):
808 if not isinstance(r, stack_data.RepeatedFrames) and self.skip_hidden:
805 if not isinstance(r, stack_data.RepeatedFrames) and self.skip_hidden:
809 if r.frame.f_locals.get("__tracebackhide__", 0) and i != lastrecord:
806 if r.frame.f_locals.get("__tracebackhide__", 0) and i != lastrecord:
810 skipped += 1
807 skipped += 1
811 continue
808 continue
812 if skipped:
809 if skipped:
813 Colors = self.Colors # just a shorthand + quicker name lookup
810 Colors = self.Colors # just a shorthand + quicker name lookup
814 ColorsNormal = Colors.Normal # used a lot
811 ColorsNormal = Colors.Normal # used a lot
815 frames.append(
812 frames.append(
816 " %s[... skipping hidden %s frame]%s\n"
813 " %s[... skipping hidden %s frame]%s\n"
817 % (Colors.excName, skipped, ColorsNormal)
814 % (Colors.excName, skipped, ColorsNormal)
818 )
815 )
819 skipped = 0
816 skipped = 0
820 frames.append(self.format_record(r))
817 frames.append(self.format_record(r))
821 if skipped:
818 if skipped:
822 Colors = self.Colors # just a shorthand + quicker name lookup
819 Colors = self.Colors # just a shorthand + quicker name lookup
823 ColorsNormal = Colors.Normal # used a lot
820 ColorsNormal = Colors.Normal # used a lot
824 frames.append(
821 frames.append(
825 " %s[... skipping hidden %s frame]%s\n"
822 " %s[... skipping hidden %s frame]%s\n"
826 % (Colors.excName, skipped, ColorsNormal)
823 % (Colors.excName, skipped, ColorsNormal)
827 )
824 )
828
825
829 formatted_exception = self.format_exception(etype, evalue)
826 formatted_exception = self.format_exception(etype, evalue)
830 if records:
827 if records:
831 frame_info = records[-1]
828 frame_info = records[-1]
832 ipinst = get_ipython()
829 ipinst = get_ipython()
833 if ipinst is not None:
830 if ipinst is not None:
834 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
831 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
835
832
836 return [[head] + frames + [''.join(formatted_exception[0])]]
833 return [[head] + frames + [''.join(formatted_exception[0])]]
837
834
838 def get_records(
835 def get_records(
839 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
836 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
840 ):
837 ):
841 context = number_of_lines_of_context - 1
838 context = number_of_lines_of_context - 1
842 after = context // 2
839 after = context // 2
843 before = context - after
840 before = context - after
844 if self.has_colors:
841 if self.has_colors:
845 style = get_style_by_name("default")
842 style = get_style_by_name("default")
846 style = stack_data.style_with_executing_node(style, "bg:ansiyellow")
843 style = stack_data.style_with_executing_node(style, "bg:ansiyellow")
847 formatter = Terminal256Formatter(style=style)
844 formatter = Terminal256Formatter(style=style)
848 else:
845 else:
849 formatter = None
846 formatter = None
850 options = stack_data.Options(
847 options = stack_data.Options(
851 before=before,
848 before=before,
852 after=after,
849 after=after,
853 pygments_formatter=formatter,
850 pygments_formatter=formatter,
854 )
851 )
855 assert etb is not None
852 assert etb is not None
856 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
853 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
857
854
858 def structured_traceback(
855 def structured_traceback(
859 self,
856 self,
860 etype: type,
857 etype: type,
861 evalue: Optional[BaseException],
858 evalue: Optional[BaseException],
862 etb: TracebackType,
859 etb: TracebackType,
863 tb_offset: Optional[int] = None,
860 tb_offset: Optional[int] = None,
864 number_of_lines_of_context: int = 5,
861 number_of_lines_of_context: int = 5,
865 ):
862 ):
866 """Return a nice text document describing the traceback."""
863 """Return a nice text document describing the traceback."""
867 assert etb is not None
864 assert etb is not None
868 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,
869 tb_offset)
866 tb_offset)
870
867
871 colors = self.Colors # just a shorthand + quicker name lookup
868 colors = self.Colors # just a shorthand + quicker name lookup
872 colorsnormal = colors.Normal # used a lot
869 colorsnormal = colors.Normal # used a lot
873 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)
874 structured_traceback_parts = [head]
871 structured_traceback_parts = [head]
875 chained_exceptions_tb_offset = 0
872 chained_exceptions_tb_offset = 0
876 lines_of_context = 3
873 lines_of_context = 3
877 formatted_exceptions = formatted_exception
874 formatted_exceptions = formatted_exception
878 exception = self.get_parts_of_chained_exception(evalue)
875 exception = self.get_parts_of_chained_exception(evalue)
879 if exception:
876 if exception:
880 assert evalue is not None
877 assert evalue is not None
881 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
878 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
882 etype, evalue, etb = exception
879 etype, evalue, etb = exception
883 else:
880 else:
884 evalue = None
881 evalue = None
885 chained_exc_ids = set()
882 chained_exc_ids = set()
886 while evalue:
883 while evalue:
887 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,
888 chained_exceptions_tb_offset)
885 chained_exceptions_tb_offset)
889 exception = self.get_parts_of_chained_exception(evalue)
886 exception = self.get_parts_of_chained_exception(evalue)
890
887
891 if exception and not id(exception[1]) in chained_exc_ids:
888 if exception and not id(exception[1]) in chained_exc_ids:
892 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
893 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
890 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
894 etype, evalue, etb = exception
891 etype, evalue, etb = exception
895 else:
892 else:
896 evalue = None
893 evalue = None
897
894
898 # we want to see exceptions in a reversed order:
895 # we want to see exceptions in a reversed order:
899 # the first exception should be on top
896 # the first exception should be on top
900 for formatted_exception in reversed(formatted_exceptions):
897 for formatted_exception in reversed(formatted_exceptions):
901 structured_traceback_parts += formatted_exception
898 structured_traceback_parts += formatted_exception
902
899
903 return structured_traceback_parts
900 return structured_traceback_parts
904
901
905 def debugger(self, force: bool = False):
902 def debugger(self, force: bool = False):
906 """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
907 reference.
904 reference.
908
905
909 Keywords:
906 Keywords:
910
907
911 - force(False): by default, this routine checks the instance call_pdb
908 - force(False): by default, this routine checks the instance call_pdb
912 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.
913 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
914 is false.
911 is false.
915
912
916 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
917 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
918 is deleted to prevent lingering references which hamper memory
915 is deleted to prevent lingering references which hamper memory
919 management.
916 management.
920
917
921 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
922 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
923 fix that by hand after invoking the exception handler."""
920 fix that by hand after invoking the exception handler."""
924
921
925 if force or self.call_pdb:
922 if force or self.call_pdb:
926 if self.pdb is None:
923 if self.pdb is None:
927 self.pdb = self.debugger_cls()
924 self.pdb = self.debugger_cls()
928 # the system displayhook may have changed, restore the original
925 # the system displayhook may have changed, restore the original
929 # for pdb
926 # for pdb
930 display_trap = DisplayTrap(hook=sys.__displayhook__)
927 display_trap = DisplayTrap(hook=sys.__displayhook__)
931 with display_trap:
928 with display_trap:
932 self.pdb.reset()
929 self.pdb.reset()
933 # 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
934 if hasattr(self, 'tb') and self.tb is not None:
931 if hasattr(self, 'tb') and self.tb is not None:
935 etb = self.tb
932 etb = self.tb
936 else:
933 else:
937 etb = self.tb = sys.last_traceback
934 etb = self.tb = sys.last_traceback
938 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:
939 assert self.tb.tb_next is not None
936 assert self.tb.tb_next is not None
940 self.tb = self.tb.tb_next
937 self.tb = self.tb.tb_next
941 if etb and etb.tb_next:
938 if etb and etb.tb_next:
942 etb = etb.tb_next
939 etb = etb.tb_next
943 self.pdb.botframe = etb.tb_frame
940 self.pdb.botframe = etb.tb_frame
944 self.pdb.interaction(None, etb)
941 self.pdb.interaction(None, etb)
945
942
946 if hasattr(self, 'tb'):
943 if hasattr(self, 'tb'):
947 del self.tb
944 del self.tb
948
945
949 def handler(self, info=None):
946 def handler(self, info=None):
950 (etype, evalue, etb) = info or sys.exc_info()
947 (etype, evalue, etb) = info or sys.exc_info()
951 self.tb = etb
948 self.tb = etb
952 ostream = self.ostream
949 ostream = self.ostream
953 ostream.flush()
950 ostream.flush()
954 ostream.write(self.text(etype, evalue, etb))
951 ostream.write(self.text(etype, evalue, etb))
955 ostream.write('\n')
952 ostream.write('\n')
956 ostream.flush()
953 ostream.flush()
957
954
958 # 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
959 # out the right info on its own.
956 # out the right info on its own.
960 def __call__(self, etype=None, evalue=None, etb=None):
957 def __call__(self, etype=None, evalue=None, etb=None):
961 """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)."""
962 if etb is None:
959 if etb is None:
963 self.handler()
960 self.handler()
964 else:
961 else:
965 self.handler((etype, evalue, etb))
962 self.handler((etype, evalue, etb))
966 try:
963 try:
967 self.debugger()
964 self.debugger()
968 except KeyboardInterrupt:
965 except KeyboardInterrupt:
969 print("\nKeyboardInterrupt")
966 print("\nKeyboardInterrupt")
970
967
971
968
972 #----------------------------------------------------------------------------
969 #----------------------------------------------------------------------------
973 class FormattedTB(VerboseTB, ListTB):
970 class FormattedTB(VerboseTB, ListTB):
974 """Subclass ListTB but allow calling with a traceback.
971 """Subclass ListTB but allow calling with a traceback.
975
972
976 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.
977
974
978 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
975 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
979
976
980 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
981 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
982 occurs with python programs that themselves execute other python code,
979 occurs with python programs that themselves execute other python code,
983 like Python shells). """
980 like Python shells). """
984
981
985 mode: str
982 mode: str
986
983
987 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
984 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
988 ostream=None,
985 ostream=None,
989 tb_offset=0, long_header=False, include_vars=False,
986 tb_offset=0, long_header=False, include_vars=False,
990 check_cache=None, debugger_cls=None,
987 check_cache=None, debugger_cls=None,
991 parent=None, config=None):
988 parent=None, config=None):
992
989
993 # 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:
994 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
991 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
995 self.verbose_modes = self.valid_modes[1:3]
992 self.verbose_modes = self.valid_modes[1:3]
996
993
997 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
994 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
998 ostream=ostream, tb_offset=tb_offset,
995 ostream=ostream, tb_offset=tb_offset,
999 long_header=long_header, include_vars=include_vars,
996 long_header=long_header, include_vars=include_vars,
1000 check_cache=check_cache, debugger_cls=debugger_cls,
997 check_cache=check_cache, debugger_cls=debugger_cls,
1001 parent=parent, config=config)
998 parent=parent, config=config)
1002
999
1003 # Different types of tracebacks are joined with different separators to
1000 # Different types of tracebacks are joined with different separators to
1004 # form a single string. They are taken from this dict
1001 # form a single string. They are taken from this dict
1005 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1002 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1006 Minimal='')
1003 Minimal='')
1007 # set_mode also sets the tb_join_char attribute
1004 # set_mode also sets the tb_join_char attribute
1008 self.set_mode(mode)
1005 self.set_mode(mode)
1009
1006
1010 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):
1011 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
1012 mode = self.mode
1009 mode = self.mode
1013 if mode in self.verbose_modes:
1010 if mode in self.verbose_modes:
1014 # Verbose modes need a full traceback
1011 # Verbose modes need a full traceback
1015 return VerboseTB.structured_traceback(
1012 return VerboseTB.structured_traceback(
1016 self, etype, value, tb, tb_offset, number_of_lines_of_context
1013 self, etype, value, tb, tb_offset, number_of_lines_of_context
1017 )
1014 )
1018 elif mode == 'Minimal':
1015 elif mode == 'Minimal':
1019 return ListTB.get_exception_only(self, etype, value)
1016 return ListTB.get_exception_only(self, etype, value)
1020 else:
1017 else:
1021 # We must check the source cache because otherwise we can print
1018 # We must check the source cache because otherwise we can print
1022 # out-of-date source code.
1019 # out-of-date source code.
1023 self.check_cache()
1020 self.check_cache()
1024 # Now we can extract and format the exception
1021 # Now we can extract and format the exception
1025 return ListTB.structured_traceback(
1022 return ListTB.structured_traceback(
1026 self, etype, value, tb, tb_offset, number_of_lines_of_context
1023 self, etype, value, tb, tb_offset, number_of_lines_of_context
1027 )
1024 )
1028
1025
1029 def stb2text(self, stb):
1026 def stb2text(self, stb):
1030 """Convert a structured traceback (a list) to a string."""
1027 """Convert a structured traceback (a list) to a string."""
1031 return self.tb_join_char.join(stb)
1028 return self.tb_join_char.join(stb)
1032
1029
1033 def set_mode(self, mode: Optional[str] = None):
1030 def set_mode(self, mode: Optional[str] = None):
1034 """Switch to the desired mode.
1031 """Switch to the desired mode.
1035
1032
1036 If mode is not specified, cycles through the available modes."""
1033 If mode is not specified, cycles through the available modes."""
1037
1034
1038 if not mode:
1035 if not mode:
1039 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1036 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1040 len(self.valid_modes)
1037 len(self.valid_modes)
1041 self.mode = self.valid_modes[new_idx]
1038 self.mode = self.valid_modes[new_idx]
1042 elif mode not in self.valid_modes:
1039 elif mode not in self.valid_modes:
1043 raise ValueError(
1040 raise ValueError(
1044 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1041 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1045 "Valid modes: " + str(self.valid_modes)
1042 "Valid modes: " + str(self.valid_modes)
1046 )
1043 )
1047 else:
1044 else:
1048 assert isinstance(mode, str)
1045 assert isinstance(mode, str)
1049 self.mode = mode
1046 self.mode = mode
1050 # include variable details only in 'Verbose' mode
1047 # include variable details only in 'Verbose' mode
1051 self.include_vars = (self.mode == self.valid_modes[2])
1048 self.include_vars = (self.mode == self.valid_modes[2])
1052 # Set the join character for generating text tracebacks
1049 # Set the join character for generating text tracebacks
1053 self.tb_join_char = self._join_chars[self.mode]
1050 self.tb_join_char = self._join_chars[self.mode]
1054
1051
1055 # some convenient shortcuts
1052 # some convenient shortcuts
1056 def plain(self):
1053 def plain(self):
1057 self.set_mode(self.valid_modes[0])
1054 self.set_mode(self.valid_modes[0])
1058
1055
1059 def context(self):
1056 def context(self):
1060 self.set_mode(self.valid_modes[1])
1057 self.set_mode(self.valid_modes[1])
1061
1058
1062 def verbose(self):
1059 def verbose(self):
1063 self.set_mode(self.valid_modes[2])
1060 self.set_mode(self.valid_modes[2])
1064
1061
1065 def minimal(self):
1062 def minimal(self):
1066 self.set_mode(self.valid_modes[3])
1063 self.set_mode(self.valid_modes[3])
1067
1064
1068
1065
1069 #----------------------------------------------------------------------------
1066 #----------------------------------------------------------------------------
1070 class AutoFormattedTB(FormattedTB):
1067 class AutoFormattedTB(FormattedTB):
1071 """A traceback printer which can be called on the fly.
1068 """A traceback printer which can be called on the fly.
1072
1069
1073 It will find out about exceptions by itself.
1070 It will find out about exceptions by itself.
1074
1071
1075 A brief example::
1072 A brief example::
1076
1073
1077 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1074 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1078 try:
1075 try:
1079 ...
1076 ...
1080 except:
1077 except:
1081 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
1082 """
1079 """
1083
1080
1084 def __call__(self, etype=None, evalue=None, etb=None,
1081 def __call__(self, etype=None, evalue=None, etb=None,
1085 out=None, tb_offset=None):
1082 out=None, tb_offset=None):
1086 """Print out a formatted exception traceback.
1083 """Print out a formatted exception traceback.
1087
1084
1088 Optional arguments:
1085 Optional arguments:
1089 - out: an open file-like object to direct output to.
1086 - out: an open file-like object to direct output to.
1090
1087
1091 - 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
1092 per-call basis (this overrides temporarily the instance's tb_offset
1089 per-call basis (this overrides temporarily the instance's tb_offset
1093 given at initialization time."""
1090 given at initialization time."""
1094
1091
1095 if out is None:
1092 if out is None:
1096 out = self.ostream
1093 out = self.ostream
1097 out.flush()
1094 out.flush()
1098 out.write(self.text(etype, evalue, etb, tb_offset))
1095 out.write(self.text(etype, evalue, etb, tb_offset))
1099 out.write('\n')
1096 out.write('\n')
1100 out.flush()
1097 out.flush()
1101 # 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
1102 # that to the clients.
1099 # that to the clients.
1103 try:
1100 try:
1104 self.debugger()
1101 self.debugger()
1105 except KeyboardInterrupt:
1102 except KeyboardInterrupt:
1106 print("\nKeyboardInterrupt")
1103 print("\nKeyboardInterrupt")
1107
1104
1108 def structured_traceback(self, etype=None, value=None, tb=None,
1105 def structured_traceback(self, etype=None, value=None, tb=None,
1109 tb_offset=None, number_of_lines_of_context=5):
1106 tb_offset=None, number_of_lines_of_context=5):
1110
1107
1111 etype: type
1108 etype: type
1112 value: BaseException
1109 value: BaseException
1113 # tb: TracebackType or tupleof tb types ?
1110 # tb: TracebackType or tupleof tb types ?
1114 if etype is None:
1111 if etype is None:
1115 etype, value, tb = sys.exc_info()
1112 etype, value, tb = sys.exc_info()
1116 if isinstance(tb, tuple):
1113 if isinstance(tb, tuple):
1117 # tb is a tuple if this is a chained exception.
1114 # tb is a tuple if this is a chained exception.
1118 self.tb = tb[0]
1115 self.tb = tb[0]
1119 else:
1116 else:
1120 self.tb = tb
1117 self.tb = tb
1121 return FormattedTB.structured_traceback(
1118 return FormattedTB.structured_traceback(
1122 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1119 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1123
1120
1124
1121
1125 #---------------------------------------------------------------------------
1122 #---------------------------------------------------------------------------
1126
1123
1127 # A simple class to preserve Nathan's original functionality.
1124 # A simple class to preserve Nathan's original functionality.
1128 class ColorTB(FormattedTB):
1125 class ColorTB(FormattedTB):
1129 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1126 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1130
1127
1131 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1128 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1132 FormattedTB.__init__(self, color_scheme=color_scheme,
1129 FormattedTB.__init__(self, color_scheme=color_scheme,
1133 call_pdb=call_pdb, **kwargs)
1130 call_pdb=call_pdb, **kwargs)
1134
1131
1135
1132
1136 class SyntaxTB(ListTB):
1133 class SyntaxTB(ListTB):
1137 """Extension which holds some state: the last exception value"""
1134 """Extension which holds some state: the last exception value"""
1138
1135
1139 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1136 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1140 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1137 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1141 self.last_syntax_error = None
1138 self.last_syntax_error = None
1142
1139
1143 def __call__(self, etype, value, elist):
1140 def __call__(self, etype, value, elist):
1144 self.last_syntax_error = value
1141 self.last_syntax_error = value
1145
1142
1146 ListTB.__call__(self, etype, value, elist)
1143 ListTB.__call__(self, etype, value, elist)
1147
1144
1148 def structured_traceback(self, etype, value, elist, tb_offset=None,
1145 def structured_traceback(self, etype, value, elist, tb_offset=None,
1149 context=5):
1146 context=5):
1150 # 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
1151 # be wrong (retrieved from an outdated cache). This replaces it with
1148 # be wrong (retrieved from an outdated cache). This replaces it with
1152 # the current value.
1149 # the current value.
1153 if isinstance(value, SyntaxError) \
1150 if isinstance(value, SyntaxError) \
1154 and isinstance(value.filename, str) \
1151 and isinstance(value.filename, str) \
1155 and isinstance(value.lineno, int):
1152 and isinstance(value.lineno, int):
1156 linecache.checkcache(value.filename)
1153 linecache.checkcache(value.filename)
1157 newtext = linecache.getline(value.filename, value.lineno)
1154 newtext = linecache.getline(value.filename, value.lineno)
1158 if newtext:
1155 if newtext:
1159 value.text = newtext
1156 value.text = newtext
1160 self.last_syntax_error = value
1157 self.last_syntax_error = value
1161 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1158 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1162 tb_offset=tb_offset, context=context)
1159 tb_offset=tb_offset, context=context)
1163
1160
1164 def clear_err_state(self):
1161 def clear_err_state(self):
1165 """Return the current error state and clear it"""
1162 """Return the current error state and clear it"""
1166 e = self.last_syntax_error
1163 e = self.last_syntax_error
1167 self.last_syntax_error = None
1164 self.last_syntax_error = None
1168 return e
1165 return e
1169
1166
1170 def stb2text(self, stb):
1167 def stb2text(self, stb):
1171 """Convert a structured traceback (a list) to a string."""
1168 """Convert a structured traceback (a list) to a string."""
1172 return ''.join(stb)
1169 return ''.join(stb)
1173
1170
1174
1171
1175 # some internal-use functions
1172 # some internal-use functions
1176 def text_repr(value):
1173 def text_repr(value):
1177 """Hopefully pretty robust repr equivalent."""
1174 """Hopefully pretty robust repr equivalent."""
1178 # this is pretty horrible but should always return *something*
1175 # this is pretty horrible but should always return *something*
1179 try:
1176 try:
1180 return pydoc.text.repr(value)
1177 return pydoc.text.repr(value)
1181 except KeyboardInterrupt:
1178 except KeyboardInterrupt:
1182 raise
1179 raise
1183 except:
1180 except:
1184 try:
1181 try:
1185 return repr(value)
1182 return repr(value)
1186 except KeyboardInterrupt:
1183 except KeyboardInterrupt:
1187 raise
1184 raise
1188 except:
1185 except:
1189 try:
1186 try:
1190 # all still in an except block so we catch
1187 # all still in an except block so we catch
1191 # getattr raising
1188 # getattr raising
1192 name = getattr(value, '__name__', None)
1189 name = getattr(value, '__name__', None)
1193 if name:
1190 if name:
1194 # ick, recursion
1191 # ick, recursion
1195 return text_repr(name)
1192 return text_repr(name)
1196 klass = getattr(value, '__class__', None)
1193 klass = getattr(value, '__class__', None)
1197 if klass:
1194 if klass:
1198 return '%s instance' % text_repr(klass)
1195 return '%s instance' % text_repr(klass)
1199 except KeyboardInterrupt:
1196 except KeyboardInterrupt:
1200 raise
1197 raise
1201 except:
1198 except:
1202 return 'UNRECOVERABLE REPR FAILURE'
1199 return 'UNRECOVERABLE REPR FAILURE'
1203
1200
1204
1201
1205 def eqrepr(value, repr=text_repr):
1202 def eqrepr(value, repr=text_repr):
1206 return '=%s' % repr(value)
1203 return '=%s' % repr(value)
1207
1204
1208
1205
1209 def nullrepr(value, repr=text_repr):
1206 def nullrepr(value, repr=text_repr):
1210 return ''
1207 return ''
General Comments 0
You need to be logged in to leave comments. Login now