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