1
# -*- coding: utf-8 -*-
3
ultraTB.py -- Spice up your tracebacks!
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
11
Installation instructions for ColorTB:
13
sys.excepthook = ultraTB.ColorTB()
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.
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).
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
39
Installation instructions for ColorTB:
41
sys.excepthook = ultraTB.VerboseTB()
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'.
47
The colors are defined in the class TBTools through the use of the
48
ColorSchemeTable class. Currently the following exist:
50
- NoColor: allows all of this module to be used in any terminal (the color
51
escapes are just dummy blank strings).
53
- Linux: is meant to look good in a terminal like the Linux console (black
54
or very dark background).
56
- LightBG: similar to Linux but swaps dark/light colors to be more readable
57
in light background terminals.
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.
64
#*****************************************************************************
65
# Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
66
# Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
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
#*****************************************************************************
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
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
99
# amount of space to put line numbers before verbose tracebacks
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'
108
#---------------------------------------------------------------------------
113
"""Print a message about internal inspect errors.
115
These are unfortunately quite common."""
117
error('Internal Python error in the inspect module.\n'
118
'Below is the traceback from this internal error.\n')
121
def findsource(object):
122
"""Return the entire source file and starting line number for an object.
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.
129
FIXED version with which we monkeypatch the stdlib to work around a bug."""
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
136
if inspect.isframe(object):
137
# XXX: can this ever be false?
138
globals_dict = object.f_globals
140
module = getmodule(object, file)
142
globals_dict = module.__dict__
143
lines = linecache.getlines(file, globals_dict)
145
raise IOError('could not get source code')
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.
157
for i in range(len(lines)):
158
match = pat.match(lines[i])
160
# if it's at toplevel, it's already the best one
161
if lines[i][0] == 'c':
163
# else add whitespace to candidate list
164
candidates.append((match.group(1), i))
166
# this will sort by whitespace, and by line number,
167
# less whitespace first
169
return lines, candidates[0][1]
171
raise IOError('could not find class definition')
174
object = object.im_func
175
if isfunction(object):
176
object = object.func_code
177
if istraceback(object):
178
object = object.tb_frame
180
object = object.f_code
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*@)')
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
190
if pmatch(lines[lnum]): break
194
raise IOError('could not find code object')
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
200
def fix_frame_records_filenames(records):
201
"""Try to fix the filenames in each record from inspect.getinnerframes().
203
Particularly, modules loaded from within zip files have useless filenames
204
attached to their code object, and inspect.getinnerframes() just uses it.
207
for frame, filename, line_no, func_name, lines, index in records:
208
# Look inside the frame's globals dictionary for __file__, which should
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
216
fixed_records.append((frame, filename, line_no, func_name, lines, index))
220
def _fixed_getinnerframes(etb, context=1,tb_offset=0):
222
LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
224
records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
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
229
rec_check = records[tb_offset:]
231
rname = rec_check[0][1]
232
if rname == '<ipython console>' or rname.endswith('<string>'):
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
246
lines = (['\n'] * -maybeStart) + lines
247
if len(lines) < context:
248
lines += ['\n'] * (context - len(lines))
249
buf = list(records[i])
251
buf[INDEX_POS] = lnum - 1 - start
252
buf[LINES_POS] = lines
253
records[i] = tuple(buf)
254
return records[tb_offset:]
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)
261
_parser = PyColorize.Parser()
263
def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None):
264
numbers_width = INDENT_SIZE - 1
268
# This lets us get fully syntax-highlighted tracebacks.
271
scheme = __IPYTHON__.rc.colors
273
scheme = DEFAULT_SCHEME
274
_line_format = _parser.format2
277
new_line, err = _line_format(line,'str',scheme)
278
if not err: line = new_line
281
# This is the line with the error
282
pad = numbers_width - len(str(i))
284
marker = '-'*(pad-3) + '-> '
291
num = marker + str(i)
292
line = '%s%s%s %s%s' %(Colors.linenoEm, num,
293
Colors.line, line, Colors.Normal)
295
num = '%*s' % (numbers_width,i)
296
line = '%s%s%s %s' %(Colors.lineno, num,
300
if lvals and i == lnum:
301
res.append(lvals + '\n')
306
#---------------------------------------------------------------------------
309
"""Basic tools used by all traceback printer classes."""
311
def __init__(self,color_scheme = 'NoColor',call_pdb=False):
312
# Whether to call the interactive pdb debugger after printing
314
self.call_pdb = call_pdb
317
self.color_scheme_table = exception_colors()
319
self.set_colors(color_scheme)
320
self.old_scheme = color_scheme # save initial value for toggles
323
self.pdb = Debugger.Pdb(self.color_scheme_table.active_scheme_name)
327
def set_colors(self,*args,**kw):
328
"""Shorthand access to the color table scheme selector method."""
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)
338
def color_toggle(self):
339
"""Toggle between the currently active color scheme and NoColor."""
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
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
349
#---------------------------------------------------------------------------
350
class ListTB(TBTools):
351
"""Print traceback information from a traceback list, with optional color.
353
Calling: requires 3 arguments:
354
(etype, evalue, elist)
355
as would be obtained by:
356
etype, evalue, tb = sys.exc_info()
358
elist = traceback.extract_tb(tb)
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
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."""
369
def __init__(self,color_scheme = 'NoColor'):
370
TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0)
372
def __call__(self, etype, value, elist):
374
print >> Term.cerr, self.text(etype,value,elist)
377
def text(self,etype, value, elist,context=5):
378
"""Return a color formatted string with the traceback info."""
381
out_string = ['%s%s%s\n' % (Colors.topline,'-'*60,Colors.Normal)]
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)
392
def _format_list(self, extracted_list):
393
"""Format a list of traceback entry tuples for printing.
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.
402
Lifted almost verbatim from traceback.py
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)
413
item = item + ' %s\n' % line.strip()
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' % \
419
Colors.filenameEm, filename, Colors.normalEm,
420
Colors.linenoEm, lineno, Colors.normalEm,
421
Colors.nameEm, name, Colors.normalEm,
424
item = item + '%s %s%s\n' % (Colors.line, line.strip(),
429
def _format_exception_only(self, etype, value):
430
"""Format the exception part of a traceback.
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.
440
Also lifted nearly verbatim from traceback.py
443
have_filedata = False
447
stype = Colors.excName + etype.__name__ + Colors.Normal
448
except AttributeError:
449
stype = etype # String exceptions don't get special coloring
451
list.append( str(stype) + '\n')
453
if etype is SyntaxError:
455
msg, (filename, lineno, offset, line) = value
457
have_filedata = False
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' % \
464
Colors.filenameEm, filename, Colors.normalEm,
465
Colors.linenoEm, lineno, Colors.Normal ))
468
while i < len(line) and line[i].isspace():
470
list.append('%s %s%s\n' % (Colors.line,
473
if offset is not None:
475
for c in line[i:offset-1]:
480
list.append('%s%s^%s\n' % (Colors.caret, s,
483
s = self._some_str(value)
485
list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
488
list.append('%s\n' % str(stype))
492
__IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
497
def _some_str(self, value):
498
# Lifted from traceback.py
502
return '<unprintable %s object>' % type(value).__name__
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.
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)."""
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.
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
526
def text(self, etype, evalue, etb, context=5):
527
"""Return a nice text document describing the traceback."""
531
etype = etype.__name__
532
except AttributeError:
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)
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*
547
return pydoc.text.repr(value)
548
except KeyboardInterrupt:
553
except KeyboardInterrupt:
557
# all still in an except block so we catch
559
name = getattr(value, '__name__', None)
562
return text_repr(name)
563
klass = getattr(value, '__class__', None)
565
return '%s instance' % text_repr(klass)
566
except KeyboardInterrupt:
569
return 'UNRECOVERABLE REPR FAILURE'
570
def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
571
def nullrepr(value, repr=text_repr): return ''
573
# meat of the code begins
575
etype = etype.__name__
576
except AttributeError:
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())
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."
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)) ) )
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
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
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.
616
traceback.print_exc(file=Term.cerr)
617
info('\nUnfortunately, your original traceback can not be constructed.\n')
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,
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,
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
639
file = file and abspath(file) or '?'
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.
645
link = tpl_link % file
647
args, varargs, varkw, locals = inspect.getargvalues(frame)
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
653
traceback.print_exc(file=Term.cerr)
654
info("\nIPython's exception reporting continues...\n")
659
# Decide whether to include variable details or not
660
var_repr = self.include_vars and eqrepr or nullrepr
662
call = tpl_call % (func,inspect.formatargvalues(args,
664
locals,formatvalue=var_repr))
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.
674
traceback.print_exc(file=Term.cerr)
675
info("\nIPython's exception reporting continues...\n")
676
call = tpl_call_fail % func
678
# Initialize a list of names on the current line, which the
679
# tokenizer below will populate.
682
def tokeneater(token_type, token, start, end, line):
683
"""Stateful tokeneater which builds dotted names.
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."""
691
# build composite names
695
# store state so the next token is added for x.y.z names
696
tokeneater.name_cont = True
700
if token_type == tokenize.NAME and token not in keyword.kwlist:
701
if tokeneater.name_cont:
704
tokeneater.name_cont = False
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.
713
elif token_type == tokenize.NEWLINE:
715
# we need to store a bit of state in the tokenizer to build
717
tokeneater.name_cont = False
719
def linereader(file=file, lnum=[lnum], getline=linecache.getline):
720
line = getline(file, lnum[0])
724
# Build the list of names on this line of code where the exception
727
# This builds the names list in-place by capturing it from the
729
tokenize.tokenize(linereader, tokeneater)
731
# signals exit of tokenizer
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)
739
# prune names list of duplicates, but keep the right order
740
unique_names = uniq_stable(names)
742
# Start loop over vars
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):
750
value = repr(eval(name_full,locals))
755
name = tpl_local_var % name_full
757
if frame.f_globals.has_key(name_base):
759
value = repr(eval(name_full,frame.f_globals))
764
name = tpl_global_var % name_full
765
lvals.append(tpl_name_val % (name,value))
767
lvals = '%s%s' % (indent,em_normal.join(lvals))
771
level = '%s %s\n' % (link,call)
776
frames.append('%s%s' % (level,''.join(
777
_formatTracebackLines(lnum,index,lines,Colors,lvals,
780
# Get (safely) a string form of the exception info
782
etype_str,evalue_str = map(str,(etype,evalue))
784
# User exception is improperly defined.
785
etype,evalue = str,sys.exc_info()[:2]
786
etype_str,evalue_str = map(str,(etype,evalue))
788
exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
789
ColorsNormal, evalue_str)]
790
if type(evalue) is types.InstanceType:
792
names = [w for w in dir(evalue) if isinstance(w, basestring)]
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))
804
value = text_repr(getattr(evalue, name))
805
exception.append('\n%s%s = %s' % (indent, name, value))
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)
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]) )
818
def debugger(self,force=False):
819
"""Call up the pdb debugger if desired, always clean up the tb
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
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
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."""
838
if force or self.call_pdb:
840
self.pdb = Debugger.Pdb(
841
self.color_scheme_table.active_scheme_name)
842
# the system displayhook may have changed, restore the original
844
dhook = sys.displayhook
845
sys.displayhook = sys.__displayhook__
847
# Find the right frame so we don't pop up inside ipython itself
848
if hasattr(self,'tb'):
851
etb = self.tb = sys.last_traceback
852
while self.tb.tb_next is not None:
853
self.tb = self.tb.tb_next
855
if etb and etb.tb_next:
857
self.pdb.botframe = etb.tb_frame
858
self.pdb.interaction(self.tb.tb_frame, self.tb)
860
sys.displayhook = dhook
862
if hasattr(self,'tb'):
865
def handler(self, info=None):
866
(etype, evalue, etb) = info or sys.exc_info()
869
print >> Term.cerr, self.text(etype, evalue, etb)
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)."""
879
self.handler((etype, evalue, etb))
882
except KeyboardInterrupt:
883
print "\nKeyboardInterrupt"
885
#----------------------------------------------------------------------------
886
class FormattedTB(VerboseTB,ListTB):
887
"""Subclass ListTB but allow calling with a traceback.
889
It can thus be used as a sys.excepthook for Python > 2.1.
891
Also adds 'Context' and 'Verbose' modes, not available in ListTB.
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). """
898
def __init__(self, mode = 'Plain', color_scheme='Linux',
899
tb_offset = 0,long_header=0,call_pdb=0,include_vars=0):
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]
905
VerboseTB.__init__(self,color_scheme,tb_offset,long_header,
906
call_pdb=call_pdb,include_vars=include_vars)
909
def _extract_tb(self,tb):
911
return traceback.extract_tb(tb)
915
def text(self, etype, value, tb,context=5,mode=None):
916
"""Return formatted traceback.
918
If the optional mode parameter is given, it overrides the current
923
if mode in self.verbose_modes:
924
# verbose modes need a full traceback
925
return VerboseTB.text(self,etype, value, tb,context=5)
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)
936
def set_mode(self,mode=None):
937
"""Switch to the desired mode.
939
If mode is not specified, cycles through the available modes."""
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)
950
# include variable details only in 'Verbose' mode
951
self.include_vars = (self.mode == self.valid_modes[2])
953
# some convenient shorcuts
955
self.set_mode(self.valid_modes[0])
958
self.set_mode(self.valid_modes[1])
961
self.set_mode(self.valid_modes[2])
963
#----------------------------------------------------------------------------
964
class AutoFormattedTB(FormattedTB):
965
"""A traceback printer which can be called on the fly.
967
It will find out about exceptions by itself.
971
AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
975
AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
977
def __call__(self,etype=None,evalue=None,etb=None,
978
out=None,tb_offset=None):
979
"""Print out a formatted exception traceback.
982
- out: an open file-like object to direct output to.
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. """
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
996
print >> out, self.text(etype, evalue, etb)
1000
except KeyboardInterrupt:
1001
print "\nKeyboardInterrupt"
1003
def text(self,etype=None,value=None,tb=None,context=5,mode=None):
1005
etype,value,tb = sys.exc_info()
1007
return FormattedTB.text(self,etype,value,tb,context=5,mode=mode)
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,
1017
#----------------------------------------------------------------------------
1018
# module testing (minimal)
1019
if __name__ == "__main__":
1020
def spam(c, (d, e)):
1025
def foo(a, b, bar=1):
1028
def eggs(f, g, z=globals()):
1034
print '*** Before ***'
1036
print spam(1, (2, 3))
1038
traceback.print_exc()
1042
print '*** ColorTB ***'
1044
print spam(1, (2, 3))
1046
apply(handler, sys.exc_info() )
1049
handler = VerboseTB()
1050
print '*** VerboseTB ***'
1052
print spam(1, (2, 3))
1054
apply(handler, sys.exc_info() )