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