1
"""More comprehensive traceback formatting for Python scripts.
3
To enable this module, do:
5
import cgitb; cgitb.enable()
7
at the top of your script. The optional arguments to enable() are:
9
display - if true, tracebacks are displayed in the web browser
10
logdir - if set, tracebacks are written to files in this directory
11
context - number of lines of source code to show for each stack frame
12
format - 'text' or 'html' controls the output format
14
By default, tracebacks are displayed but not saved, the context is 5 lines
15
and the output format is 'html' (for backwards compatibility with the
16
original use of this module)
18
Alternatively, if you have caught an exception and want cgitb to display it
19
for you, call cgitb.handler(). The optional argument to handler() is a
20
3-item tuple (etype, evalue, etb) just like the value of sys.exc_info().
21
The default handler displays output as HTML.
24
__author__ = 'Ka-Ping Yee'
26
__version__ = '$Revision: 55818 $'
31
"""Return a string that resets the CGI and browser to a known state."""
33
Content-Type: text/html
35
<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> -->
36
<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> --> -->
37
</font> </font> </font> </script> </object> </blockquote> </pre>
38
</table> </table> </table> </table> </table> </font> </font> </font>'''
40
__UNDEF__ = [] # a special sentinel object
43
return '<small>' + text + '</small>'
49
return '<strong>' + text + '</strong>'
55
return '<font color="#909090">' + text + '</font>'
59
def lookup(name, frame, locals):
60
"""Find the value for a given name in the given environment."""
62
return 'local', locals[name]
63
if name in frame.f_globals:
64
return 'global', frame.f_globals[name]
65
if '__builtins__' in frame.f_globals:
66
builtins = frame.f_globals['__builtins__']
67
if type(builtins) is type({}):
69
return 'builtin', builtins[name]
71
if hasattr(builtins, name):
72
return 'builtin', getattr(builtins, name)
73
return None, __UNDEF__
75
def scanvars(reader, frame, locals):
76
"""Scan one logical line of Python and look up values of variables used."""
77
import tokenize, keyword
78
vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__
79
for ttype, token, start, end, line in tokenize.generate_tokens(reader):
80
if ttype == tokenize.NEWLINE: break
81
if ttype == tokenize.NAME and token not in keyword.kwlist:
83
if parent is not __UNDEF__:
84
value = getattr(parent, token, __UNDEF__)
85
vars.append((prefix + token, prefix, value))
87
where, value = lookup(token, frame, locals)
88
vars.append((token, where, value))
90
prefix += lasttoken + '.'
93
parent, prefix = None, ''
97
def html(einfo, context=5):
98
"""Return a nice HTML document describing a given traceback."""
99
import os, time, traceback, linecache, inspect, pydoc
101
etype, evalue, etb = einfo
102
if isinstance(etype, type):
103
etype = etype.__name__
104
pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
105
date = time.ctime(time.time())
106
head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading(
107
'<big><big>%s</big></big>' %
108
strong(pydoc.html.escape(str(etype))),
109
'#ffffff', '#6622aa', pyver + '<br>' + date) + '''
110
<p>A problem occurred in a Python script. Here is the sequence of
111
function calls leading up to the error, in the order they occurred.</p>'''
113
indent = '<tt>' + small(' ' * 5) + ' </tt>'
115
records = inspect.getinnerframes(etb, context)
116
for frame, file, lnum, func, lines, index in records:
118
file = os.path.abspath(file)
119
link = '<a href="file://%s">%s</a>' % (file, pydoc.html.escape(file))
122
args, varargs, varkw, locals = inspect.getargvalues(frame)
125
call = 'in ' + strong(func) + \
126
inspect.formatargvalues(args, varargs, varkw, locals,
127
formatvalue=lambda value: '=' + pydoc.html.repr(value))
130
def reader(lnum=[lnum]):
131
highlight[lnum[0]] = 1
132
try: return linecache.getline(file, lnum[0])
133
finally: lnum[0] += 1
134
vars = scanvars(reader, frame, locals)
136
rows = ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
137
('<big> </big>', link, call)]
138
if index is not None:
141
num = small(' ' * (5-len(str(i))) + str(i)) + ' '
142
line = '<tt>%s%s</tt>' % (num, pydoc.html.preformat(line))
144
rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line)
146
rows.append('<tr><td>%s</td></tr>' % grey(line))
150
for name, where, value in vars:
151
if name in done: continue
153
if value is not __UNDEF__:
154
if where in ('global', 'builtin'):
155
name = ('<em>%s</em> ' % where) + strong(name)
156
elif where == 'local':
159
name = where + strong(name.split('.')[-1])
160
dump.append('%s = %s' % (name, pydoc.html.repr(value)))
162
dump.append(name + ' <em>undefined</em>')
164
rows.append('<tr><td>%s</td></tr>' % small(grey(', '.join(dump))))
166
<table width="100%%" cellspacing=0 cellpadding=0 border=0>
167
%s</table>''' % '\n'.join(rows))
169
exception = ['<p>%s: %s' % (strong(pydoc.html.escape(str(etype))),
170
pydoc.html.escape(str(evalue)))]
171
for name in dir(evalue):
172
if name[:1] == '_': continue
173
value = pydoc.html.repr(getattr(evalue, name))
174
exception.append('\n<br>%s%s =\n%s' % (indent, name, value))
177
return head + ''.join(frames) + ''.join(exception) + '''
180
<!-- The above is a description of an error in a Python program, formatted
181
for a Web browser because the 'cgitb' module was enabled. In case you
182
are not reading this in a Web browser, here is the original traceback:
186
''' % pydoc.html.escape(
187
''.join(traceback.format_exception(etype, evalue, etb)))
189
def text(einfo, context=5):
190
"""Return a plain text document describing a given traceback."""
191
import os, time, traceback, linecache, inspect, pydoc
193
etype, evalue, etb = einfo
194
if isinstance(etype, type):
195
etype = etype.__name__
196
pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
197
date = time.ctime(time.time())
198
head = "%s\n%s\n%s\n" % (str(etype), pyver, date) + '''
199
A problem occurred in a Python script. Here is the sequence of
200
function calls leading up to the error, in the order they occurred.
204
records = inspect.getinnerframes(etb, context)
205
for frame, file, lnum, func, lines, index in records:
206
file = file and os.path.abspath(file) or '?'
207
args, varargs, varkw, locals = inspect.getargvalues(frame)
210
call = 'in ' + func + \
211
inspect.formatargvalues(args, varargs, varkw, locals,
212
formatvalue=lambda value: '=' + pydoc.text.repr(value))
215
def reader(lnum=[lnum]):
216
highlight[lnum[0]] = 1
217
try: return linecache.getline(file, lnum[0])
218
finally: lnum[0] += 1
219
vars = scanvars(reader, frame, locals)
221
rows = [' %s %s' % (file, call)]
222
if index is not None:
226
rows.append(num+line.rstrip())
230
for name, where, value in vars:
231
if name in done: continue
233
if value is not __UNDEF__:
234
if where == 'global': name = 'global ' + name
235
elif where != 'local': name = where + name.split('.')[-1]
236
dump.append('%s = %s' % (name, pydoc.text.repr(value)))
238
dump.append(name + ' undefined')
240
rows.append('\n'.join(dump))
241
frames.append('\n%s\n' % '\n'.join(rows))
243
exception = ['%s: %s' % (str(etype), str(evalue))]
244
for name in dir(evalue):
245
value = pydoc.text.repr(getattr(evalue, name))
246
exception.append('\n%s%s = %s' % (" "*4, name, value))
249
return head + ''.join(frames) + ''.join(exception) + '''
251
The above is a description of an error in a Python program. Here is
252
the original traceback:
255
''' % ''.join(traceback.format_exception(etype, evalue, etb))
258
"""A hook to replace sys.excepthook that shows tracebacks in HTML."""
260
def __init__(self, display=1, logdir=None, context=5, file=None,
262
self.display = display # send tracebacks to browser if true
263
self.logdir = logdir # log tracebacks to files if not None
264
self.context = context # number of source code lines per frame
265
self.file = file or sys.stdout # place to send the output
268
def __call__(self, etype, evalue, etb):
269
self.handle((etype, evalue, etb))
271
def handle(self, info=None):
272
info = info or sys.exc_info()
273
if self.format == "html":
274
self.file.write(reset())
276
formatter = (self.format=="html") and html or text
279
doc = formatter(info, self.context)
280
except: # just in case something goes wrong
282
doc = ''.join(traceback.format_exception(*info))
287
doc = doc.replace('&', '&').replace('<', '<')
288
self.file.write('<pre>' + doc + '</pre>\n')
290
self.file.write(doc + '\n')
292
self.file.write('<p>A problem occurred in a Python script.\n')
294
if self.logdir is not None:
296
suffix = ['.txt', '.html'][self.format=="html"]
297
(fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
299
file = os.fdopen(fd, 'w')
302
msg = '<p> %s contains the description of this error.' % path
304
msg = '<p> Tried to save traceback to %s, but failed.' % path
305
self.file.write(msg + '\n')
310
handler = Hook().handle
311
def enable(display=1, logdir=None, context=5, format="html"):
312
"""Install an exception handler that formats tracebacks as HTML.
314
The optional argument 'display' can be set to 0 to suppress sending the
315
traceback to the browser, and 'logdir' can be set to a directory to cause
316
tracebacks to be written to files there."""
317
sys.excepthook = Hook(display=display, logdir=logdir,
318
context=context, format=format)