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