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