cgitb.py revision ad078a0d7a4a4a840c259ec4a744043375de452b
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.
226b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
23ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannon"""
24ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport inspect
25ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport keyword
26ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport linecache
27ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport os
28ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport pydoc
29fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yeeimport sys
30ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport tempfile
31ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport time
32ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport tokenize
33ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport traceback
34ad078a0d7a4a4a840c259ec4a744043375de452bBrett Cannonimport types
35fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee
366b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeedef reset():
376b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    """Return a string that resets the CGI and browser to a known state."""
386b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    return '''<!--: spam
396b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping YeeContent-Type: text/html
406b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
4183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> -->
4283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> --> -->
436b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee</font> </font> </font> </script> </object> </blockquote> </pre>
446b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee</table> </table> </table> </table> </table> </font> </font> </font>'''
456b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
4683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee__UNDEF__ = []                          # a special sentinel object
475fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchlingdef small(text):
485fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling    if text:
495fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        return '<small>' + text + '</small>'
505fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling    else:
515fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        return ''
52182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
535fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchlingdef strong(text):
545fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling    if text:
555fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        return '<strong>' + text + '</strong>'
565fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling    else:
575fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        return ''
58182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
595fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchlingdef grey(text):
605fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling    if text:
615fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        return '<font color="#909090">' + text + '</font>'
625fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling    else:
635fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        return ''
6483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
6583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef lookup(name, frame, locals):
6683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Find the value for a given name in the given environment."""
6783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    if name in locals:
6883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        return 'local', locals[name]
6983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    if name in frame.f_globals:
7083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        return 'global', frame.f_globals[name]
71711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee    if '__builtins__' in frame.f_globals:
72711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee        builtins = frame.f_globals['__builtins__']
73711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee        if type(builtins) is type({}):
74711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee            if name in builtins:
75711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                return 'builtin', builtins[name]
76711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee        else:
77711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee            if hasattr(builtins, name):
78711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                return 'builtin', getattr(builtins, name)
7983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    return None, __UNDEF__
8083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
8183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef scanvars(reader, frame, locals):
8283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Scan one logical line of Python and look up values of variables used."""
8326f6bdf4f19336c125e3fac7aacaf55b312f474bAndrew M. Kuchling    vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__
8483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    for ttype, token, start, end, line in tokenize.generate_tokens(reader):
8583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if ttype == tokenize.NEWLINE: break
8683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if ttype == tokenize.NAME and token not in keyword.kwlist:
8783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if lasttoken == '.':
8883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                if parent is not __UNDEF__:
8983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    value = getattr(parent, token, __UNDEF__)
9083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    vars.append((prefix + token, prefix, value))
9183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            else:
9283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                where, value = lookup(token, frame, locals)
9383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                vars.append((token, where, value))
9483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        elif token == '.':
9583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            prefix += lasttoken + '.'
9683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            parent = value
9783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        else:
9883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            parent, prefix = None, ''
9983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        lasttoken = token
10083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    return vars
10183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
10283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef html((etype, evalue, etb), context=5):
10383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Return a nice HTML document describing a given traceback."""
1046b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    if type(etype) is types.ClassType:
1056b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        etype = etype.__name__
1066b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1076b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    date = time.ctime(time.time())
10883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading(
1095fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        '<big><big>%s</big></big>' %
1105fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        strong(pydoc.html.escape(str(etype))),
11183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        '#ffffff', '#6622aa', pyver + '<br>' + date) + '''
11283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<p>A problem occurred in a Python script.  Here is the sequence of
1135fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchlingfunction calls leading up to the error, in the order they occurred.</p>'''
1146b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
11583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    indent = '<tt>' + small('&nbsp;' * 5) + '&nbsp;</tt>'
1166b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    frames = []
1176b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    records = inspect.getinnerframes(etb, context)
1186b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    for frame, file, lnum, func, lines, index in records:
11907c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl        if file:
12007c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl            file = os.path.abspath(file)
12107c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl            link = '<a href="file://%s">%s</a>' % (file, pydoc.html.escape(file))
12207c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl        else:
12307c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl            file = link = '?'
1246b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        args, varargs, varkw, locals = inspect.getargvalues(frame)
12583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        call = ''
12683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if func != '?':
12783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            call = 'in ' + strong(func) + \
12883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                inspect.formatargvalues(args, varargs, varkw, locals,
12983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    formatvalue=lambda value: '=' + pydoc.html.repr(value))
13083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
13183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        highlight = {}
13283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        def reader(lnum=[lnum]):
13383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            highlight[lnum[0]] = 1
13483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            try: return linecache.getline(file, lnum[0])
13583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            finally: lnum[0] += 1
13683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        vars = scanvars(reader, frame, locals)
13783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
13883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        rows = ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
13983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                ('<big>&nbsp;</big>', link, call)]
1406b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if index is not None:
1416b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            i = lnum - index
1426b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            for line in lines:
14383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                num = small('&nbsp;' * (5-len(str(i))) + str(i)) + '&nbsp;'
14483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                line = '<tt>%s%s</tt>' % (num, pydoc.html.preformat(line))
14583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                if i in highlight:
14683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line)
14783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                else:
14883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    rows.append('<tr><td>%s</td></tr>' % grey(line))
14983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                i += 1
15083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
15183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        done, dump = {}, []
15283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        for name, where, value in vars:
15383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if name in done: continue
15483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            done[name] = 1
15583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if value is not __UNDEF__:
156dbecd93b7203cd187c1978de1207c29d3a9a686cRaymond Hettinger                if where in ('global', 'builtin'):
157711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                    name = ('<em>%s</em> ' % where) + strong(name)
158711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                elif where == 'local':
159711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                    name = strong(name)
160711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                else:
161711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                    name = where + strong(name.split('.')[-1])
16283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                dump.append('%s&nbsp;= %s' % (name, pydoc.html.repr(value)))
16383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            else:
16483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                dump.append(name + ' <em>undefined</em>')
16583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
16683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        rows.append('<tr><td>%s</td></tr>' % small(grey(', '.join(dump))))
1675fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        frames.append('''
16883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<table width="100%%" cellspacing=0 cellpadding=0 border=0>
16983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee%s</table>''' % '\n'.join(rows))
17083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
171b67c94318ec85722ce01c03955d6fbf50e3f7aa9Andrew M. Kuchling    exception = ['<p>%s: %s' % (strong(pydoc.html.escape(str(etype))),
172b67c94318ec85722ce01c03955d6fbf50e3f7aa9Andrew M. Kuchling                                pydoc.html.escape(str(evalue)))]
173135c3174e7edbb2e01fabacfd5e015e49b640efdGeorg Brandl    if isinstance(evalue, BaseException):
1746b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        for name in dir(evalue):
175711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee            if name[:1] == '_': continue
1766b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            value = pydoc.html.repr(getattr(evalue, name))
1776b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            exception.append('\n<br>%s%s&nbsp;=\n%s' % (indent, name, value))
1786b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1796b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    return head + ''.join(frames) + ''.join(exception) + '''
1806b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1816b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
18283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<!-- The above is a description of an error in a Python program, formatted
18383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee     for a Web browser because the 'cgitb' module was enabled.  In case you
18483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee     are not reading this in a Web browser, here is the original traceback:
1856b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1866b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee%s
1876b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee-->
188a09a96a5440144f926c69250dab7b9bbf06bd789Georg Brandl''' % pydoc.html.escape(
189a09a96a5440144f926c69250dab7b9bbf06bd789Georg Brandl          ''.join(traceback.format_exception(etype, evalue, etb)))
1906b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
191364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarodef text((etype, evalue, etb), context=5):
192364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    """Return a plain text document describing a given traceback."""
193364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    if type(etype) is types.ClassType:
194364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        etype = etype.__name__
195364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
196364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    date = time.ctime(time.time())
197364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    head = "%s\n%s\n%s\n" % (str(etype), pyver, date) + '''
198364ca40c2a053718f67c2032769de1f1fd76bb22Skip MontanaroA problem occurred in a Python script.  Here is the sequence of
199364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarofunction calls leading up to the error, in the order they occurred.
200364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro'''
201364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
202364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    frames = []
203364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    records = inspect.getinnerframes(etb, context)
204364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    for frame, file, lnum, func, lines, index in records:
205364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        file = file and os.path.abspath(file) or '?'
206364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        args, varargs, varkw, locals = inspect.getargvalues(frame)
207364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        call = ''
208364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        if func != '?':
209364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            call = 'in ' + func + \
210364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                inspect.formatargvalues(args, varargs, varkw, locals,
211364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                    formatvalue=lambda value: '=' + pydoc.text.repr(value))
212364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
213364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        highlight = {}
214364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        def reader(lnum=[lnum]):
215364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            highlight[lnum[0]] = 1
216364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            try: return linecache.getline(file, lnum[0])
217364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            finally: lnum[0] += 1
218364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        vars = scanvars(reader, frame, locals)
219364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
220364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        rows = [' %s %s' % (file, call)]
221364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        if index is not None:
222364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            i = lnum - index
223364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            for line in lines:
224364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                num = '%5d ' % i
225364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                rows.append(num+line.rstrip())
226364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                i += 1
227364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
228364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        done, dump = {}, []
229364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        for name, where, value in vars:
230364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            if name in done: continue
231364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            done[name] = 1
232364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            if value is not __UNDEF__:
233364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                if where == 'global': name = 'global ' + name
2341c0228a51902233b3478e835be65198d305824e4Skip Montanaro                elif where != 'local': name = where + name.split('.')[-1]
235364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                dump.append('%s = %s' % (name, pydoc.text.repr(value)))
236364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            else:
237364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                dump.append(name + ' undefined')
238364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
239364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        rows.append('\n'.join(dump))
240364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        frames.append('\n%s\n' % '\n'.join(rows))
241364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
242364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    exception = ['%s: %s' % (str(etype), str(evalue))]
243135c3174e7edbb2e01fabacfd5e015e49b640efdGeorg Brandl    if isinstance(evalue, BaseException):
244364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        for name in dir(evalue):
245364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            value = pydoc.text.repr(getattr(evalue, name))
246364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            exception.append('\n%s%s = %s' % (" "*4, name, value))
247364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
248364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    return head + ''.join(frames) + ''.join(exception) + '''
249364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
250364ca40c2a053718f67c2032769de1f1fd76bb22Skip MontanaroThe above is a description of an error in a Python program.  Here is
251364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarothe original traceback:
252364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
253364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro%s
254364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro''' % ''.join(traceback.format_exception(etype, evalue, etb))
255364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
2566b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeeclass Hook:
25783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """A hook to replace sys.excepthook that shows tracebacks in HTML."""
25883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
259364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    def __init__(self, display=1, logdir=None, context=5, file=None,
260364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                 format="html"):
2616b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.display = display          # send tracebacks to browser if true
2626b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.logdir = logdir            # log tracebacks to files if not None
26383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        self.context = context          # number of source code lines per frame
264fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee        self.file = file or sys.stdout  # place to send the output
265364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        self.format = format
2666b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2676b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    def __call__(self, etype, evalue, etb):
2686b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.handle((etype, evalue, etb))
2696b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2706b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    def handle(self, info=None):
2716b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        info = info or sys.exc_info()
272364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        if self.format == "html":
273364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            self.file.write(reset())
2746b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
275364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        formatter = (self.format=="html") and html or text
276364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        plain = False
2776b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        try:
278364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            doc = formatter(info, self.context)
2796b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        except:                         # just in case something goes wrong
280364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            doc = ''.join(traceback.format_exception(*info))
281364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            plain = True
2826b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2836b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if self.display:
284364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            if plain:
2856b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                doc = doc.replace('&', '&amp;').replace('<', '&lt;')
286fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                self.file.write('<pre>' + doc + '</pre>\n')
2876b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            else:
288fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                self.file.write(doc + '\n')
2896b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        else:
290fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee            self.file.write('<p>A problem occurred in a Python script.\n')
2916b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2926b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if self.logdir is not None:
29330633c9a6423459cdd481a6e44954d5933c7842bAndrew M. Kuchling            suffix = ['.txt', '.html'][self.format=="html"]
294364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
2956b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            try:
2963b0a3293c369f3c3f4753e3cb9172cb4e242af76Guido van Rossum                file = os.fdopen(fd, 'w')
2976b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                file.write(doc)
2986b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                file.close()
299fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                msg = '<p> %s contains the description of this error.' % path
3006b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            except:
301fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                msg = '<p> Tried to save traceback to %s, but failed.' % path
302fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee            self.file.write(msg + '\n')
303fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee        try:
304fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee            self.file.flush()
305fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee        except: pass
3066b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
3076b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeehandler = Hook().handle
308364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarodef enable(display=1, logdir=None, context=5, format="html"):
30983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Install an exception handler that formats tracebacks as HTML.
31083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
31183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    The optional argument 'display' can be set to 0 to suppress sending the
31283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    traceback to the browser, and 'logdir' can be set to a directory to cause
31383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    tracebacks to be written to files there."""
314364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    sys.excepthook = Hook(display=display, logdir=logdir,
315364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                          context=context, format=format)
316