##// END OF EJS Templates
flush stdout/err before printing tracebacks
fperez -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,897 +1,903 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 $Id: ultraTB.py 1956 2006-11-30 05:22:31Z fperez $"""
63 $Id: ultraTB.py 2027 2007-01-19 00:55:09Z fperez $"""
64
64
65 #*****************************************************************************
65 #*****************************************************************************
66 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
66 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
67 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
67 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
68 #
68 #
69 # Distributed under the terms of the BSD License. The full license is in
69 # Distributed under the terms of the BSD License. The full license is in
70 # the file COPYING, distributed as part of this software.
70 # the file COPYING, distributed as part of this software.
71 #*****************************************************************************
71 #*****************************************************************************
72
72
73 from IPython import Release
73 from IPython import Release
74 __author__ = '%s <%s>\n%s <%s>' % (Release.authors['Nathan']+
74 __author__ = '%s <%s>\n%s <%s>' % (Release.authors['Nathan']+
75 Release.authors['Fernando'])
75 Release.authors['Fernando'])
76 __license__ = Release.license
76 __license__ = Release.license
77
77
78 # Required modules
78 # Required modules
79 import inspect
79 import inspect
80 import keyword
80 import keyword
81 import linecache
81 import linecache
82 import os
82 import os
83 import pydoc
83 import pydoc
84 import string
84 import string
85 import sys
85 import sys
86 import time
86 import time
87 import tokenize
87 import tokenize
88 import traceback
88 import traceback
89 import types
89 import types
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 import Debugger
93 from IPython import Debugger
94 from IPython.ipstruct import Struct
94 from IPython.ipstruct import Struct
95 from IPython.excolors import ExceptionColors
95 from IPython.excolors import ExceptionColors
96 from IPython.genutils import Term,uniq_stable,error,info
96 from IPython.genutils import Term,uniq_stable,error,info
97
97
98 # Globals
98 # Globals
99 # amount of space to put line numbers before verbose tracebacks
99 # amount of space to put line numbers before verbose tracebacks
100 INDENT_SIZE = 8
100 INDENT_SIZE = 8
101
101
102 #---------------------------------------------------------------------------
102 #---------------------------------------------------------------------------
103 # Code begins
103 # Code begins
104
104
105 # Utility functions
105 # Utility functions
106 def inspect_error():
106 def inspect_error():
107 """Print a message about internal inspect errors.
107 """Print a message about internal inspect errors.
108
108
109 These are unfortunately quite common."""
109 These are unfortunately quite common."""
110
110
111 error('Internal Python error in the inspect module.\n'
111 error('Internal Python error in the inspect module.\n'
112 'Below is the traceback from this internal error.\n')
112 'Below is the traceback from this internal error.\n')
113
113
114 def _fixed_getinnerframes(etb, context=1,tb_offset=0):
114 def _fixed_getinnerframes(etb, context=1,tb_offset=0):
115 import linecache
115 import linecache
116 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
116 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
117
117
118 records = inspect.getinnerframes(etb, context)
118 records = inspect.getinnerframes(etb, context)
119
119
120 # If the error is at the console, don't build any context, since it would
120 # If the error is at the console, don't build any context, since it would
121 # otherwise produce 5 blank lines printed out (there is no file at the
121 # otherwise produce 5 blank lines printed out (there is no file at the
122 # console)
122 # console)
123 rec_check = records[tb_offset:]
123 rec_check = records[tb_offset:]
124 try:
124 try:
125 rname = rec_check[0][1]
125 rname = rec_check[0][1]
126 if rname == '<ipython console>' or rname.endswith('<string>'):
126 if rname == '<ipython console>' or rname.endswith('<string>'):
127 return rec_check
127 return rec_check
128 except IndexError:
128 except IndexError:
129 pass
129 pass
130
130
131 aux = traceback.extract_tb(etb)
131 aux = traceback.extract_tb(etb)
132 assert len(records) == len(aux)
132 assert len(records) == len(aux)
133 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
133 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
134 maybeStart = lnum-1 - context//2
134 maybeStart = lnum-1 - context//2
135 start = max(maybeStart, 0)
135 start = max(maybeStart, 0)
136 end = start + context
136 end = start + context
137 lines = linecache.getlines(file)[start:end]
137 lines = linecache.getlines(file)[start:end]
138 # pad with empty lines if necessary
138 # pad with empty lines if necessary
139 if maybeStart < 0:
139 if maybeStart < 0:
140 lines = (['\n'] * -maybeStart) + lines
140 lines = (['\n'] * -maybeStart) + lines
141 if len(lines) < context:
141 if len(lines) < context:
142 lines += ['\n'] * (context - len(lines))
142 lines += ['\n'] * (context - len(lines))
143 buf = list(records[i])
143 buf = list(records[i])
144 buf[LNUM_POS] = lnum
144 buf[LNUM_POS] = lnum
145 buf[INDEX_POS] = lnum - 1 - start
145 buf[INDEX_POS] = lnum - 1 - start
146 buf[LINES_POS] = lines
146 buf[LINES_POS] = lines
147 records[i] = tuple(buf)
147 records[i] = tuple(buf)
148 return records[tb_offset:]
148 return records[tb_offset:]
149
149
150 # Helper function -- largely belongs to VerboseTB, but we need the same
150 # Helper function -- largely belongs to VerboseTB, but we need the same
151 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
151 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
152 # can be recognized properly by ipython.el's py-traceback-line-re
152 # can be recognized properly by ipython.el's py-traceback-line-re
153 # (SyntaxErrors have to be treated specially because they have no traceback)
153 # (SyntaxErrors have to be treated specially because they have no traceback)
154 def _formatTracebackLines(lnum, index, lines, Colors, lvals=None):
154 def _formatTracebackLines(lnum, index, lines, Colors, lvals=None):
155 numbers_width = INDENT_SIZE - 1
155 numbers_width = INDENT_SIZE - 1
156 res = []
156 res = []
157 i = lnum - index
157 i = lnum - index
158 for line in lines:
158 for line in lines:
159 if i == lnum:
159 if i == lnum:
160 # This is the line with the error
160 # This is the line with the error
161 pad = numbers_width - len(str(i))
161 pad = numbers_width - len(str(i))
162 if pad >= 3:
162 if pad >= 3:
163 marker = '-'*(pad-3) + '-> '
163 marker = '-'*(pad-3) + '-> '
164 elif pad == 2:
164 elif pad == 2:
165 marker = '> '
165 marker = '> '
166 elif pad == 1:
166 elif pad == 1:
167 marker = '>'
167 marker = '>'
168 else:
168 else:
169 marker = ''
169 marker = ''
170 num = marker + str(i)
170 num = marker + str(i)
171 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
171 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
172 Colors.line, line, Colors.Normal)
172 Colors.line, line, Colors.Normal)
173 else:
173 else:
174 num = '%*s' % (numbers_width,i)
174 num = '%*s' % (numbers_width,i)
175 line = '%s%s%s %s' %(Colors.lineno, num,
175 line = '%s%s%s %s' %(Colors.lineno, num,
176 Colors.Normal, line)
176 Colors.Normal, line)
177
177
178 res.append(line)
178 res.append(line)
179 if lvals and i == lnum:
179 if lvals and i == lnum:
180 res.append(lvals + '\n')
180 res.append(lvals + '\n')
181 i = i + 1
181 i = i + 1
182 return res
182 return res
183
183
184 #---------------------------------------------------------------------------
184 #---------------------------------------------------------------------------
185 # Module classes
185 # Module classes
186 class TBTools:
186 class TBTools:
187 """Basic tools used by all traceback printer classes."""
187 """Basic tools used by all traceback printer classes."""
188
188
189 def __init__(self,color_scheme = 'NoColor',call_pdb=False):
189 def __init__(self,color_scheme = 'NoColor',call_pdb=False):
190 # Whether to call the interactive pdb debugger after printing
190 # Whether to call the interactive pdb debugger after printing
191 # tracebacks or not
191 # tracebacks or not
192 self.call_pdb = call_pdb
192 self.call_pdb = call_pdb
193
193
194 # Create color table
194 # Create color table
195 self.color_scheme_table = ExceptionColors
195 self.color_scheme_table = ExceptionColors
196
196
197 self.set_colors(color_scheme)
197 self.set_colors(color_scheme)
198 self.old_scheme = color_scheme # save initial value for toggles
198 self.old_scheme = color_scheme # save initial value for toggles
199
199
200 if call_pdb:
200 if call_pdb:
201 self.pdb = Debugger.Pdb(self.color_scheme_table.active_scheme_name)
201 self.pdb = Debugger.Pdb(self.color_scheme_table.active_scheme_name)
202 else:
202 else:
203 self.pdb = None
203 self.pdb = None
204
204
205 def set_colors(self,*args,**kw):
205 def set_colors(self,*args,**kw):
206 """Shorthand access to the color table scheme selector method."""
206 """Shorthand access to the color table scheme selector method."""
207
207
208 # Set own color table
208 # Set own color table
209 self.color_scheme_table.set_active_scheme(*args,**kw)
209 self.color_scheme_table.set_active_scheme(*args,**kw)
210 # for convenience, set Colors to the active scheme
210 # for convenience, set Colors to the active scheme
211 self.Colors = self.color_scheme_table.active_colors
211 self.Colors = self.color_scheme_table.active_colors
212 # Also set colors of debugger
212 # Also set colors of debugger
213 if hasattr(self,'pdb') and self.pdb is not None:
213 if hasattr(self,'pdb') and self.pdb is not None:
214 self.pdb.set_colors(*args,**kw)
214 self.pdb.set_colors(*args,**kw)
215
215
216 def color_toggle(self):
216 def color_toggle(self):
217 """Toggle between the currently active color scheme and NoColor."""
217 """Toggle between the currently active color scheme and NoColor."""
218
218
219 if self.color_scheme_table.active_scheme_name == 'NoColor':
219 if self.color_scheme_table.active_scheme_name == 'NoColor':
220 self.color_scheme_table.set_active_scheme(self.old_scheme)
220 self.color_scheme_table.set_active_scheme(self.old_scheme)
221 self.Colors = self.color_scheme_table.active_colors
221 self.Colors = self.color_scheme_table.active_colors
222 else:
222 else:
223 self.old_scheme = self.color_scheme_table.active_scheme_name
223 self.old_scheme = self.color_scheme_table.active_scheme_name
224 self.color_scheme_table.set_active_scheme('NoColor')
224 self.color_scheme_table.set_active_scheme('NoColor')
225 self.Colors = self.color_scheme_table.active_colors
225 self.Colors = self.color_scheme_table.active_colors
226
226
227 #---------------------------------------------------------------------------
227 #---------------------------------------------------------------------------
228 class ListTB(TBTools):
228 class ListTB(TBTools):
229 """Print traceback information from a traceback list, with optional color.
229 """Print traceback information from a traceback list, with optional color.
230
230
231 Calling: requires 3 arguments:
231 Calling: requires 3 arguments:
232 (etype, evalue, elist)
232 (etype, evalue, elist)
233 as would be obtained by:
233 as would be obtained by:
234 etype, evalue, tb = sys.exc_info()
234 etype, evalue, tb = sys.exc_info()
235 if tb:
235 if tb:
236 elist = traceback.extract_tb(tb)
236 elist = traceback.extract_tb(tb)
237 else:
237 else:
238 elist = None
238 elist = None
239
239
240 It can thus be used by programs which need to process the traceback before
240 It can thus be used by programs which need to process the traceback before
241 printing (such as console replacements based on the code module from the
241 printing (such as console replacements based on the code module from the
242 standard library).
242 standard library).
243
243
244 Because they are meant to be called without a full traceback (only a
244 Because they are meant to be called without a full traceback (only a
245 list), instances of this class can't call the interactive pdb debugger."""
245 list), instances of this class can't call the interactive pdb debugger."""
246
246
247 def __init__(self,color_scheme = 'NoColor'):
247 def __init__(self,color_scheme = 'NoColor'):
248 TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0)
248 TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0)
249
249
250 def __call__(self, etype, value, elist):
250 def __call__(self, etype, value, elist):
251 Term.cout.flush()
252 Term.cerr.flush()
251 print >> Term.cerr, self.text(etype,value,elist)
253 print >> Term.cerr, self.text(etype,value,elist)
252
254
253 def text(self,etype, value, elist,context=5):
255 def text(self,etype, value, elist,context=5):
254 """Return a color formatted string with the traceback info."""
256 """Return a color formatted string with the traceback info."""
255
257
256 Colors = self.Colors
258 Colors = self.Colors
257 out_string = ['%s%s%s\n' % (Colors.topline,'-'*60,Colors.Normal)]
259 out_string = ['%s%s%s\n' % (Colors.topline,'-'*60,Colors.Normal)]
258 if elist:
260 if elist:
259 out_string.append('Traceback %s(most recent call last)%s:' % \
261 out_string.append('Traceback %s(most recent call last)%s:' % \
260 (Colors.normalEm, Colors.Normal) + '\n')
262 (Colors.normalEm, Colors.Normal) + '\n')
261 out_string.extend(self._format_list(elist))
263 out_string.extend(self._format_list(elist))
262 lines = self._format_exception_only(etype, value)
264 lines = self._format_exception_only(etype, value)
263 for line in lines[:-1]:
265 for line in lines[:-1]:
264 out_string.append(" "+line)
266 out_string.append(" "+line)
265 out_string.append(lines[-1])
267 out_string.append(lines[-1])
266 return ''.join(out_string)
268 return ''.join(out_string)
267
269
268 def _format_list(self, extracted_list):
270 def _format_list(self, extracted_list):
269 """Format a list of traceback entry tuples for printing.
271 """Format a list of traceback entry tuples for printing.
270
272
271 Given a list of tuples as returned by extract_tb() or
273 Given a list of tuples as returned by extract_tb() or
272 extract_stack(), return a list of strings ready for printing.
274 extract_stack(), return a list of strings ready for printing.
273 Each string in the resulting list corresponds to the item with the
275 Each string in the resulting list corresponds to the item with the
274 same index in the argument list. Each string ends in a newline;
276 same index in the argument list. Each string ends in a newline;
275 the strings may contain internal newlines as well, for those items
277 the strings may contain internal newlines as well, for those items
276 whose source text line is not None.
278 whose source text line is not None.
277
279
278 Lifted almost verbatim from traceback.py
280 Lifted almost verbatim from traceback.py
279 """
281 """
280
282
281 Colors = self.Colors
283 Colors = self.Colors
282 list = []
284 list = []
283 for filename, lineno, name, line in extracted_list[:-1]:
285 for filename, lineno, name, line in extracted_list[:-1]:
284 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
286 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
285 (Colors.filename, filename, Colors.Normal,
287 (Colors.filename, filename, Colors.Normal,
286 Colors.lineno, lineno, Colors.Normal,
288 Colors.lineno, lineno, Colors.Normal,
287 Colors.name, name, Colors.Normal)
289 Colors.name, name, Colors.Normal)
288 if line:
290 if line:
289 item = item + ' %s\n' % line.strip()
291 item = item + ' %s\n' % line.strip()
290 list.append(item)
292 list.append(item)
291 # Emphasize the last entry
293 # Emphasize the last entry
292 filename, lineno, name, line = extracted_list[-1]
294 filename, lineno, name, line = extracted_list[-1]
293 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
295 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
294 (Colors.normalEm,
296 (Colors.normalEm,
295 Colors.filenameEm, filename, Colors.normalEm,
297 Colors.filenameEm, filename, Colors.normalEm,
296 Colors.linenoEm, lineno, Colors.normalEm,
298 Colors.linenoEm, lineno, Colors.normalEm,
297 Colors.nameEm, name, Colors.normalEm,
299 Colors.nameEm, name, Colors.normalEm,
298 Colors.Normal)
300 Colors.Normal)
299 if line:
301 if line:
300 item = item + '%s %s%s\n' % (Colors.line, line.strip(),
302 item = item + '%s %s%s\n' % (Colors.line, line.strip(),
301 Colors.Normal)
303 Colors.Normal)
302 list.append(item)
304 list.append(item)
303 return list
305 return list
304
306
305 def _format_exception_only(self, etype, value):
307 def _format_exception_only(self, etype, value):
306 """Format the exception part of a traceback.
308 """Format the exception part of a traceback.
307
309
308 The arguments are the exception type and value such as given by
310 The arguments are the exception type and value such as given by
309 sys.exc_info()[:2]. The return value is a list of strings, each ending
311 sys.exc_info()[:2]. The return value is a list of strings, each ending
310 in a newline. Normally, the list contains a single string; however,
312 in a newline. Normally, the list contains a single string; however,
311 for SyntaxError exceptions, it contains several lines that (when
313 for SyntaxError exceptions, it contains several lines that (when
312 printed) display detailed information about where the syntax error
314 printed) display detailed information about where the syntax error
313 occurred. The message indicating which exception occurred is the
315 occurred. The message indicating which exception occurred is the
314 always last string in the list.
316 always last string in the list.
315
317
316 Also lifted nearly verbatim from traceback.py
318 Also lifted nearly verbatim from traceback.py
317 """
319 """
318
320
319 Colors = self.Colors
321 Colors = self.Colors
320 list = []
322 list = []
321 if type(etype) == types.ClassType:
323 if type(etype) == types.ClassType:
322 stype = Colors.excName + etype.__name__ + Colors.Normal
324 stype = Colors.excName + etype.__name__ + Colors.Normal
323 else:
325 else:
324 stype = etype # String exceptions don't get special coloring
326 stype = etype # String exceptions don't get special coloring
325 if value is None:
327 if value is None:
326 list.append( str(stype) + '\n')
328 list.append( str(stype) + '\n')
327 else:
329 else:
328 if etype is SyntaxError:
330 if etype is SyntaxError:
329 try:
331 try:
330 msg, (filename, lineno, offset, line) = value
332 msg, (filename, lineno, offset, line) = value
331 except:
333 except:
332 pass
334 pass
333 else:
335 else:
334 #print 'filename is',filename # dbg
336 #print 'filename is',filename # dbg
335 if not filename: filename = "<string>"
337 if not filename: filename = "<string>"
336 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
338 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
337 (Colors.normalEm,
339 (Colors.normalEm,
338 Colors.filenameEm, filename, Colors.normalEm,
340 Colors.filenameEm, filename, Colors.normalEm,
339 Colors.linenoEm, lineno, Colors.Normal ))
341 Colors.linenoEm, lineno, Colors.Normal ))
340 if line is not None:
342 if line is not None:
341 i = 0
343 i = 0
342 while i < len(line) and line[i].isspace():
344 while i < len(line) and line[i].isspace():
343 i = i+1
345 i = i+1
344 list.append('%s %s%s\n' % (Colors.line,
346 list.append('%s %s%s\n' % (Colors.line,
345 line.strip(),
347 line.strip(),
346 Colors.Normal))
348 Colors.Normal))
347 if offset is not None:
349 if offset is not None:
348 s = ' '
350 s = ' '
349 for c in line[i:offset-1]:
351 for c in line[i:offset-1]:
350 if c.isspace():
352 if c.isspace():
351 s = s + c
353 s = s + c
352 else:
354 else:
353 s = s + ' '
355 s = s + ' '
354 list.append('%s%s^%s\n' % (Colors.caret, s,
356 list.append('%s%s^%s\n' % (Colors.caret, s,
355 Colors.Normal) )
357 Colors.Normal) )
356 value = msg
358 value = msg
357 s = self._some_str(value)
359 s = self._some_str(value)
358 if s:
360 if s:
359 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
361 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
360 Colors.Normal, s))
362 Colors.Normal, s))
361 else:
363 else:
362 list.append('%s\n' % str(stype))
364 list.append('%s\n' % str(stype))
363 return list
365 return list
364
366
365 def _some_str(self, value):
367 def _some_str(self, value):
366 # Lifted from traceback.py
368 # Lifted from traceback.py
367 try:
369 try:
368 return str(value)
370 return str(value)
369 except:
371 except:
370 return '<unprintable %s object>' % type(value).__name__
372 return '<unprintable %s object>' % type(value).__name__
371
373
372 #----------------------------------------------------------------------------
374 #----------------------------------------------------------------------------
373 class VerboseTB(TBTools):
375 class VerboseTB(TBTools):
374 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
376 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
375 of HTML. Requires inspect and pydoc. Crazy, man.
377 of HTML. Requires inspect and pydoc. Crazy, man.
376
378
377 Modified version which optionally strips the topmost entries from the
379 Modified version which optionally strips the topmost entries from the
378 traceback, to be used with alternate interpreters (because their own code
380 traceback, to be used with alternate interpreters (because their own code
379 would appear in the traceback)."""
381 would appear in the traceback)."""
380
382
381 def __init__(self,color_scheme = 'Linux',tb_offset=0,long_header=0,
383 def __init__(self,color_scheme = 'Linux',tb_offset=0,long_header=0,
382 call_pdb = 0, include_vars=1):
384 call_pdb = 0, include_vars=1):
383 """Specify traceback offset, headers and color scheme.
385 """Specify traceback offset, headers and color scheme.
384
386
385 Define how many frames to drop from the tracebacks. Calling it with
387 Define how many frames to drop from the tracebacks. Calling it with
386 tb_offset=1 allows use of this handler in interpreters which will have
388 tb_offset=1 allows use of this handler in interpreters which will have
387 their own code at the top of the traceback (VerboseTB will first
389 their own code at the top of the traceback (VerboseTB will first
388 remove that frame before printing the traceback info)."""
390 remove that frame before printing the traceback info)."""
389 TBTools.__init__(self,color_scheme=color_scheme,call_pdb=call_pdb)
391 TBTools.__init__(self,color_scheme=color_scheme,call_pdb=call_pdb)
390 self.tb_offset = tb_offset
392 self.tb_offset = tb_offset
391 self.long_header = long_header
393 self.long_header = long_header
392 self.include_vars = include_vars
394 self.include_vars = include_vars
393
395
394 def text(self, etype, evalue, etb, context=5):
396 def text(self, etype, evalue, etb, context=5):
395 """Return a nice text document describing the traceback."""
397 """Return a nice text document describing the traceback."""
396
398
397 # some locals
399 # some locals
398 Colors = self.Colors # just a shorthand + quicker name lookup
400 Colors = self.Colors # just a shorthand + quicker name lookup
399 ColorsNormal = Colors.Normal # used a lot
401 ColorsNormal = Colors.Normal # used a lot
400 indent = ' '*INDENT_SIZE
402 indent = ' '*INDENT_SIZE
401 exc = '%s%s%s' % (Colors.excName, str(etype), ColorsNormal)
403 exc = '%s%s%s' % (Colors.excName, str(etype), ColorsNormal)
402 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
404 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
403 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
405 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
404
406
405 # some internal-use functions
407 # some internal-use functions
406 def text_repr(value):
408 def text_repr(value):
407 """Hopefully pretty robust repr equivalent."""
409 """Hopefully pretty robust repr equivalent."""
408 # this is pretty horrible but should always return *something*
410 # this is pretty horrible but should always return *something*
409 try:
411 try:
410 return pydoc.text.repr(value)
412 return pydoc.text.repr(value)
411 except KeyboardInterrupt:
413 except KeyboardInterrupt:
412 raise
414 raise
413 except:
415 except:
414 try:
416 try:
415 return repr(value)
417 return repr(value)
416 except KeyboardInterrupt:
418 except KeyboardInterrupt:
417 raise
419 raise
418 except:
420 except:
419 try:
421 try:
420 # all still in an except block so we catch
422 # all still in an except block so we catch
421 # getattr raising
423 # getattr raising
422 name = getattr(value, '__name__', None)
424 name = getattr(value, '__name__', None)
423 if name:
425 if name:
424 # ick, recursion
426 # ick, recursion
425 return text_repr(name)
427 return text_repr(name)
426 klass = getattr(value, '__class__', None)
428 klass = getattr(value, '__class__', None)
427 if klass:
429 if klass:
428 return '%s instance' % text_repr(klass)
430 return '%s instance' % text_repr(klass)
429 except KeyboardInterrupt:
431 except KeyboardInterrupt:
430 raise
432 raise
431 except:
433 except:
432 return 'UNRECOVERABLE REPR FAILURE'
434 return 'UNRECOVERABLE REPR FAILURE'
433 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
435 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
434 def nullrepr(value, repr=text_repr): return ''
436 def nullrepr(value, repr=text_repr): return ''
435
437
436 # meat of the code begins
438 # meat of the code begins
437 if type(etype) is types.ClassType:
439 if type(etype) is types.ClassType:
438 etype = etype.__name__
440 etype = etype.__name__
439
441
440 if self.long_header:
442 if self.long_header:
441 # Header with the exception type, python version, and date
443 # Header with the exception type, python version, and date
442 pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable
444 pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable
443 date = time.ctime(time.time())
445 date = time.ctime(time.time())
444
446
445 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
447 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
446 exc, ' '*(75-len(str(etype))-len(pyver)),
448 exc, ' '*(75-len(str(etype))-len(pyver)),
447 pyver, string.rjust(date, 75) )
449 pyver, string.rjust(date, 75) )
448 head += "\nA problem occured executing Python code. Here is the sequence of function"\
450 head += "\nA problem occured executing Python code. Here is the sequence of function"\
449 "\ncalls leading up to the error, with the most recent (innermost) call last."
451 "\ncalls leading up to the error, with the most recent (innermost) call last."
450 else:
452 else:
451 # Simplified header
453 # Simplified header
452 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
454 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
453 string.rjust('Traceback (most recent call last)',
455 string.rjust('Traceback (most recent call last)',
454 75 - len(str(etype)) ) )
456 75 - len(str(etype)) ) )
455 frames = []
457 frames = []
456 # Flush cache before calling inspect. This helps alleviate some of the
458 # Flush cache before calling inspect. This helps alleviate some of the
457 # problems with python 2.3's inspect.py.
459 # problems with python 2.3's inspect.py.
458 linecache.checkcache()
460 linecache.checkcache()
459 # Drop topmost frames if requested
461 # Drop topmost frames if requested
460 try:
462 try:
461 # Try the default getinnerframes and Alex's: Alex's fixes some
463 # Try the default getinnerframes and Alex's: Alex's fixes some
462 # problems, but it generates empty tracebacks for console errors
464 # problems, but it generates empty tracebacks for console errors
463 # (5 blanks lines) where none should be returned.
465 # (5 blanks lines) where none should be returned.
464 #records = inspect.getinnerframes(etb, context)[self.tb_offset:]
466 #records = inspect.getinnerframes(etb, context)[self.tb_offset:]
465 #print 'python records:', records # dbg
467 #print 'python records:', records # dbg
466 records = _fixed_getinnerframes(etb, context,self.tb_offset)
468 records = _fixed_getinnerframes(etb, context,self.tb_offset)
467 #print 'alex records:', records # dbg
469 #print 'alex records:', records # dbg
468 except:
470 except:
469
471
470 # FIXME: I've been getting many crash reports from python 2.3
472 # FIXME: I've been getting many crash reports from python 2.3
471 # users, traceable to inspect.py. If I can find a small test-case
473 # users, traceable to inspect.py. If I can find a small test-case
472 # to reproduce this, I should either write a better workaround or
474 # to reproduce this, I should either write a better workaround or
473 # file a bug report against inspect (if that's the real problem).
475 # file a bug report against inspect (if that's the real problem).
474 # So far, I haven't been able to find an isolated example to
476 # So far, I haven't been able to find an isolated example to
475 # reproduce the problem.
477 # reproduce the problem.
476 inspect_error()
478 inspect_error()
477 traceback.print_exc(file=Term.cerr)
479 traceback.print_exc(file=Term.cerr)
478 info('\nUnfortunately, your original traceback can not be constructed.\n')
480 info('\nUnfortunately, your original traceback can not be constructed.\n')
479 return ''
481 return ''
480
482
481 # build some color string templates outside these nested loops
483 # build some color string templates outside these nested loops
482 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
484 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
483 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
485 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
484 ColorsNormal)
486 ColorsNormal)
485 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
487 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
486 (Colors.vName, Colors.valEm, ColorsNormal)
488 (Colors.vName, Colors.valEm, ColorsNormal)
487 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
489 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
488 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
490 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
489 Colors.vName, ColorsNormal)
491 Colors.vName, ColorsNormal)
490 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
492 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
491 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
493 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
492 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
494 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
493 ColorsNormal)
495 ColorsNormal)
494
496
495 # now, loop over all records printing context and info
497 # now, loop over all records printing context and info
496 abspath = os.path.abspath
498 abspath = os.path.abspath
497 for frame, file, lnum, func, lines, index in records:
499 for frame, file, lnum, func, lines, index in records:
498 #print '*** record:',file,lnum,func,lines,index # dbg
500 #print '*** record:',file,lnum,func,lines,index # dbg
499 try:
501 try:
500 file = file and abspath(file) or '?'
502 file = file and abspath(file) or '?'
501 except OSError:
503 except OSError:
502 # if file is '<console>' or something not in the filesystem,
504 # if file is '<console>' or something not in the filesystem,
503 # the abspath call will throw an OSError. Just ignore it and
505 # the abspath call will throw an OSError. Just ignore it and
504 # keep the original file string.
506 # keep the original file string.
505 pass
507 pass
506 link = tpl_link % file
508 link = tpl_link % file
507 try:
509 try:
508 args, varargs, varkw, locals = inspect.getargvalues(frame)
510 args, varargs, varkw, locals = inspect.getargvalues(frame)
509 except:
511 except:
510 # This can happen due to a bug in python2.3. We should be
512 # This can happen due to a bug in python2.3. We should be
511 # able to remove this try/except when 2.4 becomes a
513 # able to remove this try/except when 2.4 becomes a
512 # requirement. Bug details at http://python.org/sf/1005466
514 # requirement. Bug details at http://python.org/sf/1005466
513 inspect_error()
515 inspect_error()
514 traceback.print_exc(file=Term.cerr)
516 traceback.print_exc(file=Term.cerr)
515 info("\nIPython's exception reporting continues...\n")
517 info("\nIPython's exception reporting continues...\n")
516
518
517 if func == '?':
519 if func == '?':
518 call = ''
520 call = ''
519 else:
521 else:
520 # Decide whether to include variable details or not
522 # Decide whether to include variable details or not
521 var_repr = self.include_vars and eqrepr or nullrepr
523 var_repr = self.include_vars and eqrepr or nullrepr
522 try:
524 try:
523 call = tpl_call % (func,inspect.formatargvalues(args,
525 call = tpl_call % (func,inspect.formatargvalues(args,
524 varargs, varkw,
526 varargs, varkw,
525 locals,formatvalue=var_repr))
527 locals,formatvalue=var_repr))
526 except KeyError:
528 except KeyError:
527 # Very odd crash from inspect.formatargvalues(). The
529 # Very odd crash from inspect.formatargvalues(). The
528 # scenario under which it appeared was a call to
530 # scenario under which it appeared was a call to
529 # view(array,scale) in NumTut.view.view(), where scale had
531 # view(array,scale) in NumTut.view.view(), where scale had
530 # been defined as a scalar (it should be a tuple). Somehow
532 # been defined as a scalar (it should be a tuple). Somehow
531 # inspect messes up resolving the argument list of view()
533 # inspect messes up resolving the argument list of view()
532 # and barfs out. At some point I should dig into this one
534 # and barfs out. At some point I should dig into this one
533 # and file a bug report about it.
535 # and file a bug report about it.
534 inspect_error()
536 inspect_error()
535 traceback.print_exc(file=Term.cerr)
537 traceback.print_exc(file=Term.cerr)
536 info("\nIPython's exception reporting continues...\n")
538 info("\nIPython's exception reporting continues...\n")
537 call = tpl_call_fail % func
539 call = tpl_call_fail % func
538
540
539 # Initialize a list of names on the current line, which the
541 # Initialize a list of names on the current line, which the
540 # tokenizer below will populate.
542 # tokenizer below will populate.
541 names = []
543 names = []
542
544
543 def tokeneater(token_type, token, start, end, line):
545 def tokeneater(token_type, token, start, end, line):
544 """Stateful tokeneater which builds dotted names.
546 """Stateful tokeneater which builds dotted names.
545
547
546 The list of names it appends to (from the enclosing scope) can
548 The list of names it appends to (from the enclosing scope) can
547 contain repeated composite names. This is unavoidable, since
549 contain repeated composite names. This is unavoidable, since
548 there is no way to disambguate partial dotted structures until
550 there is no way to disambguate partial dotted structures until
549 the full list is known. The caller is responsible for pruning
551 the full list is known. The caller is responsible for pruning
550 the final list of duplicates before using it."""
552 the final list of duplicates before using it."""
551
553
552 # build composite names
554 # build composite names
553 if token == '.':
555 if token == '.':
554 try:
556 try:
555 names[-1] += '.'
557 names[-1] += '.'
556 # store state so the next token is added for x.y.z names
558 # store state so the next token is added for x.y.z names
557 tokeneater.name_cont = True
559 tokeneater.name_cont = True
558 return
560 return
559 except IndexError:
561 except IndexError:
560 pass
562 pass
561 if token_type == tokenize.NAME and token not in keyword.kwlist:
563 if token_type == tokenize.NAME and token not in keyword.kwlist:
562 if tokeneater.name_cont:
564 if tokeneater.name_cont:
563 # Dotted names
565 # Dotted names
564 names[-1] += token
566 names[-1] += token
565 tokeneater.name_cont = False
567 tokeneater.name_cont = False
566 else:
568 else:
567 # Regular new names. We append everything, the caller
569 # Regular new names. We append everything, the caller
568 # will be responsible for pruning the list later. It's
570 # will be responsible for pruning the list later. It's
569 # very tricky to try to prune as we go, b/c composite
571 # very tricky to try to prune as we go, b/c composite
570 # names can fool us. The pruning at the end is easy
572 # names can fool us. The pruning at the end is easy
571 # to do (or the caller can print a list with repeated
573 # to do (or the caller can print a list with repeated
572 # names if so desired.
574 # names if so desired.
573 names.append(token)
575 names.append(token)
574 elif token_type == tokenize.NEWLINE:
576 elif token_type == tokenize.NEWLINE:
575 raise IndexError
577 raise IndexError
576 # we need to store a bit of state in the tokenizer to build
578 # we need to store a bit of state in the tokenizer to build
577 # dotted names
579 # dotted names
578 tokeneater.name_cont = False
580 tokeneater.name_cont = False
579
581
580 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
582 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
581 line = getline(file, lnum[0])
583 line = getline(file, lnum[0])
582 lnum[0] += 1
584 lnum[0] += 1
583 return line
585 return line
584
586
585 # Build the list of names on this line of code where the exception
587 # Build the list of names on this line of code where the exception
586 # occurred.
588 # occurred.
587 try:
589 try:
588 # This builds the names list in-place by capturing it from the
590 # This builds the names list in-place by capturing it from the
589 # enclosing scope.
591 # enclosing scope.
590 tokenize.tokenize(linereader, tokeneater)
592 tokenize.tokenize(linereader, tokeneater)
591 except IndexError:
593 except IndexError:
592 # signals exit of tokenizer
594 # signals exit of tokenizer
593 pass
595 pass
594 except tokenize.TokenError,msg:
596 except tokenize.TokenError,msg:
595 _m = ("An unexpected error occurred while tokenizing input\n"
597 _m = ("An unexpected error occurred while tokenizing input\n"
596 "The following traceback may be corrupted or invalid\n"
598 "The following traceback may be corrupted or invalid\n"
597 "The error message is: %s\n" % msg)
599 "The error message is: %s\n" % msg)
598 error(_m)
600 error(_m)
599
601
600 # prune names list of duplicates, but keep the right order
602 # prune names list of duplicates, but keep the right order
601 unique_names = uniq_stable(names)
603 unique_names = uniq_stable(names)
602
604
603 # Start loop over vars
605 # Start loop over vars
604 lvals = []
606 lvals = []
605 if self.include_vars:
607 if self.include_vars:
606 for name_full in unique_names:
608 for name_full in unique_names:
607 name_base = name_full.split('.',1)[0]
609 name_base = name_full.split('.',1)[0]
608 if name_base in frame.f_code.co_varnames:
610 if name_base in frame.f_code.co_varnames:
609 if locals.has_key(name_base):
611 if locals.has_key(name_base):
610 try:
612 try:
611 value = repr(eval(name_full,locals))
613 value = repr(eval(name_full,locals))
612 except:
614 except:
613 value = undefined
615 value = undefined
614 else:
616 else:
615 value = undefined
617 value = undefined
616 name = tpl_local_var % name_full
618 name = tpl_local_var % name_full
617 else:
619 else:
618 if frame.f_globals.has_key(name_base):
620 if frame.f_globals.has_key(name_base):
619 try:
621 try:
620 value = repr(eval(name_full,frame.f_globals))
622 value = repr(eval(name_full,frame.f_globals))
621 except:
623 except:
622 value = undefined
624 value = undefined
623 else:
625 else:
624 value = undefined
626 value = undefined
625 name = tpl_global_var % name_full
627 name = tpl_global_var % name_full
626 lvals.append(tpl_name_val % (name,value))
628 lvals.append(tpl_name_val % (name,value))
627 if lvals:
629 if lvals:
628 lvals = '%s%s' % (indent,em_normal.join(lvals))
630 lvals = '%s%s' % (indent,em_normal.join(lvals))
629 else:
631 else:
630 lvals = ''
632 lvals = ''
631
633
632 level = '%s %s\n' % (link,call)
634 level = '%s %s\n' % (link,call)
633
635
634 if index is None:
636 if index is None:
635 frames.append(level)
637 frames.append(level)
636 else:
638 else:
637 frames.append('%s%s' % (level,''.join(
639 frames.append('%s%s' % (level,''.join(
638 _formatTracebackLines(lnum,index,lines,self.Colors,lvals))))
640 _formatTracebackLines(lnum,index,lines,self.Colors,lvals))))
639
641
640 # Get (safely) a string form of the exception info
642 # Get (safely) a string form of the exception info
641 try:
643 try:
642 etype_str,evalue_str = map(str,(etype,evalue))
644 etype_str,evalue_str = map(str,(etype,evalue))
643 except:
645 except:
644 # User exception is improperly defined.
646 # User exception is improperly defined.
645 etype,evalue = str,sys.exc_info()[:2]
647 etype,evalue = str,sys.exc_info()[:2]
646 etype_str,evalue_str = map(str,(etype,evalue))
648 etype_str,evalue_str = map(str,(etype,evalue))
647 # ... and format it
649 # ... and format it
648 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
650 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
649 ColorsNormal, evalue_str)]
651 ColorsNormal, evalue_str)]
650 if type(evalue) is types.InstanceType:
652 if type(evalue) is types.InstanceType:
651 try:
653 try:
652 names = [w for w in dir(evalue) if isinstance(w, basestring)]
654 names = [w for w in dir(evalue) if isinstance(w, basestring)]
653 except:
655 except:
654 # Every now and then, an object with funny inernals blows up
656 # Every now and then, an object with funny inernals blows up
655 # when dir() is called on it. We do the best we can to report
657 # when dir() is called on it. We do the best we can to report
656 # the problem and continue
658 # the problem and continue
657 _m = '%sException reporting error (object with broken dir())%s:'
659 _m = '%sException reporting error (object with broken dir())%s:'
658 exception.append(_m % (Colors.excName,ColorsNormal))
660 exception.append(_m % (Colors.excName,ColorsNormal))
659 etype_str,evalue_str = map(str,sys.exc_info()[:2])
661 etype_str,evalue_str = map(str,sys.exc_info()[:2])
660 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
662 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
661 ColorsNormal, evalue_str))
663 ColorsNormal, evalue_str))
662 names = []
664 names = []
663 for name in names:
665 for name in names:
664 value = text_repr(getattr(evalue, name))
666 value = text_repr(getattr(evalue, name))
665 exception.append('\n%s%s = %s' % (indent, name, value))
667 exception.append('\n%s%s = %s' % (indent, name, value))
666 # return all our info assembled as a single string
668 # return all our info assembled as a single string
667 return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
669 return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
668
670
669 def debugger(self,force=False):
671 def debugger(self,force=False):
670 """Call up the pdb debugger if desired, always clean up the tb
672 """Call up the pdb debugger if desired, always clean up the tb
671 reference.
673 reference.
672
674
673 Keywords:
675 Keywords:
674
676
675 - force(False): by default, this routine checks the instance call_pdb
677 - force(False): by default, this routine checks the instance call_pdb
676 flag and does not actually invoke the debugger if the flag is false.
678 flag and does not actually invoke the debugger if the flag is false.
677 The 'force' option forces the debugger to activate even if the flag
679 The 'force' option forces the debugger to activate even if the flag
678 is false.
680 is false.
679
681
680 If the call_pdb flag is set, the pdb interactive debugger is
682 If the call_pdb flag is set, the pdb interactive debugger is
681 invoked. In all cases, the self.tb reference to the current traceback
683 invoked. In all cases, the self.tb reference to the current traceback
682 is deleted to prevent lingering references which hamper memory
684 is deleted to prevent lingering references which hamper memory
683 management.
685 management.
684
686
685 Note that each call to pdb() does an 'import readline', so if your app
687 Note that each call to pdb() does an 'import readline', so if your app
686 requires a special setup for the readline completers, you'll have to
688 requires a special setup for the readline completers, you'll have to
687 fix that by hand after invoking the exception handler."""
689 fix that by hand after invoking the exception handler."""
688
690
689 if force or self.call_pdb:
691 if force or self.call_pdb:
690 if self.pdb is None:
692 if self.pdb is None:
691 self.pdb = Debugger.Pdb(
693 self.pdb = Debugger.Pdb(
692 self.color_scheme_table.active_scheme_name)
694 self.color_scheme_table.active_scheme_name)
693 # the system displayhook may have changed, restore the original
695 # the system displayhook may have changed, restore the original
694 # for pdb
696 # for pdb
695 dhook = sys.displayhook
697 dhook = sys.displayhook
696 sys.displayhook = sys.__displayhook__
698 sys.displayhook = sys.__displayhook__
697 self.pdb.reset()
699 self.pdb.reset()
698 # Find the right frame so we don't pop up inside ipython itself
700 # Find the right frame so we don't pop up inside ipython itself
699 if hasattr(self,'tb'):
701 if hasattr(self,'tb'):
700 etb = self.tb
702 etb = self.tb
701 else:
703 else:
702 etb = self.tb = sys.last_traceback
704 etb = self.tb = sys.last_traceback
703 while self.tb.tb_next is not None:
705 while self.tb.tb_next is not None:
704 self.tb = self.tb.tb_next
706 self.tb = self.tb.tb_next
705 try:
707 try:
706 if etb and etb.tb_next:
708 if etb and etb.tb_next:
707 etb = etb.tb_next
709 etb = etb.tb_next
708 self.pdb.botframe = etb.tb_frame
710 self.pdb.botframe = etb.tb_frame
709 self.pdb.interaction(self.tb.tb_frame, self.tb)
711 self.pdb.interaction(self.tb.tb_frame, self.tb)
710 finally:
712 finally:
711 sys.displayhook = dhook
713 sys.displayhook = dhook
712
714
713 if hasattr(self,'tb'):
715 if hasattr(self,'tb'):
714 del self.tb
716 del self.tb
715
717
716 def handler(self, info=None):
718 def handler(self, info=None):
717 (etype, evalue, etb) = info or sys.exc_info()
719 (etype, evalue, etb) = info or sys.exc_info()
718 self.tb = etb
720 self.tb = etb
721 Term.cout.flush()
722 Term.cerr.flush()
719 print >> Term.cerr, self.text(etype, evalue, etb)
723 print >> Term.cerr, self.text(etype, evalue, etb)
720
724
721 # Changed so an instance can just be called as VerboseTB_inst() and print
725 # Changed so an instance can just be called as VerboseTB_inst() and print
722 # out the right info on its own.
726 # out the right info on its own.
723 def __call__(self, etype=None, evalue=None, etb=None):
727 def __call__(self, etype=None, evalue=None, etb=None):
724 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
728 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
725 if etb is None:
729 if etb is None:
726 self.handler()
730 self.handler()
727 else:
731 else:
728 self.handler((etype, evalue, etb))
732 self.handler((etype, evalue, etb))
729 self.debugger()
733 self.debugger()
730
734
731 #----------------------------------------------------------------------------
735 #----------------------------------------------------------------------------
732 class FormattedTB(VerboseTB,ListTB):
736 class FormattedTB(VerboseTB,ListTB):
733 """Subclass ListTB but allow calling with a traceback.
737 """Subclass ListTB but allow calling with a traceback.
734
738
735 It can thus be used as a sys.excepthook for Python > 2.1.
739 It can thus be used as a sys.excepthook for Python > 2.1.
736
740
737 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
741 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
738
742
739 Allows a tb_offset to be specified. This is useful for situations where
743 Allows a tb_offset to be specified. This is useful for situations where
740 one needs to remove a number of topmost frames from the traceback (such as
744 one needs to remove a number of topmost frames from the traceback (such as
741 occurs with python programs that themselves execute other python code,
745 occurs with python programs that themselves execute other python code,
742 like Python shells). """
746 like Python shells). """
743
747
744 def __init__(self, mode = 'Plain', color_scheme='Linux',
748 def __init__(self, mode = 'Plain', color_scheme='Linux',
745 tb_offset = 0,long_header=0,call_pdb=0,include_vars=0):
749 tb_offset = 0,long_header=0,call_pdb=0,include_vars=0):
746
750
747 # NEVER change the order of this list. Put new modes at the end:
751 # NEVER change the order of this list. Put new modes at the end:
748 self.valid_modes = ['Plain','Context','Verbose']
752 self.valid_modes = ['Plain','Context','Verbose']
749 self.verbose_modes = self.valid_modes[1:3]
753 self.verbose_modes = self.valid_modes[1:3]
750
754
751 VerboseTB.__init__(self,color_scheme,tb_offset,long_header,
755 VerboseTB.__init__(self,color_scheme,tb_offset,long_header,
752 call_pdb=call_pdb,include_vars=include_vars)
756 call_pdb=call_pdb,include_vars=include_vars)
753 self.set_mode(mode)
757 self.set_mode(mode)
754
758
755 def _extract_tb(self,tb):
759 def _extract_tb(self,tb):
756 if tb:
760 if tb:
757 return traceback.extract_tb(tb)
761 return traceback.extract_tb(tb)
758 else:
762 else:
759 return None
763 return None
760
764
761 def text(self, etype, value, tb,context=5,mode=None):
765 def text(self, etype, value, tb,context=5,mode=None):
762 """Return formatted traceback.
766 """Return formatted traceback.
763
767
764 If the optional mode parameter is given, it overrides the current
768 If the optional mode parameter is given, it overrides the current
765 mode."""
769 mode."""
766
770
767 if mode is None:
771 if mode is None:
768 mode = self.mode
772 mode = self.mode
769 if mode in self.verbose_modes:
773 if mode in self.verbose_modes:
770 # verbose modes need a full traceback
774 # verbose modes need a full traceback
771 return VerboseTB.text(self,etype, value, tb,context=5)
775 return VerboseTB.text(self,etype, value, tb,context=5)
772 else:
776 else:
773 # We must check the source cache because otherwise we can print
777 # We must check the source cache because otherwise we can print
774 # out-of-date source code.
778 # out-of-date source code.
775 linecache.checkcache()
779 linecache.checkcache()
776 # Now we can extract and format the exception
780 # Now we can extract and format the exception
777 elist = self._extract_tb(tb)
781 elist = self._extract_tb(tb)
778 if len(elist) > self.tb_offset:
782 if len(elist) > self.tb_offset:
779 del elist[:self.tb_offset]
783 del elist[:self.tb_offset]
780 return ListTB.text(self,etype,value,elist)
784 return ListTB.text(self,etype,value,elist)
781
785
782 def set_mode(self,mode=None):
786 def set_mode(self,mode=None):
783 """Switch to the desired mode.
787 """Switch to the desired mode.
784
788
785 If mode is not specified, cycles through the available modes."""
789 If mode is not specified, cycles through the available modes."""
786
790
787 if not mode:
791 if not mode:
788 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
792 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
789 len(self.valid_modes)
793 len(self.valid_modes)
790 self.mode = self.valid_modes[new_idx]
794 self.mode = self.valid_modes[new_idx]
791 elif mode not in self.valid_modes:
795 elif mode not in self.valid_modes:
792 raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\
796 raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\
793 'Valid modes: '+str(self.valid_modes)
797 'Valid modes: '+str(self.valid_modes)
794 else:
798 else:
795 self.mode = mode
799 self.mode = mode
796 # include variable details only in 'Verbose' mode
800 # include variable details only in 'Verbose' mode
797 self.include_vars = (self.mode == self.valid_modes[2])
801 self.include_vars = (self.mode == self.valid_modes[2])
798
802
799 # some convenient shorcuts
803 # some convenient shorcuts
800 def plain(self):
804 def plain(self):
801 self.set_mode(self.valid_modes[0])
805 self.set_mode(self.valid_modes[0])
802
806
803 def context(self):
807 def context(self):
804 self.set_mode(self.valid_modes[1])
808 self.set_mode(self.valid_modes[1])
805
809
806 def verbose(self):
810 def verbose(self):
807 self.set_mode(self.valid_modes[2])
811 self.set_mode(self.valid_modes[2])
808
812
809 #----------------------------------------------------------------------------
813 #----------------------------------------------------------------------------
810 class AutoFormattedTB(FormattedTB):
814 class AutoFormattedTB(FormattedTB):
811 """A traceback printer which can be called on the fly.
815 """A traceback printer which can be called on the fly.
812
816
813 It will find out about exceptions by itself.
817 It will find out about exceptions by itself.
814
818
815 A brief example:
819 A brief example:
816
820
817 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
821 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
818 try:
822 try:
819 ...
823 ...
820 except:
824 except:
821 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
825 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
822 """
826 """
823 def __call__(self,etype=None,evalue=None,etb=None,
827 def __call__(self,etype=None,evalue=None,etb=None,
824 out=None,tb_offset=None):
828 out=None,tb_offset=None):
825 """Print out a formatted exception traceback.
829 """Print out a formatted exception traceback.
826
830
827 Optional arguments:
831 Optional arguments:
828 - out: an open file-like object to direct output to.
832 - out: an open file-like object to direct output to.
829
833
830 - tb_offset: the number of frames to skip over in the stack, on a
834 - tb_offset: the number of frames to skip over in the stack, on a
831 per-call basis (this overrides temporarily the instance's tb_offset
835 per-call basis (this overrides temporarily the instance's tb_offset
832 given at initialization time. """
836 given at initialization time. """
833
837
834 if out is None:
838 if out is None:
835 out = Term.cerr
839 out = Term.cerr
840 Term.cout.flush()
841 out.flush()
836 if tb_offset is not None:
842 if tb_offset is not None:
837 tb_offset, self.tb_offset = self.tb_offset, tb_offset
843 tb_offset, self.tb_offset = self.tb_offset, tb_offset
838 print >> out, self.text(etype, evalue, etb)
844 print >> out, self.text(etype, evalue, etb)
839 self.tb_offset = tb_offset
845 self.tb_offset = tb_offset
840 else:
846 else:
841 print >> out, self.text(etype, evalue, etb)
847 print >> out, self.text(etype, evalue, etb)
842 self.debugger()
848 self.debugger()
843
849
844 def text(self,etype=None,value=None,tb=None,context=5,mode=None):
850 def text(self,etype=None,value=None,tb=None,context=5,mode=None):
845 if etype is None:
851 if etype is None:
846 etype,value,tb = sys.exc_info()
852 etype,value,tb = sys.exc_info()
847 self.tb = tb
853 self.tb = tb
848 return FormattedTB.text(self,etype,value,tb,context=5,mode=mode)
854 return FormattedTB.text(self,etype,value,tb,context=5,mode=mode)
849
855
850 #---------------------------------------------------------------------------
856 #---------------------------------------------------------------------------
851 # A simple class to preserve Nathan's original functionality.
857 # A simple class to preserve Nathan's original functionality.
852 class ColorTB(FormattedTB):
858 class ColorTB(FormattedTB):
853 """Shorthand to initialize a FormattedTB in Linux colors mode."""
859 """Shorthand to initialize a FormattedTB in Linux colors mode."""
854 def __init__(self,color_scheme='Linux',call_pdb=0):
860 def __init__(self,color_scheme='Linux',call_pdb=0):
855 FormattedTB.__init__(self,color_scheme=color_scheme,
861 FormattedTB.__init__(self,color_scheme=color_scheme,
856 call_pdb=call_pdb)
862 call_pdb=call_pdb)
857
863
858 #----------------------------------------------------------------------------
864 #----------------------------------------------------------------------------
859 # module testing (minimal)
865 # module testing (minimal)
860 if __name__ == "__main__":
866 if __name__ == "__main__":
861 def spam(c, (d, e)):
867 def spam(c, (d, e)):
862 x = c + d
868 x = c + d
863 y = c * d
869 y = c * d
864 foo(x, y)
870 foo(x, y)
865
871
866 def foo(a, b, bar=1):
872 def foo(a, b, bar=1):
867 eggs(a, b + bar)
873 eggs(a, b + bar)
868
874
869 def eggs(f, g, z=globals()):
875 def eggs(f, g, z=globals()):
870 h = f + g
876 h = f + g
871 i = f - g
877 i = f - g
872 return h / i
878 return h / i
873
879
874 print ''
880 print ''
875 print '*** Before ***'
881 print '*** Before ***'
876 try:
882 try:
877 print spam(1, (2, 3))
883 print spam(1, (2, 3))
878 except:
884 except:
879 traceback.print_exc()
885 traceback.print_exc()
880 print ''
886 print ''
881
887
882 handler = ColorTB()
888 handler = ColorTB()
883 print '*** ColorTB ***'
889 print '*** ColorTB ***'
884 try:
890 try:
885 print spam(1, (2, 3))
891 print spam(1, (2, 3))
886 except:
892 except:
887 apply(handler, sys.exc_info() )
893 apply(handler, sys.exc_info() )
888 print ''
894 print ''
889
895
890 handler = VerboseTB()
896 handler = VerboseTB()
891 print '*** VerboseTB ***'
897 print '*** VerboseTB ***'
892 try:
898 try:
893 print spam(1, (2, 3))
899 print spam(1, (2, 3))
894 except:
900 except:
895 apply(handler, sys.exc_info() )
901 apply(handler, sys.exc_info() )
896 print ''
902 print ''
897
903
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now