~ellisonbg/ipython/trunk-dev

« back to all changes in this revision

Viewing changes to IPython/kernel/core/ultraTB.py

  • Committer: Brian Granger
  • Date: 2010-01-31 23:57:46 UTC
  • mfrom: (1109.1.113 ipython)
  • Revision ID: ellisonbg@gmail.com-20100131235746-rj81qa8klooepq2m
Merging from upstream trunk.

Show diffs side-by-side

added added

removed removed

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