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.
37 """Return a string that resets the CGI and browser to a known state."""
39 Content-Type: text/html
41 <body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> -->
42 <body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> --> -->
43 </font> </font> </font> </script> </object> </blockquote> </pre>
44 </table> </table> </table> </table> </table> </font> </font> </font>'''
46 __UNDEF__
= [] # a special sentinel object
49 return '<small>' + text
+ '</small>'
55 return '<strong>' + text
+ '</strong>'
61 return '<font color="#909090">' + text
+ '</font>'
65 def lookup(name
, frame
, locals):
66 """Find the value for a given name in the given environment."""
68 return 'local', locals[name
]
69 if name
in frame
.f_globals
:
70 return 'global', frame
.f_globals
[name
]
71 if '__builtins__' in frame
.f_globals
:
72 builtins
= frame
.f_globals
['__builtins__']
73 if type(builtins
) is type({}):
75 return 'builtin', builtins
[name
]
77 if hasattr(builtins
, name
):
78 return 'builtin', getattr(builtins
, name
)
79 return None, __UNDEF__
81 def scanvars(reader
, frame
, locals):
82 """Scan one logical line of Python and look up values of variables used."""
83 vars, lasttoken
, parent
, prefix
, value
= [], None, None, '', __UNDEF__
84 for ttype
, token
, start
, end
, line
in tokenize
.generate_tokens(reader
):
85 if ttype
== tokenize
.NEWLINE
: break
86 if ttype
== tokenize
.NAME
and token
not in keyword
.kwlist
:
88 if parent
is not __UNDEF__
:
89 value
= getattr(parent
, token
, __UNDEF__
)
90 vars.append((prefix
+ token
, prefix
, value
))
92 where
, value
= lookup(token
, frame
, locals)
93 vars.append((token
, where
, value
))
95 prefix
+= lasttoken
+ '.'
98 parent
, prefix
= None, ''
102 def html(einfo
, context
=5):
103 """Return a nice HTML document describing a given traceback."""
104 etype
, evalue
, etb
= einfo
105 if type(etype
) is types
.ClassType
:
106 etype
= etype
.__name
__
107 pyver
= 'Python ' + sys
.version
.split()[0] + ': ' + sys
.executable
108 date
= time
.ctime(time
.time())
109 head
= '<body bgcolor="#f0f0f8">' + pydoc
.html
.heading(
110 '<big><big>%s</big></big>' %
111 strong(pydoc
.html
.escape(str(etype
))),
112 '#ffffff', '#6622aa', pyver
+ '<br>' + date
) + '''
113 <p>A problem occurred in a Python script. Here is the sequence of
114 function calls leading up to the error, in the order they occurred.</p>'''
116 indent
= '<tt>' + small(' ' * 5) + ' </tt>'
118 records
= inspect
.getinnerframes(etb
, context
)
119 for frame
, file, lnum
, func
, lines
, index
in records
:
121 file = os
.path
.abspath(file)
122 link
= '<a href="file://%s">%s</a>' % (file, pydoc
.html
.escape(file))
125 args
, varargs
, varkw
, locals = inspect
.getargvalues(frame
)
128 call
= 'in ' + strong(func
) + \
129 inspect
.formatargvalues(args
, varargs
, varkw
, locals,
130 formatvalue
=lambda value
: '=' + pydoc
.html
.repr(value
))
133 def reader(lnum
=[lnum
]):
134 highlight
[lnum
[0]] = 1
135 try: return linecache
.getline(file, lnum
[0])
136 finally: lnum
[0] += 1
137 vars = scanvars(reader
, frame
, locals)
139 rows
= ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
140 ('<big> </big>', link
, call
)]
141 if index
is not None:
144 num
= small(' ' * (5-len(str(i
))) + str(i
)) + ' '
146 line
= '<tt>=>%s%s</tt>' % (num
, pydoc
.html
.preformat(line
))
147 rows
.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line
)
149 line
= '<tt> %s%s</tt>' % (num
, pydoc
.html
.preformat(line
))
150 rows
.append('<tr><td>%s</td></tr>' % grey(line
))
154 for name
, where
, value
in vars:
155 if name
in done
: continue
157 if value
is not __UNDEF__
:
158 if where
in ('global', 'builtin'):
159 name
= ('<em>%s</em> ' % where
) + strong(name
)
160 elif where
== 'local':
163 name
= where
+ strong(name
.split('.')[-1])
164 dump
.append('%s = %s' % (name
, pydoc
.html
.repr(value
)))
166 dump
.append(name
+ ' <em>undefined</em>')
168 rows
.append('<tr><td>%s</td></tr>' % small(grey(', '.join(dump
))))
170 <table width="100%%" cellspacing=0 cellpadding=0 border=0>
171 %s</table>''' % '\n'.join(rows
))
173 exception
= ['<p>%s: %s' % (strong(pydoc
.html
.escape(str(etype
))),
174 pydoc
.html
.escape(str(evalue
)))]
175 if isinstance(evalue
, BaseException
):
176 for name
in dir(evalue
):
177 if name
[:1] == '_': continue
178 value
= pydoc
.html
.repr(getattr(evalue
, name
))
179 exception
.append('\n<br>%s%s =\n%s' % (indent
, name
, value
))
181 return head
+ ''.join(frames
) + ''.join(exception
) + '''
184 <!-- The above is a description of an error in a Python program, formatted
185 for a Web browser because the 'cgitb' module was enabled. In case you
186 are not reading this in a Web browser, here is the original traceback:
190 ''' % pydoc
.html
.escape(
191 ''.join(traceback
.format_exception(etype
, evalue
, etb
)))
193 def text(einfo
, context
=5):
194 """Return a plain text document describing a given traceback."""
195 etype
, evalue
, etb
= einfo
196 if type(etype
) is types
.ClassType
:
197 etype
= etype
.__name
__
198 pyver
= 'Python ' + sys
.version
.split()[0] + ': ' + sys
.executable
199 date
= time
.ctime(time
.time())
200 head
= "%s\n%s\n%s\n" % (str(etype
), pyver
, date
) + '''
201 A problem occurred in a Python script. Here is the sequence of
202 function calls leading up to the error, in the order they occurred.
206 records
= inspect
.getinnerframes(etb
, context
)
207 for frame
, file, lnum
, func
, lines
, index
in records
:
208 file = file and os
.path
.abspath(file) or '?'
209 args
, varargs
, varkw
, locals = inspect
.getargvalues(frame
)
212 call
= 'in ' + func
+ \
213 inspect
.formatargvalues(args
, varargs
, varkw
, locals,
214 formatvalue
=lambda value
: '=' + pydoc
.text
.repr(value
))
217 def reader(lnum
=[lnum
]):
218 highlight
[lnum
[0]] = 1
219 try: return linecache
.getline(file, lnum
[0])
220 finally: lnum
[0] += 1
221 vars = scanvars(reader
, frame
, locals)
223 rows
= [' %s %s' % (file, call
)]
224 if index
is not None:
228 rows
.append(num
+line
.rstrip())
232 for name
, where
, value
in vars:
233 if name
in done
: continue
235 if value
is not __UNDEF__
:
236 if where
== 'global': name
= 'global ' + name
237 elif where
!= 'local': name
= where
+ name
.split('.')[-1]
238 dump
.append('%s = %s' % (name
, pydoc
.text
.repr(value
)))
240 dump
.append(name
+ ' undefined')
242 rows
.append('\n'.join(dump
))
243 frames
.append('\n%s\n' % '\n'.join(rows
))
245 exception
= ['%s: %s' % (str(etype
), str(evalue
))]
246 if isinstance(evalue
, BaseException
):
247 for name
in dir(evalue
):
248 value
= pydoc
.text
.repr(getattr(evalue
, name
))
249 exception
.append('\n%s%s = %s' % (" "*4, name
, value
))
251 return head
+ ''.join(frames
) + ''.join(exception
) + '''
253 The above is a description of an error in a Python program. Here is
254 the original traceback:
257 ''' % ''.join(traceback
.format_exception(etype
, evalue
, etb
))
260 """A hook to replace sys.excepthook that shows tracebacks in HTML."""
262 def __init__(self
, display
=1, logdir
=None, context
=5, file=None,
264 self
.display
= display
# send tracebacks to browser if true
265 self
.logdir
= logdir
# log tracebacks to files if not None
266 self
.context
= context
# number of source code lines per frame
267 self
.file = file or sys
.stdout
# place to send the output
270 def __call__(self
, etype
, evalue
, etb
):
271 self
.handle((etype
, evalue
, etb
))
273 def handle(self
, info
=None):
274 info
= info
or sys
.exc_info()
275 if self
.format
== "html":
276 self
.file.write(reset())
278 formatter
= (self
.format
=="html") and html
or text
281 doc
= formatter(info
, self
.context
)
282 except: # just in case something goes wrong
283 doc
= ''.join(traceback
.format_exception(*info
))
288 doc
= doc
.replace('&', '&').replace('<', '<')
289 self
.file.write('<pre>' + doc
+ '</pre>\n')
291 self
.file.write(doc
+ '\n')
293 self
.file.write('<p>A problem occurred in a Python script.\n')
295 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
)