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.
36
"""Return a string that resets the CGI and browser to a known state."""
38
Content-Type: text/html
40
<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> -->
41
<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> --> -->
42
</font> </font> </font> </script> </object> </blockquote> </pre>
43
</table> </table> </table> </table> </table> </font> </font> </font>'''
45
__UNDEF__ = [] # a special sentinel object
48
return '<small>' + text + '</small>'
54
return '<strong>' + text + '</strong>'
60
return '<font color="#909090">' + text + '</font>'
64
def lookup(name, frame, locals):
65
"""Find the value for a given name in the given environment."""
67
return 'local', locals[name]
68
if name in frame.f_globals:
69
return 'global', frame.f_globals[name]
70
if '__builtins__' in frame.f_globals:
71
builtins = frame.f_globals['__builtins__']
72
if type(builtins) is type({}):
74
return 'builtin', builtins[name]
76
if hasattr(builtins, name):
77
return 'builtin', getattr(builtins, name)
78
return None, __UNDEF__
80
def scanvars(reader, frame, locals):
81
"""Scan one logical line of Python and look up values of variables used."""
82
vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__
83
for ttype, token, start, end, line in tokenize.generate_tokens(reader):
84
if ttype == tokenize.NEWLINE: break
85
if ttype == tokenize.NAME and token not in keyword.kwlist:
87
if parent is not __UNDEF__:
88
value = getattr(parent, token, __UNDEF__)
89
vars.append((prefix + token, prefix, value))
91
where, value = lookup(token, frame, locals)
92
vars.append((token, where, value))
94
prefix += lasttoken + '.'
97
parent, prefix = None, ''
101
def html(einfo, context=5):
102
"""Return a nice HTML document describing a given traceback."""
103
etype, evalue, etb = einfo
104
if isinstance(etype, type):
105
etype = etype.__name__
106
pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
107
date = time.ctime(time.time())
108
head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading(
109
'<big><big>%s</big></big>' %
110
strong(pydoc.html.escape(str(etype))),
111
'#ffffff', '#6622aa', pyver + '<br>' + date) + '''
112
<p>A problem occurred in a Python script. Here is the sequence of
113
function calls leading up to the error, in the order they occurred.</p>'''
115
indent = '<tt>' + small(' ' * 5) + ' </tt>'
117
records = inspect.getinnerframes(etb, context)
118
for frame, file, lnum, func, lines, index in records:
120
file = os.path.abspath(file)
121
link = '<a href="file://%s">%s</a>' % (file, pydoc.html.escape(file))
124
args, varargs, varkw, locals = inspect.getargvalues(frame)
127
call = 'in ' + strong(func) + \
128
inspect.formatargvalues(args, varargs, varkw, locals,
129
formatvalue=lambda value: '=' + pydoc.html.repr(value))
132
def reader(lnum=[lnum]):
133
highlight[lnum[0]] = 1
134
try: return linecache.getline(file, lnum[0])
135
finally: lnum[0] += 1
136
vars = scanvars(reader, frame, locals)
138
rows = ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
139
('<big> </big>', link, call)]
140
if index is not None:
143
num = small(' ' * (5-len(str(i))) + str(i)) + ' '
145
line = '<tt>=>%s%s</tt>' % (num, pydoc.html.preformat(line))
146
rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line)
148
line = '<tt> %s%s</tt>' % (num, pydoc.html.preformat(line))
149
rows.append('<tr><td>%s</td></tr>' % grey(line))
153
for name, where, value in vars:
154
if name in done: continue
156
if value is not __UNDEF__:
157
if where in ('global', 'builtin'):
158
name = ('<em>%s</em> ' % where) + strong(name)
159
elif where == 'local':
162
name = where + strong(name.split('.')[-1])
163
dump.append('%s = %s' % (name, pydoc.html.repr(value)))
165
dump.append(name + ' <em>undefined</em>')
167
rows.append('<tr><td>%s</td></tr>' % small(grey(', '.join(dump))))
169
<table width="100%%" cellspacing=0 cellpadding=0 border=0>
170
%s</table>''' % '\n'.join(rows))
172
exception = ['<p>%s: %s' % (strong(pydoc.html.escape(str(etype))),
173
pydoc.html.escape(str(evalue)))]
174
for name in dir(evalue):
175
if name[:1] == '_': continue
176
value = pydoc.html.repr(getattr(evalue, name))
177
exception.append('\n<br>%s%s =\n%s' % (indent, name, value))
179
return head + ''.join(frames) + ''.join(exception) + '''
182
<!-- The above is a description of an error in a Python program, formatted
183
for a Web browser because the 'cgitb' module was enabled. In case you
184
are not reading this in a Web browser, here is the original traceback:
188
''' % pydoc.html.escape(
189
''.join(traceback.format_exception(etype, evalue, etb)))
191
def text(einfo, context=5):
192
"""Return a plain text document describing a given traceback."""
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))
248
return head + ''.join(frames) + ''.join(exception) + '''
250
The above is a description of an error in a Python program. Here is
251
the original traceback:
254
''' % ''.join(traceback.format_exception(etype, evalue, etb))
257
"""A hook to replace sys.excepthook that shows tracebacks in HTML."""
259
def __init__(self, display=1, logdir=None, context=5, file=None,
261
self.display = display # send tracebacks to browser if true
262
self.logdir = logdir # log tracebacks to files if not None
263
self.context = context # number of source code lines per frame
264
self.file = file or sys.stdout # place to send the output
267
def __call__(self, etype, evalue, etb):
268
self.handle((etype, evalue, etb))
270
def handle(self, info=None):
271
info = info or sys.exc_info()
272
if self.format == "html":
273
self.file.write(reset())
275
formatter = (self.format=="html") and html or text
278
doc = formatter(info, self.context)
279
except: # just in case something goes wrong
280
doc = ''.join(traceback.format_exception(*info))
285
doc = doc.replace('&', '&').replace('<', '<')
286
self.file.write('<pre>' + doc + '</pre>\n')
288
self.file.write(doc + '\n')
290
self.file.write('<p>A problem occurred in a Python script.\n')
292
if self.logdir is not None:
293
suffix = ['.txt', '.html'][self.format=="html"]
294
(fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
297
file = os.fdopen(fd, 'w')
300
msg = '%s contains the description of this error.' % path
302
msg = 'Tried to save traceback to %s, but failed.' % path
304
if self.format == 'html':
305
self.file.write('<p>%s</p>\n' % msg)
307
self.file.write(msg + '\n')
312
handler = Hook().handle
313
def enable(display=1, logdir=None, context=5, format="html"):
314
"""Install an exception handler that formats tracebacks as HTML.
316
The optional argument 'display' can be set to 0 to suppress sending the
317
traceback to the browser, and 'logdir' can be set to a directory to cause
318
tracebacks to be written to files there."""
319
sys.excepthook = Hook(display=display, logdir=logdir,
320
context=context, format=format)