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