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
1028d1da0f5c3b8c6e3fa29e6ae7f891f699a041bb6Florent Xiclunadef html(einfo, context=5):
10383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Return a nice HTML document describing a given traceback."""
1048d1da0f5c3b8c6e3fa29e6ae7f891f699a041bb6Florent Xicluna    etype, evalue, etb = einfo
1056b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    if type(etype) is types.ClassType:
1066b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        etype = etype.__name__
1076b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
1086b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    date = time.ctime(time.time())
10983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading(
1105fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        '<big><big>%s</big></big>' %
1115fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        strong(pydoc.html.escape(str(etype))),
11283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        '#ffffff', '#6622aa', pyver + '<br>' + date) + '''
11383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<p>A problem occurred in a Python script.  Here is the sequence of
1145fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchlingfunction calls leading up to the error, in the order they occurred.</p>'''
1156b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
11683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    indent = '<tt>' + small('&nbsp;' * 5) + '&nbsp;</tt>'
1176b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    frames = []
1186b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    records = inspect.getinnerframes(etb, context)
1196b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    for frame, file, lnum, func, lines, index in records:
12007c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl        if file:
12107c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl            file = os.path.abspath(file)
12207c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl            link = '<a href="file://%s">%s</a>' % (file, pydoc.html.escape(file))
12307c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl        else:
12407c81d9074fd72cb6a7ad1548685a87d5a764a09Georg Brandl            file = link = '?'
1256b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        args, varargs, varkw, locals = inspect.getargvalues(frame)
12683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        call = ''
12783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if func != '?':
12883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            call = 'in ' + strong(func) + \
12983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                inspect.formatargvalues(args, varargs, varkw, locals,
13083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    formatvalue=lambda value: '=' + pydoc.html.repr(value))
13183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
13283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        highlight = {}
13383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        def reader(lnum=[lnum]):
13483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            highlight[lnum[0]] = 1
13583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            try: return linecache.getline(file, lnum[0])
13683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            finally: lnum[0] += 1
13783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        vars = scanvars(reader, frame, locals)
13883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
13983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        rows = ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
14083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                ('<big>&nbsp;</big>', link, call)]
1416b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if index is not None:
1426b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            i = lnum - index
1436b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            for line in lines:
14483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                num = small('&nbsp;' * (5-len(str(i))) + str(i)) + '&nbsp;'
14583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                if i in highlight:
1467ab5eb91b7aba558ef39d7fa4ca7d3a63d4e886eGeorg Brandl                    line = '<tt>=&gt;%s%s</tt>' % (num, pydoc.html.preformat(line))
14783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line)
14883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                else:
1497ab5eb91b7aba558ef39d7fa4ca7d3a63d4e886eGeorg Brandl                    line = '<tt>&nbsp;&nbsp;%s%s</tt>' % (num, pydoc.html.preformat(line))
15083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    rows.append('<tr><td>%s</td></tr>' % grey(line))
15183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                i += 1
15283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
15383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        done, dump = {}, []
15483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        for name, where, value in vars:
15583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if name in done: continue
15683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            done[name] = 1
15783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if value is not __UNDEF__:
158dbecd93b7203cd187c1978de1207c29d3a9a686cRaymond Hettinger                if where in ('global', 'builtin'):
159711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                    name = ('<em>%s</em> ' % where) + strong(name)
160711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                elif where == 'local':
161711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                    name = strong(name)
162711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                else:
163711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee                    name = where + strong(name.split('.')[-1])
16483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                dump.append('%s&nbsp;= %s' % (name, pydoc.html.repr(value)))
16583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            else:
16683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                dump.append(name + ' <em>undefined</em>')
16783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
16883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        rows.append('<tr><td>%s</td></tr>' % small(grey(', '.join(dump))))
1695fcefdb32600fa64d3160f031d48fb033150fdb3Andrew M. Kuchling        frames.append('''
17083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<table width="100%%" cellspacing=0 cellpadding=0 border=0>
17183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee%s</table>''' % '\n'.join(rows))
17283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
173b67c94318ec85722ce01c03955d6fbf50e3f7aa9Andrew M. Kuchling    exception = ['<p>%s: %s' % (strong(pydoc.html.escape(str(etype))),
174b67c94318ec85722ce01c03955d6fbf50e3f7aa9Andrew M. Kuchling                                pydoc.html.escape(str(evalue)))]
175135c3174e7edbb2e01fabacfd5e015e49b640efdGeorg Brandl    if isinstance(evalue, BaseException):
1766b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        for name in dir(evalue):
177711cad769a60b4a53520bb1daf580d2ca252b450Ka-Ping Yee            if name[:1] == '_': continue
1786b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            value = pydoc.html.repr(getattr(evalue, name))
1796b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            exception.append('\n<br>%s%s&nbsp;=\n%s' % (indent, name, value))
1806b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1816b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    return head + ''.join(frames) + ''.join(exception) + '''
1826b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1836b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
18483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<!-- The above is a description of an error in a Python program, formatted
18583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee     for a Web browser because the 'cgitb' module was enabled.  In case you
18683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee     are not reading this in a Web browser, here is the original traceback:
1876b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1886b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee%s
1896b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee-->
190a09a96a5440144f926c69250dab7b9bbf06bd789Georg Brandl''' % pydoc.html.escape(
191a09a96a5440144f926c69250dab7b9bbf06bd789Georg Brandl          ''.join(traceback.format_exception(etype, evalue, etb)))
1926b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1938d1da0f5c3b8c6e3fa29e6ae7f891f699a041bb6Florent Xiclunadef text(einfo, context=5):
194364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    """Return a plain text document describing a given traceback."""
1958d1da0f5c3b8c6e3fa29e6ae7f891f699a041bb6Florent Xicluna    etype, evalue, etb = einfo
196364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    if type(etype) is types.ClassType:
197364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        etype = etype.__name__
198364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
199364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    date = time.ctime(time.time())
200364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    head = "%s\n%s\n%s\n" % (str(etype), pyver, date) + '''
201364ca40c2a053718f67c2032769de1f1fd76bb22Skip MontanaroA problem occurred in a Python script.  Here is the sequence of
202364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarofunction calls leading up to the error, in the order they occurred.
203364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro'''
204364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
205364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    frames = []
206364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    records = inspect.getinnerframes(etb, context)
207364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    for frame, file, lnum, func, lines, index in records:
208364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        file = file and os.path.abspath(file) or '?'
209364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        args, varargs, varkw, locals = inspect.getargvalues(frame)
210364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        call = ''
211364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        if func != '?':
212364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            call = 'in ' + func + \
213364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                inspect.formatargvalues(args, varargs, varkw, locals,
214364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                    formatvalue=lambda value: '=' + pydoc.text.repr(value))
215364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
216364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        highlight = {}
217364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        def reader(lnum=[lnum]):
218364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            highlight[lnum[0]] = 1
219364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            try: return linecache.getline(file, lnum[0])
220364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            finally: lnum[0] += 1
221364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        vars = scanvars(reader, frame, locals)
222364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
223364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        rows = [' %s %s' % (file, call)]
224364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        if index is not None:
225364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            i = lnum - index
226364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            for line in lines:
227364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                num = '%5d ' % i
228364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                rows.append(num+line.rstrip())
229364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                i += 1
230364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
231364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        done, dump = {}, []
232364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        for name, where, value in vars:
233364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            if name in done: continue
234364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            done[name] = 1
235364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            if value is not __UNDEF__:
236364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                if where == 'global': name = 'global ' + name
2371c0228a51902233b3478e835be65198d305824e4Skip Montanaro                elif where != 'local': name = where + name.split('.')[-1]
238364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                dump.append('%s = %s' % (name, pydoc.text.repr(value)))
239364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            else:
240364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                dump.append(name + ' undefined')
241364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
242364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        rows.append('\n'.join(dump))
243364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        frames.append('\n%s\n' % '\n'.join(rows))
244364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
245364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    exception = ['%s: %s' % (str(etype), str(evalue))]
246135c3174e7edbb2e01fabacfd5e015e49b640efdGeorg Brandl    if isinstance(evalue, BaseException):
247364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        for name in dir(evalue):
248364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            value = pydoc.text.repr(getattr(evalue, name))
249364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            exception.append('\n%s%s = %s' % (" "*4, name, value))
250364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
251364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    return head + ''.join(frames) + ''.join(exception) + '''
252364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
253364ca40c2a053718f67c2032769de1f1fd76bb22Skip MontanaroThe above is a description of an error in a Python program.  Here is
254364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarothe original traceback:
255364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
256364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro%s
257364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro''' % ''.join(traceback.format_exception(etype, evalue, etb))
258364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro
2596b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeeclass Hook:
26083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """A hook to replace sys.excepthook that shows tracebacks in HTML."""
26183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
262364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    def __init__(self, display=1, logdir=None, context=5, file=None,
263364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                 format="html"):
2646b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.display = display          # send tracebacks to browser if true
2656b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.logdir = logdir            # log tracebacks to files if not None
26683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        self.context = context          # number of source code lines per frame
267fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee        self.file = file or sys.stdout  # place to send the output
268364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        self.format = format
2696b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2706b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    def __call__(self, etype, evalue, etb):
2716b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.handle((etype, evalue, etb))
2726b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2736b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    def handle(self, info=None):
2746b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        info = info or sys.exc_info()
275364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        if self.format == "html":
276364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            self.file.write(reset())
2776b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
278364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        formatter = (self.format=="html") and html or text
279364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro        plain = False
2806b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        try:
281364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            doc = formatter(info, self.context)
2826b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        except:                         # just in case something goes wrong
283364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            doc = ''.join(traceback.format_exception(*info))
284364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            plain = True
2856b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2866b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if self.display:
287364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            if plain:
2886b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                doc = doc.replace('&', '&amp;').replace('<', '&lt;')
289fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                self.file.write('<pre>' + doc + '</pre>\n')
2906b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            else:
291fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee                self.file.write(doc + '\n')
2926b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        else:
293fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee            self.file.write('<p>A problem occurred in a Python script.\n')
2946b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2956b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if self.logdir is not None:
29630633c9a6423459cdd481a6e44954d5933c7842bAndrew M. Kuchling            suffix = ['.txt', '.html'][self.format=="html"]
297364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro            (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
29854eed2e36d2c8e06ce1f1a30a3979587a3ac1af6R David Murray
2996b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            try:
3003b0a3293c369f3c3f4753e3cb9172cb4e242af76Guido van Rossum                file = os.fdopen(fd, 'w')
3016b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                file.write(doc)
3026b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                file.close()
30354eed2e36d2c8e06ce1f1a30a3979587a3ac1af6R David Murray                msg = '%s contains the description of this error.' % path
3046b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            except:
30554eed2e36d2c8e06ce1f1a30a3979587a3ac1af6R David Murray                msg = 'Tried to save traceback to %s, but failed.' % path
30654eed2e36d2c8e06ce1f1a30a3979587a3ac1af6R David Murray
30754eed2e36d2c8e06ce1f1a30a3979587a3ac1af6R David Murray            if self.format == 'html':
30854eed2e36d2c8e06ce1f1a30a3979587a3ac1af6R David Murray                self.file.write('<p>%s</p>\n' % msg)
30954eed2e36d2c8e06ce1f1a30a3979587a3ac1af6R David Murray            else:
31054eed2e36d2c8e06ce1f1a30a3979587a3ac1af6R David Murray                self.file.write(msg + '\n')
311fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee        try:
312fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee            self.file.flush()
313fa78d0fbe45a7f16d5d30c3d4e3ad30dc4933e8fKa-Ping Yee        except: pass
3146b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
3156b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeehandler = Hook().handle
316364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanarodef enable(display=1, logdir=None, context=5, format="html"):
31783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Install an exception handler that formats tracebacks as HTML.
31883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
31983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    The optional argument 'display' can be set to 0 to suppress sending the
32083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    traceback to the browser, and 'logdir' can be set to a directory to cause
32183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    tracebacks to be written to files there."""
322364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro    sys.excepthook = Hook(display=display, logdir=logdir,
323364ca40c2a053718f67c2032769de1f1fd76bb22Skip Montanaro                          context=context, format=format)
324