cgitb.py revision 83205972a2d027517ebedd827d1c19a5b0264c0a
16b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee"""Handle exceptions in CGI scripts by formatting tracebacks into nice HTML.
26b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
36b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping YeeTo enable this module, do:
46b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
56b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    import cgitb; cgitb.enable()
66b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
76b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeeat the top of your CGI 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
126b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping YeeBy default, tracebacks are displayed but not saved, and context is 5.
146b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
156b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping YeeAlternatively, if you have caught an exception and want cgitb to display it
166b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeefor you, call cgitb.handle().  The optional argument to handle() is a 3-item
176b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeetuple (etype, evalue, etb) just like the value of sys.exc_info()."""
186b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
196b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee__author__ = 'Ka-Ping Yee'
206b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee__version__ = '$Revision$'
216b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
226b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeedef reset():
236b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    """Return a string that resets the CGI and browser to a known state."""
246b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    return '''<!--: spam
256b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping YeeContent-Type: text/html
266b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
2783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> -->
2883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> --> -->
296b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee</font> </font> </font> </script> </object> </blockquote> </pre>
306b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee</table> </table> </table> </table> </table> </font> </font> </font>'''
316b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
3283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee__UNDEF__ = []                          # a special sentinel object
3383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef small(text): return '<small>' + text + '</small>'
3483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef strong(text): return '<strong>' + text + '</strong>'
3583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef grey(text): return '<font color="#909090">' + text + '</font>'
3683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
3783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef lookup(name, frame, locals):
3883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Find the value for a given name in the given environment."""
3983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    if name in locals:
4083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        return 'local', locals[name]
4183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    if name in frame.f_globals:
4283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        return 'global', frame.f_globals[name]
4383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    return None, __UNDEF__
4483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
4583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef scanvars(reader, frame, locals):
4683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Scan one logical line of Python and look up values of variables used."""
4783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    import tokenize, keyword
4883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    vars, lasttoken, parent, prefix = [], None, None, ''
4983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    for ttype, token, start, end, line in tokenize.generate_tokens(reader):
5083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if ttype == tokenize.NEWLINE: break
5183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if ttype == tokenize.NAME and token not in keyword.kwlist:
5283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if lasttoken == '.':
5383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                if parent is not __UNDEF__:
5483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    value = getattr(parent, token, __UNDEF__)
5583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    vars.append((prefix + token, prefix, value))
5683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            else:
5783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                where, value = lookup(token, frame, locals)
5883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                vars.append((token, where, value))
5983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        elif token == '.':
6083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            prefix += lasttoken + '.'
6183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            parent = value
6283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        else:
6383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            parent, prefix = None, ''
6483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        lasttoken = token
6583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    return vars
6683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
6783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef html((etype, evalue, etb), context=5):
6883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Return a nice HTML document describing a given traceback."""
6983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    import sys, os, types, time, traceback, linecache, inspect, pydoc
706b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
716b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    if type(etype) is types.ClassType:
726b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        etype = etype.__name__
736b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
746b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    date = time.ctime(time.time())
7583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading(
766b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        '<big><big><strong>%s</strong></big></big>' % str(etype),
7783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        '#ffffff', '#6622aa', pyver + '<br>' + date) + '''
7883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<p>A problem occurred in a Python script.  Here is the sequence of
7983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeefunction calls leading up to the error, in the order they occurred.'''
806b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
8183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    indent = '<tt>' + small('&nbsp;' * 5) + '&nbsp;</tt>'
826b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    frames = []
836b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    records = inspect.getinnerframes(etb, context)
846b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    for frame, file, lnum, func, lines, index in records:
856b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        file = file and os.path.abspath(file) or '?'
8683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        link = '<a href="file://%s">%s</a>' % (file, pydoc.html.escape(file))
876b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        args, varargs, varkw, locals = inspect.getargvalues(frame)
8883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        call = ''
8983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        if func != '?':
9083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            call = 'in ' + strong(func) + \
9183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                inspect.formatargvalues(args, varargs, varkw, locals,
9283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    formatvalue=lambda value: '=' + pydoc.html.repr(value))
9383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
9483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        highlight = {}
9583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        def reader(lnum=[lnum]):
9683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            highlight[lnum[0]] = 1
9783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            try: return linecache.getline(file, lnum[0])
9883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            finally: lnum[0] += 1
9983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        vars = scanvars(reader, frame, locals)
10083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
10183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        rows = ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
10283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                ('<big>&nbsp;</big>', link, call)]
1036b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if index is not None:
1046b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            i = lnum - index
1056b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            for line in lines:
10683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                num = small('&nbsp;' * (5-len(str(i))) + str(i)) + '&nbsp;'
10783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                line = '<tt>%s%s</tt>' % (num, pydoc.html.preformat(line))
10883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                if i in highlight:
10983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line)
11083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                else:
11183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                    rows.append('<tr><td>%s</td></tr>' % grey(line))
11283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                i += 1
11383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
11483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        done, dump = {}, []
11583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        for name, where, value in vars:
11683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if name in done: continue
11783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            done[name] = 1
11883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            if value is not __UNDEF__:
11983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                if where == 'global': name = '<em>global</em> ' + strong(name)
12083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                elif where == 'local': name = strong(name)
12183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                else: name = where + strong(name.split('.')[-1])
12283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                dump.append('%s&nbsp;= %s' % (name, pydoc.html.repr(value)))
12383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            else:
12483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                dump.append(name + ' <em>undefined</em>')
12583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
12683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        rows.append('<tr><td>%s</td></tr>' % small(grey(', '.join(dump))))
12783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        frames.append('''<p>
12883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<table width="100%%" cellspacing=0 cellpadding=0 border=0>
12983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee%s</table>''' % '\n'.join(rows))
13083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
13183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    exception = ['<p>%s: %s' % (strong(str(etype)), str(evalue))]
1326b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    if type(evalue) is types.InstanceType:
1336b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        for name in dir(evalue):
1346b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            value = pydoc.html.repr(getattr(evalue, name))
1356b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            exception.append('\n<br>%s%s&nbsp;=\n%s' % (indent, name, value))
1366b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1376b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    import traceback
1386b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    return head + ''.join(frames) + ''.join(exception) + '''
1396b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1406b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
14183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee<!-- The above is a description of an error in a Python program, formatted
14283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee     for a Web browser because the 'cgitb' module was enabled.  In case you
14383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee     are not reading this in a Web browser, here is the original traceback:
1446b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1456b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee%s
1466b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee-->
14783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee''' % ''.join(traceback.format_exception(etype, evalue, etb))
1486b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1496b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeeclass Hook:
15083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """A hook to replace sys.excepthook that shows tracebacks in HTML."""
15183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
15283205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    def __init__(self, display=1, logdir=None, context=5):
1536b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.display = display          # send tracebacks to browser if true
1546b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.logdir = logdir            # log tracebacks to files if not None
15583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        self.context = context          # number of source code lines per frame
1566b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1576b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    def __call__(self, etype, evalue, etb):
1586b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        self.handle((etype, evalue, etb))
1596b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1606b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    def handle(self, info=None):
16183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee        import sys
1626b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        info = info or sys.exc_info()
1636b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        print reset()
1646b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1656b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        try:
16683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            text, doc = 0, html(info, self.context)
1676b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        except:                         # just in case something goes wrong
1686b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            import traceback
16983205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            text, doc = 1, ''.join(traceback.format_exception(*info))
1706b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1716b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if self.display:
1726b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            if text:
1736b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                doc = doc.replace('&', '&amp;').replace('<', '&lt;')
17483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                print '<pre>' + doc + '</pre>'
1756b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            else:
1766b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                print doc
1776b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        else:
1786b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            print '<p>A problem occurred in a Python script.'
1796b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1806b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee        if self.logdir is not None:
18183205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee            import os, tempfile
1826b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            name = tempfile.mktemp(['.html', '.txt'][text])
1836b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            path = os.path.join(self.logdir, os.path.basename(name))
1846b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            try:
1856b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                file = open(path, 'w')
1866b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                file.write(doc)
1876b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee                file.close()
18883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                print '<p> %s contains the description of this error.' % path
1896b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee            except:
19083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee                print '<p> Tried to save traceback to %s, but failed.' % path
1916b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee
1926b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yeehandler = Hook().handle
19383205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yeedef enable(display=1, logdir=None, context=5):
19483205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    """Install an exception handler that formats tracebacks as HTML.
19583205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee
19683205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    The optional argument 'display' can be set to 0 to suppress sending the
19783205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    traceback to the browser, and 'logdir' can be set to a directory to cause
19883205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    tracebacks to be written to files there."""
1996b5a48d48ea15c1364b2fbc8552f50ebf72fa64aKa-Ping Yee    import sys
20083205972a2d027517ebedd827d1c19a5b0264c0aKa-Ping Yee    sys.excepthook = Hook(display, logdir, context)
201