##// END OF EJS Templates
Fixed long-standing and very subtle bug in ultratb....
Fernando Perez -
Show More
@@ -1,1224 +1,1230 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 ultratb.py -- Spice up your tracebacks!
3 ultratb.py -- Spice up your tracebacks!
4
4
5 * ColorTB
5 * ColorTB
6 I've always found it a bit hard to visually parse tracebacks in Python. The
6 I've always found it a bit hard to visually parse tracebacks in Python. The
7 ColorTB class is a solution to that problem. It colors the different parts of a
7 ColorTB class is a solution to that problem. It colors the different parts of a
8 traceback in a manner similar to what you would expect from a syntax-highlighting
8 traceback in a manner similar to what you would expect from a syntax-highlighting
9 text editor.
9 text editor.
10
10
11 Installation instructions for ColorTB:
11 Installation instructions for ColorTB:
12 import sys,ultratb
12 import sys,ultratb
13 sys.excepthook = ultratb.ColorTB()
13 sys.excepthook = ultratb.ColorTB()
14
14
15 * VerboseTB
15 * VerboseTB
16 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
16 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
17 of useful info when a traceback occurs. Ping originally had it spit out HTML
17 of useful info when a traceback occurs. Ping originally had it spit out HTML
18 and intended it for CGI programmers, but why should they have all the fun? I
18 and intended it for CGI programmers, but why should they have all the fun? I
19 altered it to spit out colored text to the terminal. It's a bit overwhelming,
19 altered it to spit out colored text to the terminal. It's a bit overwhelming,
20 but kind of neat, and maybe useful for long-running programs that you believe
20 but kind of neat, and maybe useful for long-running programs that you believe
21 are bug-free. If a crash *does* occur in that type of program you want details.
21 are bug-free. If a crash *does* occur in that type of program you want details.
22 Give it a shot--you'll love it or you'll hate it.
22 Give it a shot--you'll love it or you'll hate it.
23
23
24 Note:
24 Note:
25
25
26 The Verbose mode prints the variables currently visible where the exception
26 The Verbose mode prints the variables currently visible where the exception
27 happened (shortening their strings if too long). This can potentially be
27 happened (shortening their strings if too long). This can potentially be
28 very slow, if you happen to have a huge data structure whose string
28 very slow, if you happen to have a huge data structure whose string
29 representation is complex to compute. Your computer may appear to freeze for
29 representation is complex to compute. Your computer may appear to freeze for
30 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
30 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
31 with Ctrl-C (maybe hitting it more than once).
31 with Ctrl-C (maybe hitting it more than once).
32
32
33 If you encounter this kind of situation often, you may want to use the
33 If you encounter this kind of situation often, you may want to use the
34 Verbose_novars mode instead of the regular Verbose, which avoids formatting
34 Verbose_novars mode instead of the regular Verbose, which avoids formatting
35 variables (but otherwise includes the information and context given by
35 variables (but otherwise includes the information and context given by
36 Verbose).
36 Verbose).
37
37
38
38
39 Installation instructions for ColorTB:
39 Installation instructions for ColorTB:
40 import sys,ultratb
40 import sys,ultratb
41 sys.excepthook = ultratb.VerboseTB()
41 sys.excepthook = ultratb.VerboseTB()
42
42
43 Note: Much of the code in this module was lifted verbatim from the standard
43 Note: Much of the code in this module was lifted verbatim from the standard
44 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
44 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
45
45
46 * Color schemes
46 * Color schemes
47 The colors are defined in the class TBTools through the use of the
47 The colors are defined in the class TBTools through the use of the
48 ColorSchemeTable class. Currently the following exist:
48 ColorSchemeTable class. Currently the following exist:
49
49
50 - NoColor: allows all of this module to be used in any terminal (the color
50 - NoColor: allows all of this module to be used in any terminal (the color
51 escapes are just dummy blank strings).
51 escapes are just dummy blank strings).
52
52
53 - Linux: is meant to look good in a terminal like the Linux console (black
53 - Linux: is meant to look good in a terminal like the Linux console (black
54 or very dark background).
54 or very dark background).
55
55
56 - LightBG: similar to Linux but swaps dark/light colors to be more readable
56 - LightBG: similar to Linux but swaps dark/light colors to be more readable
57 in light background terminals.
57 in light background terminals.
58
58
59 You can implement other color schemes easily, the syntax is fairly
59 You can implement other color schemes easily, the syntax is fairly
60 self-explanatory. Please send back new schemes you develop to the author for
60 self-explanatory. Please send back new schemes you develop to the author for
61 possible inclusion in future releases.
61 possible inclusion in future releases.
62 """
62 """
63
63
64 #*****************************************************************************
64 #*****************************************************************************
65 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
65 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
66 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
66 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
67 #
67 #
68 # Distributed under the terms of the BSD License. The full license is in
68 # Distributed under the terms of the BSD License. The full license is in
69 # the file COPYING, distributed as part of this software.
69 # the file COPYING, distributed as part of this software.
70 #*****************************************************************************
70 #*****************************************************************************
71
71
72 from __future__ import with_statement
72 from __future__ import with_statement
73
73
74 import inspect
74 import inspect
75 import keyword
75 import keyword
76 import linecache
76 import linecache
77 import os
77 import os
78 import pydoc
78 import pydoc
79 import re
79 import re
80 import string
80 import string
81 import sys
81 import sys
82 import time
82 import time
83 import tokenize
83 import tokenize
84 import traceback
84 import traceback
85 import types
85 import types
86
86
87 # For purposes of monkeypatching inspect to fix a bug in it.
87 # For purposes of monkeypatching inspect to fix a bug in it.
88 from inspect import getsourcefile, getfile, getmodule,\
88 from inspect import getsourcefile, getfile, getmodule,\
89 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
89 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
90
90
91 # IPython's own modules
91 # IPython's own modules
92 # Modified pdb which doesn't damage IPython's readline handling
92 # Modified pdb which doesn't damage IPython's readline handling
93 from IPython.core import debugger, ipapi
93 from IPython.core import debugger, ipapi
94 from IPython.core.display_trap import DisplayTrap
94 from IPython.core.display_trap import DisplayTrap
95 from IPython.core.excolors import exception_colors
95 from IPython.core.excolors import exception_colors
96 from IPython.utils import PyColorize
96 from IPython.utils import PyColorize
97 from IPython.utils import io
97 from IPython.utils import io
98 from IPython.utils.data import uniq_stable
98 from IPython.utils.data import uniq_stable
99 from IPython.utils.warn import info, error
99 from IPython.utils.warn import info, error
100
100
101 # Globals
101 # Globals
102 # amount of space to put line numbers before verbose tracebacks
102 # amount of space to put line numbers before verbose tracebacks
103 INDENT_SIZE = 8
103 INDENT_SIZE = 8
104
104
105 # Default color scheme. This is used, for example, by the traceback
105 # Default color scheme. This is used, for example, by the traceback
106 # formatter. When running in an actual IPython instance, the user's rc.colors
106 # formatter. When running in an actual IPython instance, the user's rc.colors
107 # value is used, but havinga module global makes this functionality available
107 # value is used, but havinga module global makes this functionality available
108 # to users of ultratb who are NOT running inside ipython.
108 # to users of ultratb who are NOT running inside ipython.
109 DEFAULT_SCHEME = 'NoColor'
109 DEFAULT_SCHEME = 'NoColor'
110
110
111 #---------------------------------------------------------------------------
111 #---------------------------------------------------------------------------
112 # Code begins
112 # Code begins
113
113
114 # Utility functions
114 # Utility functions
115 def inspect_error():
115 def inspect_error():
116 """Print a message about internal inspect errors.
116 """Print a message about internal inspect errors.
117
117
118 These are unfortunately quite common."""
118 These are unfortunately quite common."""
119
119
120 error('Internal Python error in the inspect module.\n'
120 error('Internal Python error in the inspect module.\n'
121 'Below is the traceback from this internal error.\n')
121 'Below is the traceback from this internal error.\n')
122
122
123
123
124 def findsource(object):
124 def findsource(object):
125 """Return the entire source file and starting line number for an object.
125 """Return the entire source file and starting line number for an object.
126
126
127 The argument may be a module, class, method, function, traceback, frame,
127 The argument may be a module, class, method, function, traceback, frame,
128 or code object. The source code is returned as a list of all the lines
128 or code object. The source code is returned as a list of all the lines
129 in the file and the line number indexes a line in that list. An IOError
129 in the file and the line number indexes a line in that list. An IOError
130 is raised if the source code cannot be retrieved.
130 is raised if the source code cannot be retrieved.
131
131
132 FIXED version with which we monkeypatch the stdlib to work around a bug."""
132 FIXED version with which we monkeypatch the stdlib to work around a bug."""
133
133
134 file = getsourcefile(object) or getfile(object)
134 file = getsourcefile(object) or getfile(object)
135 # If the object is a frame, then trying to get the globals dict from its
135 # If the object is a frame, then trying to get the globals dict from its
136 # module won't work. Instead, the frame object itself has the globals
136 # module won't work. Instead, the frame object itself has the globals
137 # dictionary.
137 # dictionary.
138 globals_dict = None
138 globals_dict = None
139 if inspect.isframe(object):
139 if inspect.isframe(object):
140 # XXX: can this ever be false?
140 # XXX: can this ever be false?
141 globals_dict = object.f_globals
141 globals_dict = object.f_globals
142 else:
142 else:
143 module = getmodule(object, file)
143 module = getmodule(object, file)
144 if module:
144 if module:
145 globals_dict = module.__dict__
145 globals_dict = module.__dict__
146 lines = linecache.getlines(file, globals_dict)
146 lines = linecache.getlines(file, globals_dict)
147 if not lines:
147 if not lines:
148 raise IOError('could not get source code')
148 raise IOError('could not get source code')
149
149
150 if ismodule(object):
150 if ismodule(object):
151 return lines, 0
151 return lines, 0
152
152
153 if isclass(object):
153 if isclass(object):
154 name = object.__name__
154 name = object.__name__
155 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
155 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
156 # make some effort to find the best matching class definition:
156 # make some effort to find the best matching class definition:
157 # use the one with the least indentation, which is the one
157 # use the one with the least indentation, which is the one
158 # that's most probably not inside a function definition.
158 # that's most probably not inside a function definition.
159 candidates = []
159 candidates = []
160 for i in range(len(lines)):
160 for i in range(len(lines)):
161 match = pat.match(lines[i])
161 match = pat.match(lines[i])
162 if match:
162 if match:
163 # if it's at toplevel, it's already the best one
163 # if it's at toplevel, it's already the best one
164 if lines[i][0] == 'c':
164 if lines[i][0] == 'c':
165 return lines, i
165 return lines, i
166 # else add whitespace to candidate list
166 # else add whitespace to candidate list
167 candidates.append((match.group(1), i))
167 candidates.append((match.group(1), i))
168 if candidates:
168 if candidates:
169 # this will sort by whitespace, and by line number,
169 # this will sort by whitespace, and by line number,
170 # less whitespace first
170 # less whitespace first
171 candidates.sort()
171 candidates.sort()
172 return lines, candidates[0][1]
172 return lines, candidates[0][1]
173 else:
173 else:
174 raise IOError('could not find class definition')
174 raise IOError('could not find class definition')
175
175
176 if ismethod(object):
176 if ismethod(object):
177 object = object.im_func
177 object = object.im_func
178 if isfunction(object):
178 if isfunction(object):
179 object = object.func_code
179 object = object.func_code
180 if istraceback(object):
180 if istraceback(object):
181 object = object.tb_frame
181 object = object.tb_frame
182 if isframe(object):
182 if isframe(object):
183 object = object.f_code
183 object = object.f_code
184 if iscode(object):
184 if iscode(object):
185 if not hasattr(object, 'co_firstlineno'):
185 if not hasattr(object, 'co_firstlineno'):
186 raise IOError('could not find function definition')
186 raise IOError('could not find function definition')
187 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
187 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
188 pmatch = pat.match
188 pmatch = pat.match
189 # fperez - fix: sometimes, co_firstlineno can give a number larger than
189 # fperez - fix: sometimes, co_firstlineno can give a number larger than
190 # the length of lines, which causes an error. Safeguard against that.
190 # the length of lines, which causes an error. Safeguard against that.
191 lnum = min(object.co_firstlineno,len(lines))-1
191 lnum = min(object.co_firstlineno,len(lines))-1
192 while lnum > 0:
192 while lnum > 0:
193 if pmatch(lines[lnum]): break
193 if pmatch(lines[lnum]): break
194 lnum -= 1
194 lnum -= 1
195
195
196 return lines, lnum
196 return lines, lnum
197 raise IOError('could not find code object')
197 raise IOError('could not find code object')
198
198
199 # Monkeypatch inspect to apply our bugfix. This code only works with py25
199 # Monkeypatch inspect to apply our bugfix. This code only works with py25
200 if sys.version_info[:2] >= (2,5):
200 if sys.version_info[:2] >= (2,5):
201 inspect.findsource = findsource
201 inspect.findsource = findsource
202
202
203 def fix_frame_records_filenames(records):
203 def fix_frame_records_filenames(records):
204 """Try to fix the filenames in each record from inspect.getinnerframes().
204 """Try to fix the filenames in each record from inspect.getinnerframes().
205
205
206 Particularly, modules loaded from within zip files have useless filenames
206 Particularly, modules loaded from within zip files have useless filenames
207 attached to their code object, and inspect.getinnerframes() just uses it.
207 attached to their code object, and inspect.getinnerframes() just uses it.
208 """
208 """
209 fixed_records = []
209 fixed_records = []
210 for frame, filename, line_no, func_name, lines, index in records:
210 for frame, filename, line_no, func_name, lines, index in records:
211 # Look inside the frame's globals dictionary for __file__, which should
211 # Look inside the frame's globals dictionary for __file__, which should
212 # be better.
212 # be better.
213 better_fn = frame.f_globals.get('__file__', None)
213 better_fn = frame.f_globals.get('__file__', None)
214 if isinstance(better_fn, str):
214 if isinstance(better_fn, str):
215 # Check the type just in case someone did something weird with
215 # Check the type just in case someone did something weird with
216 # __file__. It might also be None if the error occurred during
216 # __file__. It might also be None if the error occurred during
217 # import.
217 # import.
218 filename = better_fn
218 filename = better_fn
219 fixed_records.append((frame, filename, line_no, func_name, lines, index))
219 fixed_records.append((frame, filename, line_no, func_name, lines, index))
220 return fixed_records
220 return fixed_records
221
221
222
222
223 def _fixed_getinnerframes(etb, context=1,tb_offset=0):
223 def _fixed_getinnerframes(etb, context=1,tb_offset=0):
224 import linecache
224 import linecache
225 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
225 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
226
226
227 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
227 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
228
228
229 # If the error is at the console, don't build any context, since it would
229 # If the error is at the console, don't build any context, since it would
230 # otherwise produce 5 blank lines printed out (there is no file at the
230 # otherwise produce 5 blank lines printed out (there is no file at the
231 # console)
231 # console)
232 rec_check = records[tb_offset:]
232 rec_check = records[tb_offset:]
233 try:
233 try:
234 rname = rec_check[0][1]
234 rname = rec_check[0][1]
235 if rname == '<ipython console>' or rname.endswith('<string>'):
235 if rname == '<ipython console>' or rname.endswith('<string>'):
236 return rec_check
236 return rec_check
237 except IndexError:
237 except IndexError:
238 pass
238 pass
239
239
240 aux = traceback.extract_tb(etb)
240 aux = traceback.extract_tb(etb)
241 assert len(records) == len(aux)
241 assert len(records) == len(aux)
242 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
242 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
243 maybeStart = lnum-1 - context//2
243 maybeStart = lnum-1 - context//2
244 start = max(maybeStart, 0)
244 start = max(maybeStart, 0)
245 end = start + context
245 end = start + context
246 lines = linecache.getlines(file)[start:end]
246 lines = linecache.getlines(file)[start:end]
247 # pad with empty lines if necessary
247 # pad with empty lines if necessary
248 if maybeStart < 0:
248 if maybeStart < 0:
249 lines = (['\n'] * -maybeStart) + lines
249 lines = (['\n'] * -maybeStart) + lines
250 if len(lines) < context:
250 if len(lines) < context:
251 lines += ['\n'] * (context - len(lines))
251 lines += ['\n'] * (context - len(lines))
252 buf = list(records[i])
252 buf = list(records[i])
253 buf[LNUM_POS] = lnum
253 buf[LNUM_POS] = lnum
254 buf[INDEX_POS] = lnum - 1 - start
254 buf[INDEX_POS] = lnum - 1 - start
255 buf[LINES_POS] = lines
255 buf[LINES_POS] = lines
256 records[i] = tuple(buf)
256 records[i] = tuple(buf)
257 return records[tb_offset:]
257 return records[tb_offset:]
258
258
259 # Helper function -- largely belongs to VerboseTB, but we need the same
259 # Helper function -- largely belongs to VerboseTB, but we need the same
260 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
260 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
261 # can be recognized properly by ipython.el's py-traceback-line-re
261 # can be recognized properly by ipython.el's py-traceback-line-re
262 # (SyntaxErrors have to be treated specially because they have no traceback)
262 # (SyntaxErrors have to be treated specially because they have no traceback)
263
263
264 _parser = PyColorize.Parser()
264 _parser = PyColorize.Parser()
265
265
266 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):
266 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):
267 numbers_width = INDENT_SIZE - 1
267 numbers_width = INDENT_SIZE - 1
268 res = []
268 res = []
269 i = lnum - index
269 i = lnum - index
270
270
271 # This lets us get fully syntax-highlighted tracebacks.
271 # This lets us get fully syntax-highlighted tracebacks.
272 if scheme is None:
272 if scheme is None:
273 ipinst = ipapi.get()
273 ipinst = ipapi.get()
274 if ipinst is not None:
274 if ipinst is not None:
275 scheme = ipinst.colors
275 scheme = ipinst.colors
276 else:
276 else:
277 scheme = DEFAULT_SCHEME
277 scheme = DEFAULT_SCHEME
278
278
279 _line_format = _parser.format2
279 _line_format = _parser.format2
280
280
281 for line in lines:
281 for line in lines:
282 new_line, err = _line_format(line,'str',scheme)
282 new_line, err = _line_format(line,'str',scheme)
283 if not err: line = new_line
283 if not err: line = new_line
284
284
285 if i == lnum:
285 if i == lnum:
286 # This is the line with the error
286 # This is the line with the error
287 pad = numbers_width - len(str(i))
287 pad = numbers_width - len(str(i))
288 if pad >= 3:
288 if pad >= 3:
289 marker = '-'*(pad-3) + '-> '
289 marker = '-'*(pad-3) + '-> '
290 elif pad == 2:
290 elif pad == 2:
291 marker = '> '
291 marker = '> '
292 elif pad == 1:
292 elif pad == 1:
293 marker = '>'
293 marker = '>'
294 else:
294 else:
295 marker = ''
295 marker = ''
296 num = marker + str(i)
296 num = marker + str(i)
297 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
297 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
298 Colors.line, line, Colors.Normal)
298 Colors.line, line, Colors.Normal)
299 else:
299 else:
300 num = '%*s' % (numbers_width,i)
300 num = '%*s' % (numbers_width,i)
301 line = '%s%s%s %s' %(Colors.lineno, num,
301 line = '%s%s%s %s' %(Colors.lineno, num,
302 Colors.Normal, line)
302 Colors.Normal, line)
303
303
304 res.append(line)
304 res.append(line)
305 if lvals and i == lnum:
305 if lvals and i == lnum:
306 res.append(lvals + '\n')
306 res.append(lvals + '\n')
307 i = i + 1
307 i = i + 1
308 return res
308 return res
309
309
310
310
311 #---------------------------------------------------------------------------
311 #---------------------------------------------------------------------------
312 # Module classes
312 # Module classes
313 class TBTools(object):
313 class TBTools(object):
314 """Basic tools used by all traceback printer classes."""
314 """Basic tools used by all traceback printer classes."""
315
315
316 # Number of frames to skip when reporting tracebacks
316 # Number of frames to skip when reporting tracebacks
317 tb_offset = 0
317 tb_offset = 0
318
318
319 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
319 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
320 # Whether to call the interactive pdb debugger after printing
320 # Whether to call the interactive pdb debugger after printing
321 # tracebacks or not
321 # tracebacks or not
322 self.call_pdb = call_pdb
322 self.call_pdb = call_pdb
323
323
324 # Output stream to write to. Note that we store the original value in
324 # Output stream to write to. Note that we store the original value in
325 # a private attribute and then make the public ostream a property, so
325 # a private attribute and then make the public ostream a property, so
326 # that we can delay accessing io.Term.cout until runtime. The way
326 # that we can delay accessing io.Term.cout until runtime. The way
327 # things are written now, the Term.cout object is dynamically managed
327 # things are written now, the Term.cout object is dynamically managed
328 # so a reference to it should NEVER be stored statically. This
328 # so a reference to it should NEVER be stored statically. This
329 # property approach confines this detail to a single location, and all
329 # property approach confines this detail to a single location, and all
330 # subclasses can simply access self.ostream for writing.
330 # subclasses can simply access self.ostream for writing.
331 self._ostream = ostream
331 self._ostream = ostream
332
332
333 # Create color table
333 # Create color table
334 self.color_scheme_table = exception_colors()
334 self.color_scheme_table = exception_colors()
335
335
336 self.set_colors(color_scheme)
336 self.set_colors(color_scheme)
337 self.old_scheme = color_scheme # save initial value for toggles
337 self.old_scheme = color_scheme # save initial value for toggles
338
338
339 if call_pdb:
339 if call_pdb:
340 self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name)
340 self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name)
341 else:
341 else:
342 self.pdb = None
342 self.pdb = None
343
343
344 def _get_ostream(self):
344 def _get_ostream(self):
345 """Output stream that exceptions are written to.
345 """Output stream that exceptions are written to.
346
346
347 Valid values are:
347 Valid values are:
348
348
349 - None: the default, which means that IPython will dynamically resolve
349 - None: the default, which means that IPython will dynamically resolve
350 to io.Term.cout. This ensures compatibility with most tools, including
350 to io.Term.cout. This ensures compatibility with most tools, including
351 Windows (where plain stdout doesn't recognize ANSI escapes).
351 Windows (where plain stdout doesn't recognize ANSI escapes).
352
352
353 - Any object with 'write' and 'flush' attributes.
353 - Any object with 'write' and 'flush' attributes.
354 """
354 """
355 return io.Term.cout if self._ostream is None else self._ostream
355 return io.Term.cout if self._ostream is None else self._ostream
356
356
357 def _set_ostream(self, val):
357 def _set_ostream(self, val):
358 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
358 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
359 self._ostream = val
359 self._ostream = val
360
360
361 ostream = property(_get_ostream, _set_ostream)
361 ostream = property(_get_ostream, _set_ostream)
362
362
363 def set_colors(self,*args,**kw):
363 def set_colors(self,*args,**kw):
364 """Shorthand access to the color table scheme selector method."""
364 """Shorthand access to the color table scheme selector method."""
365
365
366 # Set own color table
366 # Set own color table
367 self.color_scheme_table.set_active_scheme(*args,**kw)
367 self.color_scheme_table.set_active_scheme(*args,**kw)
368 # for convenience, set Colors to the active scheme
368 # for convenience, set Colors to the active scheme
369 self.Colors = self.color_scheme_table.active_colors
369 self.Colors = self.color_scheme_table.active_colors
370 # Also set colors of debugger
370 # Also set colors of debugger
371 if hasattr(self,'pdb') and self.pdb is not None:
371 if hasattr(self,'pdb') and self.pdb is not None:
372 self.pdb.set_colors(*args,**kw)
372 self.pdb.set_colors(*args,**kw)
373
373
374 def color_toggle(self):
374 def color_toggle(self):
375 """Toggle between the currently active color scheme and NoColor."""
375 """Toggle between the currently active color scheme and NoColor."""
376
376
377 if self.color_scheme_table.active_scheme_name == 'NoColor':
377 if self.color_scheme_table.active_scheme_name == 'NoColor':
378 self.color_scheme_table.set_active_scheme(self.old_scheme)
378 self.color_scheme_table.set_active_scheme(self.old_scheme)
379 self.Colors = self.color_scheme_table.active_colors
379 self.Colors = self.color_scheme_table.active_colors
380 else:
380 else:
381 self.old_scheme = self.color_scheme_table.active_scheme_name
381 self.old_scheme = self.color_scheme_table.active_scheme_name
382 self.color_scheme_table.set_active_scheme('NoColor')
382 self.color_scheme_table.set_active_scheme('NoColor')
383 self.Colors = self.color_scheme_table.active_colors
383 self.Colors = self.color_scheme_table.active_colors
384
384
385 def stb2text(self, stb):
385 def stb2text(self, stb):
386 """Convert a structured traceback (a list) to a string."""
386 """Convert a structured traceback (a list) to a string."""
387 return '\n'.join(stb)
387 return '\n'.join(stb)
388
388
389 def text(self, etype, value, tb, tb_offset=None, context=5):
389 def text(self, etype, value, tb, tb_offset=None, context=5):
390 """Return formatted traceback.
390 """Return formatted traceback.
391
391
392 Subclasses may override this if they add extra arguments.
392 Subclasses may override this if they add extra arguments.
393 """
393 """
394 tb_list = self.structured_traceback(etype, value, tb,
394 tb_list = self.structured_traceback(etype, value, tb,
395 tb_offset, context)
395 tb_offset, context)
396 return self.stb2text(tb_list)
396 return self.stb2text(tb_list)
397
397
398 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
398 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
399 context=5, mode=None):
399 context=5, mode=None):
400 """Return a list of traceback frames.
400 """Return a list of traceback frames.
401
401
402 Must be implemented by each class.
402 Must be implemented by each class.
403 """
403 """
404 raise NotImplementedError()
404 raise NotImplementedError()
405
405
406
406
407 #---------------------------------------------------------------------------
407 #---------------------------------------------------------------------------
408 class ListTB(TBTools):
408 class ListTB(TBTools):
409 """Print traceback information from a traceback list, with optional color.
409 """Print traceback information from a traceback list, with optional color.
410
410
411 Calling: requires 3 arguments:
411 Calling: requires 3 arguments:
412 (etype, evalue, elist)
412 (etype, evalue, elist)
413 as would be obtained by:
413 as would be obtained by:
414 etype, evalue, tb = sys.exc_info()
414 etype, evalue, tb = sys.exc_info()
415 if tb:
415 if tb:
416 elist = traceback.extract_tb(tb)
416 elist = traceback.extract_tb(tb)
417 else:
417 else:
418 elist = None
418 elist = None
419
419
420 It can thus be used by programs which need to process the traceback before
420 It can thus be used by programs which need to process the traceback before
421 printing (such as console replacements based on the code module from the
421 printing (such as console replacements based on the code module from the
422 standard library).
422 standard library).
423
423
424 Because they are meant to be called without a full traceback (only a
424 Because they are meant to be called without a full traceback (only a
425 list), instances of this class can't call the interactive pdb debugger."""
425 list), instances of this class can't call the interactive pdb debugger."""
426
426
427 def __init__(self,color_scheme = 'NoColor', call_pdb=False, ostream=None):
427 def __init__(self,color_scheme = 'NoColor', call_pdb=False, ostream=None):
428 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
428 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
429 ostream=ostream)
429 ostream=ostream)
430
430
431 def __call__(self, etype, value, elist):
431 def __call__(self, etype, value, elist):
432 self.ostream.flush()
432 self.ostream.flush()
433 self.ostream.write(self.text(etype, value, elist))
433 self.ostream.write(self.text(etype, value, elist))
434 self.ostream.write('\n')
434 self.ostream.write('\n')
435
435
436 def structured_traceback(self, etype, value, elist, tb_offset=None,
436 def structured_traceback(self, etype, value, elist, tb_offset=None,
437 context=5):
437 context=5):
438 """Return a color formatted string with the traceback info.
438 """Return a color formatted string with the traceback info.
439
439
440 Parameters
440 Parameters
441 ----------
441 ----------
442 etype : exception type
442 etype : exception type
443 Type of the exception raised.
443 Type of the exception raised.
444
444
445 value : object
445 value : object
446 Data stored in the exception
446 Data stored in the exception
447
447
448 elist : list
448 elist : list
449 List of frames, see class docstring for details.
449 List of frames, see class docstring for details.
450
450
451 tb_offset : int, optional
451 tb_offset : int, optional
452 Number of frames in the traceback to skip. If not given, the
452 Number of frames in the traceback to skip. If not given, the
453 instance value is used (set in constructor).
453 instance value is used (set in constructor).
454
454
455 context : int, optional
455 context : int, optional
456 Number of lines of context information to print.
456 Number of lines of context information to print.
457
457
458 Returns
458 Returns
459 -------
459 -------
460 String with formatted exception.
460 String with formatted exception.
461 """
461 """
462 tb_offset = self.tb_offset if tb_offset is None else tb_offset
462 tb_offset = self.tb_offset if tb_offset is None else tb_offset
463 Colors = self.Colors
463 Colors = self.Colors
464 out_list = []
464 out_list = []
465 if elist:
465 if elist:
466
466
467 if tb_offset and len(elist) > tb_offset:
467 if tb_offset and len(elist) > tb_offset:
468 elist = elist[tb_offset:]
468 elist = elist[tb_offset:]
469
469
470 out_list.append('Traceback %s(most recent call last)%s:' %
470 out_list.append('Traceback %s(most recent call last)%s:' %
471 (Colors.normalEm, Colors.Normal) + '\n')
471 (Colors.normalEm, Colors.Normal) + '\n')
472 out_list.extend(self._format_list(elist))
472 out_list.extend(self._format_list(elist))
473 # The exception info should be a single entry in the list.
473 # The exception info should be a single entry in the list.
474 lines = ''.join(self._format_exception_only(etype, value))
474 lines = ''.join(self._format_exception_only(etype, value))
475 out_list.append(lines)
475 out_list.append(lines)
476
476
477 # Note: this code originally read:
477 # Note: this code originally read:
478
478
479 ## for line in lines[:-1]:
479 ## for line in lines[:-1]:
480 ## out_list.append(" "+line)
480 ## out_list.append(" "+line)
481 ## out_list.append(lines[-1])
481 ## out_list.append(lines[-1])
482
482
483 # This means it was indenting everything but the last line by a little
483 # This means it was indenting everything but the last line by a little
484 # bit. I've disabled this for now, but if we see ugliness somewhre we
484 # bit. I've disabled this for now, but if we see ugliness somewhre we
485 # can restore it.
485 # can restore it.
486
486
487 return out_list
487 return out_list
488
488
489 def _format_list(self, extracted_list):
489 def _format_list(self, extracted_list):
490 """Format a list of traceback entry tuples for printing.
490 """Format a list of traceback entry tuples for printing.
491
491
492 Given a list of tuples as returned by extract_tb() or
492 Given a list of tuples as returned by extract_tb() or
493 extract_stack(), return a list of strings ready for printing.
493 extract_stack(), return a list of strings ready for printing.
494 Each string in the resulting list corresponds to the item with the
494 Each string in the resulting list corresponds to the item with the
495 same index in the argument list. Each string ends in a newline;
495 same index in the argument list. Each string ends in a newline;
496 the strings may contain internal newlines as well, for those items
496 the strings may contain internal newlines as well, for those items
497 whose source text line is not None.
497 whose source text line is not None.
498
498
499 Lifted almost verbatim from traceback.py
499 Lifted almost verbatim from traceback.py
500 """
500 """
501
501
502 Colors = self.Colors
502 Colors = self.Colors
503 list = []
503 list = []
504 for filename, lineno, name, line in extracted_list[:-1]:
504 for filename, lineno, name, line in extracted_list[:-1]:
505 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
505 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
506 (Colors.filename, filename, Colors.Normal,
506 (Colors.filename, filename, Colors.Normal,
507 Colors.lineno, lineno, Colors.Normal,
507 Colors.lineno, lineno, Colors.Normal,
508 Colors.name, name, Colors.Normal)
508 Colors.name, name, Colors.Normal)
509 if line:
509 if line:
510 item = item + ' %s\n' % line.strip()
510 item = item + ' %s\n' % line.strip()
511 list.append(item)
511 list.append(item)
512 # Emphasize the last entry
512 # Emphasize the last entry
513 filename, lineno, name, line = extracted_list[-1]
513 filename, lineno, name, line = extracted_list[-1]
514 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
514 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
515 (Colors.normalEm,
515 (Colors.normalEm,
516 Colors.filenameEm, filename, Colors.normalEm,
516 Colors.filenameEm, filename, Colors.normalEm,
517 Colors.linenoEm, lineno, Colors.normalEm,
517 Colors.linenoEm, lineno, Colors.normalEm,
518 Colors.nameEm, name, Colors.normalEm,
518 Colors.nameEm, name, Colors.normalEm,
519 Colors.Normal)
519 Colors.Normal)
520 if line:
520 if line:
521 item = item + '%s %s%s\n' % (Colors.line, line.strip(),
521 item = item + '%s %s%s\n' % (Colors.line, line.strip(),
522 Colors.Normal)
522 Colors.Normal)
523 list.append(item)
523 list.append(item)
524 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
524 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
525 return list
525 return list
526
526
527 def _format_exception_only(self, etype, value):
527 def _format_exception_only(self, etype, value):
528 """Format the exception part of a traceback.
528 """Format the exception part of a traceback.
529
529
530 The arguments are the exception type and value such as given by
530 The arguments are the exception type and value such as given by
531 sys.exc_info()[:2]. The return value is a list of strings, each ending
531 sys.exc_info()[:2]. The return value is a list of strings, each ending
532 in a newline. Normally, the list contains a single string; however,
532 in a newline. Normally, the list contains a single string; however,
533 for SyntaxError exceptions, it contains several lines that (when
533 for SyntaxError exceptions, it contains several lines that (when
534 printed) display detailed information about where the syntax error
534 printed) display detailed information about where the syntax error
535 occurred. The message indicating which exception occurred is the
535 occurred. The message indicating which exception occurred is the
536 always last string in the list.
536 always last string in the list.
537
537
538 Also lifted nearly verbatim from traceback.py
538 Also lifted nearly verbatim from traceback.py
539 """
539 """
540
540
541 have_filedata = False
541 have_filedata = False
542 Colors = self.Colors
542 Colors = self.Colors
543 list = []
543 list = []
544 try:
544 try:
545 stype = Colors.excName + etype.__name__ + Colors.Normal
545 stype = Colors.excName + etype.__name__ + Colors.Normal
546 except AttributeError:
546 except AttributeError:
547 stype = etype # String exceptions don't get special coloring
547 stype = etype # String exceptions don't get special coloring
548 if value is None:
548 if value is None:
549 list.append( str(stype) + '\n')
549 list.append( str(stype) + '\n')
550 else:
550 else:
551 if etype is SyntaxError:
551 if etype is SyntaxError:
552 try:
552 try:
553 msg, (filename, lineno, offset, line) = value
553 msg, (filename, lineno, offset, line) = value
554 except:
554 except:
555 have_filedata = False
555 have_filedata = False
556 else:
556 else:
557 have_filedata = True
557 have_filedata = True
558 #print 'filename is',filename # dbg
558 #print 'filename is',filename # dbg
559 if not filename: filename = "<string>"
559 if not filename: filename = "<string>"
560 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
560 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
561 (Colors.normalEm,
561 (Colors.normalEm,
562 Colors.filenameEm, filename, Colors.normalEm,
562 Colors.filenameEm, filename, Colors.normalEm,
563 Colors.linenoEm, lineno, Colors.Normal ))
563 Colors.linenoEm, lineno, Colors.Normal ))
564 if line is not None:
564 if line is not None:
565 i = 0
565 i = 0
566 while i < len(line) and line[i].isspace():
566 while i < len(line) and line[i].isspace():
567 i = i+1
567 i = i+1
568 list.append('%s %s%s\n' % (Colors.line,
568 list.append('%s %s%s\n' % (Colors.line,
569 line.strip(),
569 line.strip(),
570 Colors.Normal))
570 Colors.Normal))
571 if offset is not None:
571 if offset is not None:
572 s = ' '
572 s = ' '
573 for c in line[i:offset-1]:
573 for c in line[i:offset-1]:
574 if c.isspace():
574 if c.isspace():
575 s = s + c
575 s = s + c
576 else:
576 else:
577 s = s + ' '
577 s = s + ' '
578 list.append('%s%s^%s\n' % (Colors.caret, s,
578 list.append('%s%s^%s\n' % (Colors.caret, s,
579 Colors.Normal) )
579 Colors.Normal) )
580 value = msg
580 value = msg
581 s = self._some_str(value)
581 s = self._some_str(value)
582 if s:
582 if s:
583 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
583 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
584 Colors.Normal, s))
584 Colors.Normal, s))
585 else:
585 else:
586 list.append('%s\n' % str(stype))
586 list.append('%s\n' % str(stype))
587
587
588 # sync with user hooks
588 # sync with user hooks
589 if have_filedata:
589 if have_filedata:
590 ipinst = ipapi.get()
590 ipinst = ipapi.get()
591 if ipinst is not None:
591 if ipinst is not None:
592 ipinst.hooks.synchronize_with_editor(filename, lineno, 0)
592 ipinst.hooks.synchronize_with_editor(filename, lineno, 0)
593
593
594 return list
594 return list
595
595
596 def get_exception_only(self, etype, value):
596 def get_exception_only(self, etype, value):
597 """Only print the exception type and message, without a traceback.
597 """Only print the exception type and message, without a traceback.
598
598
599 Parameters
599 Parameters
600 ----------
600 ----------
601 etype : exception type
601 etype : exception type
602 value : exception value
602 value : exception value
603 """
603 """
604 return ListTB.structured_traceback(self, etype, value, [])
604 return ListTB.structured_traceback(self, etype, value, [])
605
605
606
606
607 def show_exception_only(self, etype, evalue):
607 def show_exception_only(self, etype, evalue):
608 """Only print the exception type and message, without a traceback.
608 """Only print the exception type and message, without a traceback.
609
609
610 Parameters
610 Parameters
611 ----------
611 ----------
612 etype : exception type
612 etype : exception type
613 value : exception value
613 value : exception value
614 """
614 """
615 # This method needs to use __call__ from *this* class, not the one from
615 # This method needs to use __call__ from *this* class, not the one from
616 # a subclass whose signature or behavior may be different
616 # a subclass whose signature or behavior may be different
617 ostream = self.ostream
617 ostream = self.ostream
618 ostream.flush()
618 ostream.flush()
619 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
619 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
620 ostream.flush()
620 ostream.flush()
621
621
622 def _some_str(self, value):
622 def _some_str(self, value):
623 # Lifted from traceback.py
623 # Lifted from traceback.py
624 try:
624 try:
625 return str(value)
625 return str(value)
626 except:
626 except:
627 return '<unprintable %s object>' % type(value).__name__
627 return '<unprintable %s object>' % type(value).__name__
628
628
629 #----------------------------------------------------------------------------
629 #----------------------------------------------------------------------------
630 class VerboseTB(TBTools):
630 class VerboseTB(TBTools):
631 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
631 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
632 of HTML. Requires inspect and pydoc. Crazy, man.
632 of HTML. Requires inspect and pydoc. Crazy, man.
633
633
634 Modified version which optionally strips the topmost entries from the
634 Modified version which optionally strips the topmost entries from the
635 traceback, to be used with alternate interpreters (because their own code
635 traceback, to be used with alternate interpreters (because their own code
636 would appear in the traceback)."""
636 would appear in the traceback)."""
637
637
638 def __init__(self,color_scheme = 'Linux', call_pdb=False, ostream=None,
638 def __init__(self,color_scheme = 'Linux', call_pdb=False, ostream=None,
639 tb_offset=0, long_header=False, include_vars=True):
639 tb_offset=0, long_header=False, include_vars=True):
640 """Specify traceback offset, headers and color scheme.
640 """Specify traceback offset, headers and color scheme.
641
641
642 Define how many frames to drop from the tracebacks. Calling it with
642 Define how many frames to drop from the tracebacks. Calling it with
643 tb_offset=1 allows use of this handler in interpreters which will have
643 tb_offset=1 allows use of this handler in interpreters which will have
644 their own code at the top of the traceback (VerboseTB will first
644 their own code at the top of the traceback (VerboseTB will first
645 remove that frame before printing the traceback info)."""
645 remove that frame before printing the traceback info)."""
646 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
646 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
647 ostream=ostream)
647 ostream=ostream)
648 self.tb_offset = tb_offset
648 self.tb_offset = tb_offset
649 self.long_header = long_header
649 self.long_header = long_header
650 self.include_vars = include_vars
650 self.include_vars = include_vars
651
651
652 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
652 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
653 context=5):
653 context=5):
654 """Return a nice text document describing the traceback."""
654 """Return a nice text document describing the traceback."""
655
655
656 tb_offset = self.tb_offset if tb_offset is None else tb_offset
656 tb_offset = self.tb_offset if tb_offset is None else tb_offset
657
657
658 # some locals
658 # some locals
659 try:
659 try:
660 etype = etype.__name__
660 etype = etype.__name__
661 except AttributeError:
661 except AttributeError:
662 pass
662 pass
663 Colors = self.Colors # just a shorthand + quicker name lookup
663 Colors = self.Colors # just a shorthand + quicker name lookup
664 ColorsNormal = Colors.Normal # used a lot
664 ColorsNormal = Colors.Normal # used a lot
665 col_scheme = self.color_scheme_table.active_scheme_name
665 col_scheme = self.color_scheme_table.active_scheme_name
666 indent = ' '*INDENT_SIZE
666 indent = ' '*INDENT_SIZE
667 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
667 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
668 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
668 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
669 exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal)
669 exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal)
670
670
671 # some internal-use functions
671 # some internal-use functions
672 def text_repr(value):
672 def text_repr(value):
673 """Hopefully pretty robust repr equivalent."""
673 """Hopefully pretty robust repr equivalent."""
674 # this is pretty horrible but should always return *something*
674 # this is pretty horrible but should always return *something*
675 try:
675 try:
676 return pydoc.text.repr(value)
676 return pydoc.text.repr(value)
677 except KeyboardInterrupt:
677 except KeyboardInterrupt:
678 raise
678 raise
679 except:
679 except:
680 try:
680 try:
681 return repr(value)
681 return repr(value)
682 except KeyboardInterrupt:
682 except KeyboardInterrupt:
683 raise
683 raise
684 except:
684 except:
685 try:
685 try:
686 # all still in an except block so we catch
686 # all still in an except block so we catch
687 # getattr raising
687 # getattr raising
688 name = getattr(value, '__name__', None)
688 name = getattr(value, '__name__', None)
689 if name:
689 if name:
690 # ick, recursion
690 # ick, recursion
691 return text_repr(name)
691 return text_repr(name)
692 klass = getattr(value, '__class__', None)
692 klass = getattr(value, '__class__', None)
693 if klass:
693 if klass:
694 return '%s instance' % text_repr(klass)
694 return '%s instance' % text_repr(klass)
695 except KeyboardInterrupt:
695 except KeyboardInterrupt:
696 raise
696 raise
697 except:
697 except:
698 return 'UNRECOVERABLE REPR FAILURE'
698 return 'UNRECOVERABLE REPR FAILURE'
699 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
699 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
700 def nullrepr(value, repr=text_repr): return ''
700 def nullrepr(value, repr=text_repr): return ''
701
701
702 # meat of the code begins
702 # meat of the code begins
703 try:
703 try:
704 etype = etype.__name__
704 etype = etype.__name__
705 except AttributeError:
705 except AttributeError:
706 pass
706 pass
707
707
708 if self.long_header:
708 if self.long_header:
709 # Header with the exception type, python version, and date
709 # Header with the exception type, python version, and date
710 pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable
710 pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable
711 date = time.ctime(time.time())
711 date = time.ctime(time.time())
712
712
713 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
713 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
714 exc, ' '*(75-len(str(etype))-len(pyver)),
714 exc, ' '*(75-len(str(etype))-len(pyver)),
715 pyver, date.rjust(75) )
715 pyver, date.rjust(75) )
716 head += "\nA problem occured executing Python code. Here is the sequence of function"\
716 head += "\nA problem occured executing Python code. Here is the sequence of function"\
717 "\ncalls leading up to the error, with the most recent (innermost) call last."
717 "\ncalls leading up to the error, with the most recent (innermost) call last."
718 else:
718 else:
719 # Simplified header
719 # Simplified header
720 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
720 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
721 'Traceback (most recent call last)'.\
721 'Traceback (most recent call last)'.\
722 rjust(75 - len(str(etype)) ) )
722 rjust(75 - len(str(etype)) ) )
723 frames = []
723 frames = []
724 # Flush cache before calling inspect. This helps alleviate some of the
724 # Flush cache before calling inspect. This helps alleviate some of the
725 # problems with python 2.3's inspect.py.
725 # problems with python 2.3's inspect.py.
726 linecache.checkcache()
726 linecache.checkcache()
727 # Drop topmost frames if requested
727 # Drop topmost frames if requested
728 try:
728 try:
729 # Try the default getinnerframes and Alex's: Alex's fixes some
729 # Try the default getinnerframes and Alex's: Alex's fixes some
730 # problems, but it generates empty tracebacks for console errors
730 # problems, but it generates empty tracebacks for console errors
731 # (5 blanks lines) where none should be returned.
731 # (5 blanks lines) where none should be returned.
732 #records = inspect.getinnerframes(etb, context)[tb_offset:]
732 #records = inspect.getinnerframes(etb, context)[tb_offset:]
733 #print 'python records:', records # dbg
733 #print 'python records:', records # dbg
734 records = _fixed_getinnerframes(etb, context, tb_offset)
734 records = _fixed_getinnerframes(etb, context, tb_offset)
735 #print 'alex records:', records # dbg
735 #print 'alex records:', records # dbg
736 except:
736 except:
737
737
738 # FIXME: I've been getting many crash reports from python 2.3
738 # FIXME: I've been getting many crash reports from python 2.3
739 # users, traceable to inspect.py. If I can find a small test-case
739 # users, traceable to inspect.py. If I can find a small test-case
740 # to reproduce this, I should either write a better workaround or
740 # to reproduce this, I should either write a better workaround or
741 # file a bug report against inspect (if that's the real problem).
741 # file a bug report against inspect (if that's the real problem).
742 # So far, I haven't been able to find an isolated example to
742 # So far, I haven't been able to find an isolated example to
743 # reproduce the problem.
743 # reproduce the problem.
744 inspect_error()
744 inspect_error()
745 traceback.print_exc(file=self.ostream)
745 traceback.print_exc(file=self.ostream)
746 info('\nUnfortunately, your original traceback can not be constructed.\n')
746 info('\nUnfortunately, your original traceback can not be constructed.\n')
747 return ''
747 return ''
748
748
749 # build some color string templates outside these nested loops
749 # build some color string templates outside these nested loops
750 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
750 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
751 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
751 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
752 ColorsNormal)
752 ColorsNormal)
753 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
753 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
754 (Colors.vName, Colors.valEm, ColorsNormal)
754 (Colors.vName, Colors.valEm, ColorsNormal)
755 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
755 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
756 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
756 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
757 Colors.vName, ColorsNormal)
757 Colors.vName, ColorsNormal)
758 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
758 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
759 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
759 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
760 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
760 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
761 ColorsNormal)
761 ColorsNormal)
762
762
763 # now, loop over all records printing context and info
763 # now, loop over all records printing context and info
764 abspath = os.path.abspath
764 abspath = os.path.abspath
765 for frame, file, lnum, func, lines, index in records:
765 for frame, file, lnum, func, lines, index in records:
766 #print '*** record:',file,lnum,func,lines,index # dbg
766 #print '*** record:',file,lnum,func,lines,index # dbg
767 try:
767 try:
768 file = file and abspath(file) or '?'
768 file = file and abspath(file) or '?'
769 except OSError:
769 except OSError:
770 # if file is '<console>' or something not in the filesystem,
770 # if file is '<console>' or something not in the filesystem,
771 # the abspath call will throw an OSError. Just ignore it and
771 # the abspath call will throw an OSError. Just ignore it and
772 # keep the original file string.
772 # keep the original file string.
773 pass
773 pass
774 link = tpl_link % file
774 link = tpl_link % file
775 try:
775 try:
776 args, varargs, varkw, locals = inspect.getargvalues(frame)
776 args, varargs, varkw, locals = inspect.getargvalues(frame)
777 except:
777 except:
778 # This can happen due to a bug in python2.3. We should be
778 # This can happen due to a bug in python2.3. We should be
779 # able to remove this try/except when 2.4 becomes a
779 # able to remove this try/except when 2.4 becomes a
780 # requirement. Bug details at http://python.org/sf/1005466
780 # requirement. Bug details at http://python.org/sf/1005466
781 inspect_error()
781 inspect_error()
782 traceback.print_exc(file=self.ostream)
782 traceback.print_exc(file=self.ostream)
783 info("\nIPython's exception reporting continues...\n")
783 info("\nIPython's exception reporting continues...\n")
784
784
785 if func == '?':
785 if func == '?':
786 call = ''
786 call = ''
787 else:
787 else:
788 # Decide whether to include variable details or not
788 # Decide whether to include variable details or not
789 var_repr = self.include_vars and eqrepr or nullrepr
789 var_repr = self.include_vars and eqrepr or nullrepr
790 try:
790 try:
791 call = tpl_call % (func,inspect.formatargvalues(args,
791 call = tpl_call % (func,inspect.formatargvalues(args,
792 varargs, varkw,
792 varargs, varkw,
793 locals,formatvalue=var_repr))
793 locals,formatvalue=var_repr))
794 except KeyError:
794 except KeyError:
795 # Very odd crash from inspect.formatargvalues(). The
795 # This happens in situations like errors inside generator
796 # scenario under which it appeared was a call to
796 # expressions, where local variables are listed in the
797 # view(array,scale) in NumTut.view.view(), where scale had
797 # line, but can't be extracted from the frame. I'm not
798 # been defined as a scalar (it should be a tuple). Somehow
798 # 100% sure this isn't actually a bug in inspect itself,
799 # inspect messes up resolving the argument list of view()
799 # but since there's no info for us to compute with, the
800 # and barfs out. At some point I should dig into this one
800 # best we can do is report the failure and move on. Here
801 # and file a bug report about it.
801 # we must *not* call any traceback construction again,
802 inspect_error()
802 # because that would mess up use of %debug later on. So we
803 traceback.print_exc(file=self.ostream)
803 # simply report the failure and move on. The only
804 info("\nIPython's exception reporting continues...\n")
804 # limitation will be that this frame won't have locals
805 # listed in the call signature. Quite subtle problem...
806 # I can't think of a good way to validate this in a unit
807 # test, but running a script consisting of:
808 # dict( (k,v.strip()) for (k,v) in range(10) )
809 # will illustrate the error, if this exception catch is
810 # disabled.
805 call = tpl_call_fail % func
811 call = tpl_call_fail % func
806
812
807 # Initialize a list of names on the current line, which the
813 # Initialize a list of names on the current line, which the
808 # tokenizer below will populate.
814 # tokenizer below will populate.
809 names = []
815 names = []
810
816
811 def tokeneater(token_type, token, start, end, line):
817 def tokeneater(token_type, token, start, end, line):
812 """Stateful tokeneater which builds dotted names.
818 """Stateful tokeneater which builds dotted names.
813
819
814 The list of names it appends to (from the enclosing scope) can
820 The list of names it appends to (from the enclosing scope) can
815 contain repeated composite names. This is unavoidable, since
821 contain repeated composite names. This is unavoidable, since
816 there is no way to disambguate partial dotted structures until
822 there is no way to disambguate partial dotted structures until
817 the full list is known. The caller is responsible for pruning
823 the full list is known. The caller is responsible for pruning
818 the final list of duplicates before using it."""
824 the final list of duplicates before using it."""
819
825
820 # build composite names
826 # build composite names
821 if token == '.':
827 if token == '.':
822 try:
828 try:
823 names[-1] += '.'
829 names[-1] += '.'
824 # store state so the next token is added for x.y.z names
830 # store state so the next token is added for x.y.z names
825 tokeneater.name_cont = True
831 tokeneater.name_cont = True
826 return
832 return
827 except IndexError:
833 except IndexError:
828 pass
834 pass
829 if token_type == tokenize.NAME and token not in keyword.kwlist:
835 if token_type == tokenize.NAME and token not in keyword.kwlist:
830 if tokeneater.name_cont:
836 if tokeneater.name_cont:
831 # Dotted names
837 # Dotted names
832 names[-1] += token
838 names[-1] += token
833 tokeneater.name_cont = False
839 tokeneater.name_cont = False
834 else:
840 else:
835 # Regular new names. We append everything, the caller
841 # Regular new names. We append everything, the caller
836 # will be responsible for pruning the list later. It's
842 # will be responsible for pruning the list later. It's
837 # very tricky to try to prune as we go, b/c composite
843 # very tricky to try to prune as we go, b/c composite
838 # names can fool us. The pruning at the end is easy
844 # names can fool us. The pruning at the end is easy
839 # to do (or the caller can print a list with repeated
845 # to do (or the caller can print a list with repeated
840 # names if so desired.
846 # names if so desired.
841 names.append(token)
847 names.append(token)
842 elif token_type == tokenize.NEWLINE:
848 elif token_type == tokenize.NEWLINE:
843 raise IndexError
849 raise IndexError
844 # we need to store a bit of state in the tokenizer to build
850 # we need to store a bit of state in the tokenizer to build
845 # dotted names
851 # dotted names
846 tokeneater.name_cont = False
852 tokeneater.name_cont = False
847
853
848 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
854 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
849 line = getline(file, lnum[0])
855 line = getline(file, lnum[0])
850 lnum[0] += 1
856 lnum[0] += 1
851 return line
857 return line
852
858
853 # Build the list of names on this line of code where the exception
859 # Build the list of names on this line of code where the exception
854 # occurred.
860 # occurred.
855 try:
861 try:
856 # This builds the names list in-place by capturing it from the
862 # This builds the names list in-place by capturing it from the
857 # enclosing scope.
863 # enclosing scope.
858 tokenize.tokenize(linereader, tokeneater)
864 tokenize.tokenize(linereader, tokeneater)
859 except IndexError:
865 except IndexError:
860 # signals exit of tokenizer
866 # signals exit of tokenizer
861 pass
867 pass
862 except tokenize.TokenError,msg:
868 except tokenize.TokenError,msg:
863 _m = ("An unexpected error occurred while tokenizing input\n"
869 _m = ("An unexpected error occurred while tokenizing input\n"
864 "The following traceback may be corrupted or invalid\n"
870 "The following traceback may be corrupted or invalid\n"
865 "The error message is: %s\n" % msg)
871 "The error message is: %s\n" % msg)
866 error(_m)
872 error(_m)
867
873
868 # prune names list of duplicates, but keep the right order
874 # prune names list of duplicates, but keep the right order
869 unique_names = uniq_stable(names)
875 unique_names = uniq_stable(names)
870
876
871 # Start loop over vars
877 # Start loop over vars
872 lvals = []
878 lvals = []
873 if self.include_vars:
879 if self.include_vars:
874 for name_full in unique_names:
880 for name_full in unique_names:
875 name_base = name_full.split('.',1)[0]
881 name_base = name_full.split('.',1)[0]
876 if name_base in frame.f_code.co_varnames:
882 if name_base in frame.f_code.co_varnames:
877 if locals.has_key(name_base):
883 if locals.has_key(name_base):
878 try:
884 try:
879 value = repr(eval(name_full,locals))
885 value = repr(eval(name_full,locals))
880 except:
886 except:
881 value = undefined
887 value = undefined
882 else:
888 else:
883 value = undefined
889 value = undefined
884 name = tpl_local_var % name_full
890 name = tpl_local_var % name_full
885 else:
891 else:
886 if frame.f_globals.has_key(name_base):
892 if frame.f_globals.has_key(name_base):
887 try:
893 try:
888 value = repr(eval(name_full,frame.f_globals))
894 value = repr(eval(name_full,frame.f_globals))
889 except:
895 except:
890 value = undefined
896 value = undefined
891 else:
897 else:
892 value = undefined
898 value = undefined
893 name = tpl_global_var % name_full
899 name = tpl_global_var % name_full
894 lvals.append(tpl_name_val % (name,value))
900 lvals.append(tpl_name_val % (name,value))
895 if lvals:
901 if lvals:
896 lvals = '%s%s' % (indent,em_normal.join(lvals))
902 lvals = '%s%s' % (indent,em_normal.join(lvals))
897 else:
903 else:
898 lvals = ''
904 lvals = ''
899
905
900 level = '%s %s\n' % (link,call)
906 level = '%s %s\n' % (link,call)
901
907
902 if index is None:
908 if index is None:
903 frames.append(level)
909 frames.append(level)
904 else:
910 else:
905 frames.append('%s%s' % (level,''.join(
911 frames.append('%s%s' % (level,''.join(
906 _format_traceback_lines(lnum,index,lines,Colors,lvals,
912 _format_traceback_lines(lnum,index,lines,Colors,lvals,
907 col_scheme))))
913 col_scheme))))
908
914
909 # Get (safely) a string form of the exception info
915 # Get (safely) a string form of the exception info
910 try:
916 try:
911 etype_str,evalue_str = map(str,(etype,evalue))
917 etype_str,evalue_str = map(str,(etype,evalue))
912 except:
918 except:
913 # User exception is improperly defined.
919 # User exception is improperly defined.
914 etype,evalue = str,sys.exc_info()[:2]
920 etype,evalue = str,sys.exc_info()[:2]
915 etype_str,evalue_str = map(str,(etype,evalue))
921 etype_str,evalue_str = map(str,(etype,evalue))
916 # ... and format it
922 # ... and format it
917 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
923 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
918 ColorsNormal, evalue_str)]
924 ColorsNormal, evalue_str)]
919 if type(evalue) is types.InstanceType:
925 if type(evalue) is types.InstanceType:
920 try:
926 try:
921 names = [w for w in dir(evalue) if isinstance(w, basestring)]
927 names = [w for w in dir(evalue) if isinstance(w, basestring)]
922 except:
928 except:
923 # Every now and then, an object with funny inernals blows up
929 # Every now and then, an object with funny inernals blows up
924 # when dir() is called on it. We do the best we can to report
930 # when dir() is called on it. We do the best we can to report
925 # the problem and continue
931 # the problem and continue
926 _m = '%sException reporting error (object with broken dir())%s:'
932 _m = '%sException reporting error (object with broken dir())%s:'
927 exception.append(_m % (Colors.excName,ColorsNormal))
933 exception.append(_m % (Colors.excName,ColorsNormal))
928 etype_str,evalue_str = map(str,sys.exc_info()[:2])
934 etype_str,evalue_str = map(str,sys.exc_info()[:2])
929 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
935 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
930 ColorsNormal, evalue_str))
936 ColorsNormal, evalue_str))
931 names = []
937 names = []
932 for name in names:
938 for name in names:
933 value = text_repr(getattr(evalue, name))
939 value = text_repr(getattr(evalue, name))
934 exception.append('\n%s%s = %s' % (indent, name, value))
940 exception.append('\n%s%s = %s' % (indent, name, value))
935
941
936 # vds: >>
942 # vds: >>
937 if records:
943 if records:
938 filepath, lnum = records[-1][1:3]
944 filepath, lnum = records[-1][1:3]
939 #print "file:", str(file), "linenb", str(lnum) # dbg
945 #print "file:", str(file), "linenb", str(lnum) # dbg
940 filepath = os.path.abspath(filepath)
946 filepath = os.path.abspath(filepath)
941 ipinst = ipapi.get()
947 ipinst = ipapi.get()
942 if ipinst is not None:
948 if ipinst is not None:
943 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
949 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
944 # vds: <<
950 # vds: <<
945
951
946 # return all our info assembled as a single string
952 # return all our info assembled as a single string
947 # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
953 # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
948 return [head] + frames + [''.join(exception[0])]
954 return [head] + frames + [''.join(exception[0])]
949
955
950 def debugger(self,force=False):
956 def debugger(self,force=False):
951 """Call up the pdb debugger if desired, always clean up the tb
957 """Call up the pdb debugger if desired, always clean up the tb
952 reference.
958 reference.
953
959
954 Keywords:
960 Keywords:
955
961
956 - force(False): by default, this routine checks the instance call_pdb
962 - force(False): by default, this routine checks the instance call_pdb
957 flag and does not actually invoke the debugger if the flag is false.
963 flag and does not actually invoke the debugger if the flag is false.
958 The 'force' option forces the debugger to activate even if the flag
964 The 'force' option forces the debugger to activate even if the flag
959 is false.
965 is false.
960
966
961 If the call_pdb flag is set, the pdb interactive debugger is
967 If the call_pdb flag is set, the pdb interactive debugger is
962 invoked. In all cases, the self.tb reference to the current traceback
968 invoked. In all cases, the self.tb reference to the current traceback
963 is deleted to prevent lingering references which hamper memory
969 is deleted to prevent lingering references which hamper memory
964 management.
970 management.
965
971
966 Note that each call to pdb() does an 'import readline', so if your app
972 Note that each call to pdb() does an 'import readline', so if your app
967 requires a special setup for the readline completers, you'll have to
973 requires a special setup for the readline completers, you'll have to
968 fix that by hand after invoking the exception handler."""
974 fix that by hand after invoking the exception handler."""
969
975
970 if force or self.call_pdb:
976 if force or self.call_pdb:
971 if self.pdb is None:
977 if self.pdb is None:
972 self.pdb = debugger.Pdb(
978 self.pdb = debugger.Pdb(
973 self.color_scheme_table.active_scheme_name)
979 self.color_scheme_table.active_scheme_name)
974 # the system displayhook may have changed, restore the original
980 # the system displayhook may have changed, restore the original
975 # for pdb
981 # for pdb
976 display_trap = DisplayTrap(hook=sys.__displayhook__)
982 display_trap = DisplayTrap(hook=sys.__displayhook__)
977 with display_trap:
983 with display_trap:
978 self.pdb.reset()
984 self.pdb.reset()
979 # Find the right frame so we don't pop up inside ipython itself
985 # Find the right frame so we don't pop up inside ipython itself
980 if hasattr(self,'tb') and self.tb is not None:
986 if hasattr(self,'tb') and self.tb is not None:
981 etb = self.tb
987 etb = self.tb
982 else:
988 else:
983 etb = self.tb = sys.last_traceback
989 etb = self.tb = sys.last_traceback
984 while self.tb is not None and self.tb.tb_next is not None:
990 while self.tb is not None and self.tb.tb_next is not None:
985 self.tb = self.tb.tb_next
991 self.tb = self.tb.tb_next
986 if etb and etb.tb_next:
992 if etb and etb.tb_next:
987 etb = etb.tb_next
993 etb = etb.tb_next
988 self.pdb.botframe = etb.tb_frame
994 self.pdb.botframe = etb.tb_frame
989 self.pdb.interaction(self.tb.tb_frame, self.tb)
995 self.pdb.interaction(self.tb.tb_frame, self.tb)
990
996
991 if hasattr(self,'tb'):
997 if hasattr(self,'tb'):
992 del self.tb
998 del self.tb
993
999
994 def handler(self, info=None):
1000 def handler(self, info=None):
995 (etype, evalue, etb) = info or sys.exc_info()
1001 (etype, evalue, etb) = info or sys.exc_info()
996 self.tb = etb
1002 self.tb = etb
997 ostream = self.ostream
1003 ostream = self.ostream
998 ostream.flush()
1004 ostream.flush()
999 ostream.write(self.text(etype, evalue, etb))
1005 ostream.write(self.text(etype, evalue, etb))
1000 ostream.write('\n')
1006 ostream.write('\n')
1001 ostream.flush()
1007 ostream.flush()
1002
1008
1003 # Changed so an instance can just be called as VerboseTB_inst() and print
1009 # Changed so an instance can just be called as VerboseTB_inst() and print
1004 # out the right info on its own.
1010 # out the right info on its own.
1005 def __call__(self, etype=None, evalue=None, etb=None):
1011 def __call__(self, etype=None, evalue=None, etb=None):
1006 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1012 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1007 if etb is None:
1013 if etb is None:
1008 self.handler()
1014 self.handler()
1009 else:
1015 else:
1010 self.handler((etype, evalue, etb))
1016 self.handler((etype, evalue, etb))
1011 try:
1017 try:
1012 self.debugger()
1018 self.debugger()
1013 except KeyboardInterrupt:
1019 except KeyboardInterrupt:
1014 print "\nKeyboardInterrupt"
1020 print "\nKeyboardInterrupt"
1015
1021
1016 #----------------------------------------------------------------------------
1022 #----------------------------------------------------------------------------
1017 class FormattedTB(VerboseTB, ListTB):
1023 class FormattedTB(VerboseTB, ListTB):
1018 """Subclass ListTB but allow calling with a traceback.
1024 """Subclass ListTB but allow calling with a traceback.
1019
1025
1020 It can thus be used as a sys.excepthook for Python > 2.1.
1026 It can thus be used as a sys.excepthook for Python > 2.1.
1021
1027
1022 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1028 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1023
1029
1024 Allows a tb_offset to be specified. This is useful for situations where
1030 Allows a tb_offset to be specified. This is useful for situations where
1025 one needs to remove a number of topmost frames from the traceback (such as
1031 one needs to remove a number of topmost frames from the traceback (such as
1026 occurs with python programs that themselves execute other python code,
1032 occurs with python programs that themselves execute other python code,
1027 like Python shells). """
1033 like Python shells). """
1028
1034
1029 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1035 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1030 ostream=None,
1036 ostream=None,
1031 tb_offset=0, long_header=False, include_vars=False):
1037 tb_offset=0, long_header=False, include_vars=False):
1032
1038
1033 # NEVER change the order of this list. Put new modes at the end:
1039 # NEVER change the order of this list. Put new modes at the end:
1034 self.valid_modes = ['Plain','Context','Verbose']
1040 self.valid_modes = ['Plain','Context','Verbose']
1035 self.verbose_modes = self.valid_modes[1:3]
1041 self.verbose_modes = self.valid_modes[1:3]
1036
1042
1037 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1043 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1038 ostream=ostream, tb_offset=tb_offset,
1044 ostream=ostream, tb_offset=tb_offset,
1039 long_header=long_header, include_vars=include_vars)
1045 long_header=long_header, include_vars=include_vars)
1040
1046
1041 # Different types of tracebacks are joined with different separators to
1047 # Different types of tracebacks are joined with different separators to
1042 # form a single string. They are taken from this dict
1048 # form a single string. They are taken from this dict
1043 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1049 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1044 # set_mode also sets the tb_join_char attribute
1050 # set_mode also sets the tb_join_char attribute
1045 self.set_mode(mode)
1051 self.set_mode(mode)
1046
1052
1047 def _extract_tb(self,tb):
1053 def _extract_tb(self,tb):
1048 if tb:
1054 if tb:
1049 return traceback.extract_tb(tb)
1055 return traceback.extract_tb(tb)
1050 else:
1056 else:
1051 return None
1057 return None
1052
1058
1053 def structured_traceback(self, etype, value, tb, tb_offset=None, context=5):
1059 def structured_traceback(self, etype, value, tb, tb_offset=None, context=5):
1054 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1060 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1055 mode = self.mode
1061 mode = self.mode
1056 if mode in self.verbose_modes:
1062 if mode in self.verbose_modes:
1057 # Verbose modes need a full traceback
1063 # Verbose modes need a full traceback
1058 return VerboseTB.structured_traceback(
1064 return VerboseTB.structured_traceback(
1059 self, etype, value, tb, tb_offset, context
1065 self, etype, value, tb, tb_offset, context
1060 )
1066 )
1061 else:
1067 else:
1062 # We must check the source cache because otherwise we can print
1068 # We must check the source cache because otherwise we can print
1063 # out-of-date source code.
1069 # out-of-date source code.
1064 linecache.checkcache()
1070 linecache.checkcache()
1065 # Now we can extract and format the exception
1071 # Now we can extract and format the exception
1066 elist = self._extract_tb(tb)
1072 elist = self._extract_tb(tb)
1067 return ListTB.structured_traceback(
1073 return ListTB.structured_traceback(
1068 self, etype, value, elist, tb_offset, context
1074 self, etype, value, elist, tb_offset, context
1069 )
1075 )
1070
1076
1071 def stb2text(self, stb):
1077 def stb2text(self, stb):
1072 """Convert a structured traceback (a list) to a string."""
1078 """Convert a structured traceback (a list) to a string."""
1073 return self.tb_join_char.join(stb)
1079 return self.tb_join_char.join(stb)
1074
1080
1075
1081
1076 def set_mode(self,mode=None):
1082 def set_mode(self,mode=None):
1077 """Switch to the desired mode.
1083 """Switch to the desired mode.
1078
1084
1079 If mode is not specified, cycles through the available modes."""
1085 If mode is not specified, cycles through the available modes."""
1080
1086
1081 if not mode:
1087 if not mode:
1082 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
1088 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
1083 len(self.valid_modes)
1089 len(self.valid_modes)
1084 self.mode = self.valid_modes[new_idx]
1090 self.mode = self.valid_modes[new_idx]
1085 elif mode not in self.valid_modes:
1091 elif mode not in self.valid_modes:
1086 raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\
1092 raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\
1087 'Valid modes: '+str(self.valid_modes)
1093 'Valid modes: '+str(self.valid_modes)
1088 else:
1094 else:
1089 self.mode = mode
1095 self.mode = mode
1090 # include variable details only in 'Verbose' mode
1096 # include variable details only in 'Verbose' mode
1091 self.include_vars = (self.mode == self.valid_modes[2])
1097 self.include_vars = (self.mode == self.valid_modes[2])
1092 # Set the join character for generating text tracebacks
1098 # Set the join character for generating text tracebacks
1093 self.tb_join_char = self._join_chars[self.mode]
1099 self.tb_join_char = self._join_chars[self.mode]
1094
1100
1095 # some convenient shorcuts
1101 # some convenient shorcuts
1096 def plain(self):
1102 def plain(self):
1097 self.set_mode(self.valid_modes[0])
1103 self.set_mode(self.valid_modes[0])
1098
1104
1099 def context(self):
1105 def context(self):
1100 self.set_mode(self.valid_modes[1])
1106 self.set_mode(self.valid_modes[1])
1101
1107
1102 def verbose(self):
1108 def verbose(self):
1103 self.set_mode(self.valid_modes[2])
1109 self.set_mode(self.valid_modes[2])
1104
1110
1105 #----------------------------------------------------------------------------
1111 #----------------------------------------------------------------------------
1106 class AutoFormattedTB(FormattedTB):
1112 class AutoFormattedTB(FormattedTB):
1107 """A traceback printer which can be called on the fly.
1113 """A traceback printer which can be called on the fly.
1108
1114
1109 It will find out about exceptions by itself.
1115 It will find out about exceptions by itself.
1110
1116
1111 A brief example:
1117 A brief example:
1112
1118
1113 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1119 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1114 try:
1120 try:
1115 ...
1121 ...
1116 except:
1122 except:
1117 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1123 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1118 """
1124 """
1119
1125
1120 def __call__(self,etype=None,evalue=None,etb=None,
1126 def __call__(self,etype=None,evalue=None,etb=None,
1121 out=None,tb_offset=None):
1127 out=None,tb_offset=None):
1122 """Print out a formatted exception traceback.
1128 """Print out a formatted exception traceback.
1123
1129
1124 Optional arguments:
1130 Optional arguments:
1125 - out: an open file-like object to direct output to.
1131 - out: an open file-like object to direct output to.
1126
1132
1127 - tb_offset: the number of frames to skip over in the stack, on a
1133 - tb_offset: the number of frames to skip over in the stack, on a
1128 per-call basis (this overrides temporarily the instance's tb_offset
1134 per-call basis (this overrides temporarily the instance's tb_offset
1129 given at initialization time. """
1135 given at initialization time. """
1130
1136
1131
1137
1132 if out is None:
1138 if out is None:
1133 out = self.ostream
1139 out = self.ostream
1134 out.flush()
1140 out.flush()
1135 out.write(self.text(etype, evalue, etb, tb_offset))
1141 out.write(self.text(etype, evalue, etb, tb_offset))
1136 out.write('\n')
1142 out.write('\n')
1137 out.flush()
1143 out.flush()
1138 # FIXME: we should remove the auto pdb behavior from here and leave
1144 # FIXME: we should remove the auto pdb behavior from here and leave
1139 # that to the clients.
1145 # that to the clients.
1140 try:
1146 try:
1141 self.debugger()
1147 self.debugger()
1142 except KeyboardInterrupt:
1148 except KeyboardInterrupt:
1143 print "\nKeyboardInterrupt"
1149 print "\nKeyboardInterrupt"
1144
1150
1145 def structured_traceback(self, etype=None, value=None, tb=None,
1151 def structured_traceback(self, etype=None, value=None, tb=None,
1146 tb_offset=None, context=5):
1152 tb_offset=None, context=5):
1147 if etype is None:
1153 if etype is None:
1148 etype,value,tb = sys.exc_info()
1154 etype,value,tb = sys.exc_info()
1149 self.tb = tb
1155 self.tb = tb
1150 return FormattedTB.structured_traceback(
1156 return FormattedTB.structured_traceback(
1151 self, etype, value, tb, tb_offset, context)
1157 self, etype, value, tb, tb_offset, context)
1152
1158
1153 #---------------------------------------------------------------------------
1159 #---------------------------------------------------------------------------
1154
1160
1155 # A simple class to preserve Nathan's original functionality.
1161 # A simple class to preserve Nathan's original functionality.
1156 class ColorTB(FormattedTB):
1162 class ColorTB(FormattedTB):
1157 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1163 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1158 def __init__(self,color_scheme='Linux',call_pdb=0):
1164 def __init__(self,color_scheme='Linux',call_pdb=0):
1159 FormattedTB.__init__(self,color_scheme=color_scheme,
1165 FormattedTB.__init__(self,color_scheme=color_scheme,
1160 call_pdb=call_pdb)
1166 call_pdb=call_pdb)
1161
1167
1162
1168
1163 class SyntaxTB(ListTB):
1169 class SyntaxTB(ListTB):
1164 """Extension which holds some state: the last exception value"""
1170 """Extension which holds some state: the last exception value"""
1165
1171
1166 def __init__(self,color_scheme = 'NoColor'):
1172 def __init__(self,color_scheme = 'NoColor'):
1167 ListTB.__init__(self,color_scheme)
1173 ListTB.__init__(self,color_scheme)
1168 self.last_syntax_error = None
1174 self.last_syntax_error = None
1169
1175
1170 def __call__(self, etype, value, elist):
1176 def __call__(self, etype, value, elist):
1171 self.last_syntax_error = value
1177 self.last_syntax_error = value
1172 ListTB.__call__(self,etype,value,elist)
1178 ListTB.__call__(self,etype,value,elist)
1173
1179
1174 def clear_err_state(self):
1180 def clear_err_state(self):
1175 """Return the current error state and clear it"""
1181 """Return the current error state and clear it"""
1176 e = self.last_syntax_error
1182 e = self.last_syntax_error
1177 self.last_syntax_error = None
1183 self.last_syntax_error = None
1178 return e
1184 return e
1179
1185
1180 def stb2text(self, stb):
1186 def stb2text(self, stb):
1181 """Convert a structured traceback (a list) to a string."""
1187 """Convert a structured traceback (a list) to a string."""
1182 return ''.join(stb)
1188 return ''.join(stb)
1183
1189
1184
1190
1185 #----------------------------------------------------------------------------
1191 #----------------------------------------------------------------------------
1186 # module testing (minimal)
1192 # module testing (minimal)
1187 if __name__ == "__main__":
1193 if __name__ == "__main__":
1188 def spam(c, (d, e)):
1194 def spam(c, (d, e)):
1189 x = c + d
1195 x = c + d
1190 y = c * d
1196 y = c * d
1191 foo(x, y)
1197 foo(x, y)
1192
1198
1193 def foo(a, b, bar=1):
1199 def foo(a, b, bar=1):
1194 eggs(a, b + bar)
1200 eggs(a, b + bar)
1195
1201
1196 def eggs(f, g, z=globals()):
1202 def eggs(f, g, z=globals()):
1197 h = f + g
1203 h = f + g
1198 i = f - g
1204 i = f - g
1199 return h / i
1205 return h / i
1200
1206
1201 print ''
1207 print ''
1202 print '*** Before ***'
1208 print '*** Before ***'
1203 try:
1209 try:
1204 print spam(1, (2, 3))
1210 print spam(1, (2, 3))
1205 except:
1211 except:
1206 traceback.print_exc()
1212 traceback.print_exc()
1207 print ''
1213 print ''
1208
1214
1209 handler = ColorTB()
1215 handler = ColorTB()
1210 print '*** ColorTB ***'
1216 print '*** ColorTB ***'
1211 try:
1217 try:
1212 print spam(1, (2, 3))
1218 print spam(1, (2, 3))
1213 except:
1219 except:
1214 apply(handler, sys.exc_info() )
1220 apply(handler, sys.exc_info() )
1215 print ''
1221 print ''
1216
1222
1217 handler = VerboseTB()
1223 handler = VerboseTB()
1218 print '*** VerboseTB ***'
1224 print '*** VerboseTB ***'
1219 try:
1225 try:
1220 print spam(1, (2, 3))
1226 print spam(1, (2, 3))
1221 except:
1227 except:
1222 apply(handler, sys.exc_info() )
1228 apply(handler, sys.exc_info() )
1223 print ''
1229 print ''
1224
1230
General Comments 0
You need to be logged in to leave comments. Login now