cgitb.py revision 26f6bdf4f19336c125e3fac7aacaf55b312f474b
1364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro"""More comprehensive traceback formatting for Python scripts.
26b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
36b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping YeeTo enable this module, do:
46b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
56b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    import cgitb; cgitb.enable()
66b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
7364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaroat the top of your script.  The optional arguments to enable() are:
86b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
96b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    display     - if true, tracebacks are displayed in the web browser
106b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    logdir      - if set, tracebacks are written to files in this directory
1183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    context     - number of lines of source code to show for each stack frame
12478c10554b366989b341af4017602571edfe9b2eTim Peters    format      - 'text' or 'html' controls the output format
136b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
14364ca40c2a053718f67c2032769de1f1fd76bb22Skip MontanaroBy default, tracebacks are displayed but not saved, the context is 5 lines
15364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaroand the output format is 'html' (for backwards compatibility with the
16364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarooriginal use of this module)
176b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
186b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping YeeAlternatively, if you have caught an exception and want cgitb to display it
19364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarofor you, call cgitb.handler().  The optional argument to handler() is a
20364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro3-item tuple (etype, evalue, etb) just like the value of sys.exc_info().
21364ca40c2a053718f67c2032769de1f1fd76bb22Skip MontanaroThe default handler displays output as HTML.
22364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro"""
236b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
246b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee__author__ = 'Ka-Ping Yee'
256b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee__version__ = '$Revision$'
266b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
27fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yeeimport sys
28fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee
296b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeedef reset():
306b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    """Return a string that resets the CGI and browser to a known state."""
316b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    return '''<!--: spam
326b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping YeeContent-Type: text/html
336b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
3483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> -->
3583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> --> -->
366b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee</font> </font> </font> </script> </object> </blockquote> </pre>
376b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee</table> </table> </table> </table> </table> </font> </font> </font>'''
386b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
3983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee__UNDEF__ = []                          # a special sentinel object
4083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef small(text): return '<small>' + text + '</small>'
4183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef strong(text): return '<strong>' + text + '</strong>'
4283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef grey(text): return '<font color="#909090">' + text + '</font>'
4383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
4483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef lookup(name, frame, locals):
4583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Find the value for a given name in the given environment."""
4683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    if name in locals:
4783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        return 'local', locals[name]
4883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    if name in frame.f_globals:
4983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        return 'global', frame.f_globals[name]
50711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee    if '__builtins__' in frame.f_globals:
51711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee        builtins = frame.f_globals['__builtins__']
52711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee        if type(builtins) is type({}):
53711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee            if name in builtins:
54711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                return 'builtin', builtins[name]
55711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee        else:
56711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee            if hasattr(builtins, name):
57711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                return 'builtin', getattr(builtins, name)
5883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    return None, __UNDEF__
5983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
6083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef scanvars(reader, frame, locals):
6183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Scan one logical line of Python and look up values of variables used."""
6283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    import tokenize, keyword
6326f6bdf4f19336c125e3fac7aacaf55b312f474bAndrew M. Kuchling    vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__
6483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    for ttype, token, start, end, line in tokenize.generate_tokens(reader):
6583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if ttype == tokenize.NEWLINE: break
6683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if ttype == tokenize.NAME and token not in keyword.kwlist:
6783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if lasttoken == '.':
6883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                if parent is not __UNDEF__:
6983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    value = getattr(parent, token, __UNDEF__)
7083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    vars.append((prefix + token, prefix, value))
7183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            else:
7283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                where, value = lookup(token, frame, locals)
7383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                vars.append((token, where, value))
7483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        elif token == '.':
7583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            prefix += lasttoken + '.'
7683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            parent = value
7783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        else:
7883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            parent, prefix = None, ''
7983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        lasttoken = token
8083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    return vars
8183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
8283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef html((etype, evalue, etb), context=5):
8383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Return a nice HTML document describing a given traceback."""
84fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee    import os, types, time, traceback, linecache, inspect, pydoc
856b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
866b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    if type(etype) is types.ClassType:
876b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        etype = etype.__name__
886b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
896b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    date = time.ctime(time.time())
9083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading(
916b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        '<big><big><strong>%s</strong></big></big>' % str(etype),
9283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        '#ffffff', '#6622aa', pyver + '<br>' + date) + '''
9383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<p>A problem occurred in a Python script.  Here is the sequence of
9483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeefunction calls leading up to the error, in the order they occurred.'''
956b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
9683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    indent = '<tt>' + small('&nbsp;' * 5) + '&nbsp;</tt>'
976b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    frames = []
986b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    records = inspect.getinnerframes(etb, context)
996b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    for frame, file, lnum, func, lines, index in records:
1006b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        file = file and os.path.abspath(file) or '?'
10183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        link = '<a href="file://%s">%s</a>' % (file, pydoc.html.escape(file))
1026b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        args, varargs, varkw, locals = inspect.getargvalues(frame)
10383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        call = ''
10483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if func != '?':
10583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            call = 'in ' + strong(func) + \
10683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                inspect.formatargvalues(args, varargs, varkw, locals,
10783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    formatvalue=lambda value: '=' + pydoc.html.repr(value))
10883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
10983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        highlight = {}
11083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        def reader(lnum=[lnum]):
11183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            highlight[lnum[0]] = 1
11283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            try: return linecache.getline(file, lnum[0])
11383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            finally: lnum[0] += 1
11483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        vars = scanvars(reader, frame, locals)
11583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
11683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        rows = ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
11783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                ('<big>&nbsp;</big>', link, call)]
1186b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if index is not None:
1196b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            i = lnum - index
1206b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            for line in lines:
12183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                num = small('&nbsp;' * (5-len(str(i))) + str(i)) + '&nbsp;'
12283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                line = '<tt>%s%s</tt>' % (num, pydoc.html.preformat(line))
12383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                if i in highlight:
12483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line)
12583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                else:
12683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    rows.append('<tr><td>%s</td></tr>' % grey(line))
12783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                i += 1
12883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
12983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        done, dump = {}, []
13083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        for name, where, value in vars:
13183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if name in done: continue
13283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            done[name] = 1
13383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if value is not __UNDEF__:
134711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                if where in ['global', 'builtin']:
135711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                    name = ('<em>%s</em> ' % where) + strong(name)
136711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                elif where == 'local':
137711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                    name = strong(name)
138711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                else:
139711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                    name = where + strong(name.split('.')[-1])
14083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                dump.append('%s&nbsp;= %s' % (name, pydoc.html.repr(value)))
14183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            else:
14283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                dump.append(name + ' <em>undefined</em>')
14383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
14483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        rows.append('<tr><td>%s</td></tr>' % small(grey(', '.join(dump))))
14583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        frames.append('''<p>
14683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<table width="100%%" cellspacing=0 cellpadding=0 border=0>
14783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee%s</table>''' % '\n'.join(rows))
14883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
149b67c94318ec85722ce01c03955d6fbf50e3f7aa9Andrew M. Kuchling    exception = ['<p>%s: %s' % (strong(pydoc.html.escape(str(etype))),
150b67c94318ec85722ce01c03955d6fbf50e3f7aa9Andrew M. Kuchling                                pydoc.html.escape(str(evalue)))]
1516b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    if type(evalue) is types.InstanceType:
1526b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        for name in dir(evalue):
153711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee            if name[:1] == '_': continue
1546b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            value = pydoc.html.repr(getattr(evalue, name))
1556b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            exception.append('\n<br>%s%s&nbsp;=\n%s' % (indent, name, value))
1566b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1576b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    import traceback
1586b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    return head + ''.join(frames) + ''.join(exception) + '''
1596b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1606b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
16183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<!-- The above is a description of an error in a Python program, formatted
16283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee     for a Web browser because the 'cgitb' module was enabled.  In case you
16383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee     are not reading this in a Web browser, here is the original traceback:
1646b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1656b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee%s
1666b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee-->
16783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee''' % ''.join(traceback.format_exception(etype, evalue, etb))
1686b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
169364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarodef text((etype, evalue, etb), context=5):
170364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    """Return a plain text document describing a given traceback."""
171364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    import os, types, time, traceback, linecache, inspect, pydoc
172364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
173364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    if type(etype) is types.ClassType:
174364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        etype = etype.__name__
175364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
176364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    date = time.ctime(time.time())
177364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    head = "%s\n%s\n%s\n" % (str(etype), pyver, date) + '''
178364ca40c2a053718f67c2032769de1f1fd76bb22Skip MontanaroA problem occurred in a Python script.  Here is the sequence of
179364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarofunction calls leading up to the error, in the order they occurred.
180364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro'''
181364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
182364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    frames = []
183364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    records = inspect.getinnerframes(etb, context)
184364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    for frame, file, lnum, func, lines, index in records:
185364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        file = file and os.path.abspath(file) or '?'
186364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        args, varargs, varkw, locals = inspect.getargvalues(frame)
187364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        call = ''
188364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        if func != '?':
189364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            call = 'in ' + func + \
190364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                inspect.formatargvalues(args, varargs, varkw, locals,
191364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                    formatvalue=lambda value: '=' + pydoc.text.repr(value))
192364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
193364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        highlight = {}
194364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        def reader(lnum=[lnum]):
195364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            highlight[lnum[0]] = 1
196364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            try: return linecache.getline(file, lnum[0])
197364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            finally: lnum[0] += 1
198364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        vars = scanvars(reader, frame, locals)
199364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
200364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        rows = [' %s %s' % (file, call)]
201364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        if index is not None:
202364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            i = lnum - index
203364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            for line in lines:
204364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                num = '%5d ' % i
205364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                rows.append(num+line.rstrip())
206364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                i += 1
207364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
208364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        done, dump = {}, []
209364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        for name, where, value in vars:
210364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            if name in done: continue
211364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            done[name] = 1
212364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            if value is not __UNDEF__:
213364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                if where == 'global': name = 'global ' + name
214364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                elif where == 'local': name = name
215364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                else: name = where + name.split('.')[-1]
216364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                dump.append('%s = %s' % (name, pydoc.text.repr(value)))
217364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            else:
218364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                dump.append(name + ' undefined')
219364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
220364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        rows.append('\n'.join(dump))
221364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        frames.append('\n%s\n' % '\n'.join(rows))
222364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
223364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    exception = ['%s: %s' % (str(etype), str(evalue))]
224364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    if type(evalue) is types.InstanceType:
225364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        for name in dir(evalue):
226364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            value = pydoc.text.repr(getattr(evalue, name))
227364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            exception.append('\n%s%s = %s' % (" "*4, name, value))
228364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
229364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    import traceback
230364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    return head + ''.join(frames) + ''.join(exception) + '''
231364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
232364ca40c2a053718f67c2032769de1f1fd76bb22Skip MontanaroThe above is a description of an error in a Python program.  Here is
233364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarothe original traceback:
234364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
235364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro%s
236364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro''' % ''.join(traceback.format_exception(etype, evalue, etb))
237364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
2386b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeeclass Hook:
23983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """A hook to replace sys.excepthook that shows tracebacks in HTML."""
24083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
241364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    def __init__(self, display=1, logdir=None, context=5, file=None,
242364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                 format="html"):
2436b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.display = display          # send tracebacks to browser if true
2446b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.logdir = logdir            # log tracebacks to files if not None
24583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        self.context = context          # number of source code lines per frame
246fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee        self.file = file or sys.stdout  # place to send the output
247364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        self.format = format
2486b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2496b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    def __call__(self, etype, evalue, etb):
2506b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.handle((etype, evalue, etb))
2516b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2526b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    def handle(self, info=None):
2536b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        info = info or sys.exc_info()
254364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        if self.format == "html":
255364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            self.file.write(reset())
2566b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
257364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        formatter = (self.format=="html") and html or text
258364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        plain = False
2596b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        try:
260364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            doc = formatter(info, self.context)
2616b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        except:                         # just in case something goes wrong
2626b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            import traceback
263364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            doc = ''.join(traceback.format_exception(*info))
264364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            plain = True
2656b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2666b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if self.display:
267364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            if plain:
2686b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                doc = doc.replace('&', '&amp;').replace('<', '&lt;')
269fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                self.file.write('<pre>' + doc + '</pre>\n')
2706b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            else:
271fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                self.file.write(doc + '\n')
2726b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        else:
273fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee            self.file.write('<p>A problem occurred in a Python script.\n')
2746b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2756b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if self.logdir is not None:
27683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            import os, tempfile
27730633c9a6423459cdd481a6e44954d5933c7842bAndrew M. Kuchling            suffix = ['.txt', '.html'][self.format=="html"]
278364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
2796b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            try:
2803b0a3293c369f3c3f4753e3cb9172cb4e242af76Guido van Rossum                file = os.fdopen(fd, 'w')
2816b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                file.write(doc)
2826b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                file.close()
283fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                msg = '<p> %s contains the description of this error.' % path
2846b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            except:
285fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                msg = '<p> Tried to save traceback to %s, but failed.' % path
286fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee            self.file.write(msg + '\n')
287fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee        try:
288fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee            self.file.flush()
289fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee        except: pass
2906b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2916b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeehandler = Hook().handle
292364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarodef enable(display=1, logdir=None, context=5, format="html"):
29383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Install an exception handler that formats tracebacks as HTML.
29483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
29583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    The optional argument 'display' can be set to 0 to suppress sending the
29683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    traceback to the browser, and 'logdir' can be set to a directory to cause
29783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    tracebacks to be written to files there."""
298364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    sys.excepthook = Hook(display=display, logdir=logdir,
299364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                          context=context, format=format)
300