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