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