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 13 viewClass - sub class of View. Create this if you want to customize the 14 layout of the traceback. 15 debug - may be used by viewClass to decide on level of detail 17 By default, tracebacks are displayed but not saved, the context is 5 lines 18 and the output format is 'html' (for backwards compatibility with the 19 original use of this module). 21 Alternatively, if you have caught an exception and want cgitb to display it 22 for you, call cgitb.handler(). The optional argument to handler() is a 23 3-item tuple (etype, evalue, etb) just like the value of sys.exc_info(). 24 The default handler displays output as HTML. 27 2005-04-22 Nir Soffer <nirs@freeshell.org> 30 - Refactor html and text functions to View class, HTMLFormatter and 31 TextFormatter. No more duplicate formating code. 32 - Layout is done with minimal html and css, in a way it can't be 33 affected by surrounding code. 34 - Built to be easy to subclass and modify without duplicating code. 35 - Change layout, important details come first. 36 - Factor frame analyzing and formatting into separate class. 37 - Add debug argument, can be used to change error display e.g. user 38 error view, developer error view. 39 - Add viewClass argument, make it easy to customize the traceback view. 40 - Easy to customize system details and application details. 42 The main goal of this rewrite was to have a traceback that can render 43 few tracebacks combined. It's needed when you wrap an expection and want 44 to print both the traceback up to the wrapper exception, and the 45 original traceback. There is no code to support this here, but it's easy 46 to add by using your own View sub class. 49 __author__ =
'Ka-Ping Yee' 50 __version__ =
'$Revision: 1.10 $' 52 import sys, os, pydoc, inspect, linecache, tokenize, keyword
56 """ Reset the CGI and the browser 58 Return a string that resets the CGI and browser to a known state. 59 TODO: probably some of this is not needed any more. 62 Content-Type: text/html 64 <body><font style="color: white; font-size: 1px"> --> 65 <body><font style="color: white; font-size: 1px"> --> --> 66 </font> </font> </font> </script> </object> </blockquote> </pre> 67 </table> </table> </table> </table> </table> </font> </font> </font> 79 """ Minimal html formatter """ 83 result = [
' %s="%s"' % (k, v)
for k, v
in attributes.items()]
84 return ''.join(result)
87 def tag(self, name, text, attributes=None):
88 return '<%s%s>%s</%s>\n' % (name, self.
attributes(attributes), text, name)
91 return self.
tag(
'div', text, attributes)
93 def title(self, text, attributes=None):
94 return self.
tag(
'h1', text, attributes)
97 return self.
tag(
'h2', text, attributes)
100 return self.
tag(
'h3', text, attributes)
103 return self.
tag(
'p', text, attributes)
105 def list(self, items, attributes=None):
106 return self.
formatList(
'ul', items, attributes)
109 return self.
formatList(
'ol', items, attributes)
112 """ Send list of raw texts or formatted items. """ 113 if isinstance(items, (list, tuple)):
114 items =
'\n' +
''.join([self.
listItem(i)
for i
in items])
115 return self.
tag(name, items, attributes)
118 return self.
tag(
'li', text, attributes)
120 def link(self, href, text, attributes=None):
121 if attributes
is None:
123 attributes[
'href'] = href
124 return self.
tag(
'a', text, attributes)
127 return self.
tag(
'strong', text, attributes)
129 def em(self, text, attributes=None):
130 return self.
tag(
'em', text, attributes)
133 return pydoc.html.repr(object)
137 """ Plain text formatter """ 142 def title(self, text, attributes=None):
143 lineBellow =
'=' * len(text)
144 return '%s\n%s\n\n' % (text, lineBellow)
147 lineBellow =
'-' * len(text)
148 return '%s\n%s\n\n' % (text, lineBellow)
151 lineBellow =
'~' * len(text)
152 return '%s\n%s\n\n' % (text, lineBellow)
157 def list(self, items, attributes=None):
158 if isinstance(items, (list, tuple)):
159 items = [
' * %s\n' % i
for i
in items]
160 return ''.join(items) +
'\n' 164 if isinstance(items, (list, tuple)):
166 for i
in range(items):
167 result.append(
' %d. %s\n' % (i, items[i]))
168 return ''.join(result) +
'\n' 172 return ' * %s\n' % text
174 def link(self, href, text, attributes=None):
175 return '[[%s]]' % text
180 def em(self, text, attributes=None):
188 """ Analyze and format single frame in a traceback """ 190 def __init__(self, frame, file, lnum, func, lines, index):
199 """ Return formatted content """ 201 vars, highlight = self.
scan()
205 return ''.join(items)
212 self.formatter.strong(self.
func),
214 return self.formatter.paragraph(call, {
'class':
'call'})
217 """ Return formatted file link """ 220 file = pydoc.html.escape(os.path.abspath(self.
file))
221 return self.formatter.link(
'file://' + file, file)
224 """ Return formated arguments list """ 228 def formatValue(value):
229 return '=' + self.formatter.repr(value)
231 args, varargs, varkw, locals = inspect.getargvalues(self.
frame)
232 return inspect.formatargvalues(args, varargs, varkw, locals,
233 formatvalue=formatValue)
236 """ Return formatted context, next call highlighted """ 237 if self.
index is None:
241 for line
in self.
lines:
242 line =
'%5d %s' % (i, pydoc.html.escape(line))
245 attributes = {
'class':
'highlight'}
246 context.append(self.formatter.listItem(line, attributes))
248 context =
'\n' +
''.join(context) +
'\n' 249 return self.formatter.orderedList(context, {
'class':
'context'})
252 """ Return formatted variables """ 255 for name, where, value
in vars:
259 if value
is __UNDEF__:
260 dump.append(
'%s %s' % (name, self.formatter.em(
'undefined')))
263 return self.formatter.list(dump, {
'class':
'variables'})
266 """ Format variable name and value according to scope """ 267 if where
in [
'global',
'builtin']:
268 name =
'%s %s' % (self.formatter.em(where),
269 self.formatter.strong(name))
270 elif where ==
'local':
271 name = self.formatter.strong(name)
273 name = where + self.formatter.strong(name.split(
'.')[-1])
274 return '%s = %s' % (name, self.formatter.repr(value))
280 """ Scan frame for vars while setting highlight line """ 283 def reader(lnum=[self.lnum]):
284 highlight[lnum[0]] = 1
286 return linecache.getline(self.
file, lnum[0])
291 return vars, highlight
294 """ Lookup variables in one logical Python line """ 295 vars, lasttoken, parent, prefix, value = [],
None,
None,
'', __UNDEF__
296 for ttype, token, start, end, line
in tokenize.generate_tokens(reader):
297 if ttype == tokenize.NEWLINE:
299 if ttype == tokenize.NAME
and token
not in keyword.kwlist:
301 if parent
is not __UNDEF__:
305 value = getattr(parent, token, __UNDEF__)
306 vars.append((prefix + token, prefix, value))
308 where, value = self.
lookup(token)
309 vars.append((token, where, value))
311 prefix += lasttoken +
'.' 314 parent, prefix =
None,
'' 319 """ Return the scope and the value of name """ 322 locals = inspect.getargvalues(self.
frame)[3]
324 scope, value =
'local', locals[name]
325 elif name
in self.frame.f_globals:
326 scope, value =
'global', self.frame.f_globals[name]
327 elif '__builtins__' in self.frame.f_globals:
329 builtins = self.frame.f_globals[
'__builtins__']
330 if isinstance(builtins, dict):
331 value = builtins.get(name, __UNDEF__)
333 value = getattr(builtins, name, __UNDEF__)
339 return name
in self.frame.f_globals.get(
"unsafe_names", ())
342 """ Traceback view """ 347 """ Save starting info or current exception info """ 348 self.
info = info
or sys.exc_info()
354 return formatter.section(self.
formatContent(), {
'class':
'cgitb'})
357 """ General layout - override to change layout """ 366 return ''.join(content)
372 """ Format inline html stylesheet """ 373 return '<style type="text/css">%s</style>' % self.
stylesheet()
376 """ Return stylesheet rules. Override to change rules. 378 The rules are sparated to make it easy to extend. 380 The stylesheet must work even if sorounding code define the same 381 css names, and it must not change the sorounding code look and 382 behavior. This is done by having all content in a .traceback 386 .cgitb {background: #E6EAF0; border: 1px solid #4D6180; direction: ltr;} 387 .cgitb p {margin: 0.5em 0; padding: 5px 10px; text-align: left;} 388 .cgitb ol {margin: 0} 389 .cgitb li {margin: 0.25em 0;} 390 .cgitb h1, .cgitb h2, .cgitb h3 {padding: 5px 10px; margin: 0; background: #4D6180; color: white;} 391 .cgitb h1 {font-size: 1.3em;} 392 .cgitb h2 {font-size: 1em; margin-top: 1em;} 393 .cgitb h3 {font-size: 1em;} 394 .cgitb .frames {margin: 0; padding: 0; color: #606060} 395 .cgitb .frames li {display: block;} 396 .cgitb .call {padding: 5px 10px; background: #A3B4CC; color: black} 397 .cgitb .context {padding: 0; font-family: monospace; } 398 .cgitb .context li {display: block; white-space: pre;} 399 .cgitb .context li.highlight {background: #C0D3F0; color: black} 400 .cgitb .variables {padding: 5px 10px; font-family: monospace;} 401 .cgitb .variables li {display: inline;} 402 .cgitb .variables li:after {content: ", ";} 403 .cgitb .variables li:last-child:after {content: "";} 404 .cgitb .exception {border: 1px solid #4D6180; margin: 10px} 405 .cgitb .exception h3 {background: #4D6180; color: white;} 406 .cgitb .exception p {color: black;} 407 .cgitb .exception ul {padding: 0 10px; font-family: monospace;} 408 .cgitb .exception li {display: block;} 424 """ Return formatted traceback """ 428 """ Format one traceback 430 Separate to enable formatting multiple tracebacks. 432 output = [self.formatter.subTitle(
'Traceback'),
435 {
'class':
'frames'}),
437 {
'class':
'exception'}), ]
438 return self.formatter.section(
''.join(output), {
'class':
'traceback'})
443 for record
in inspect.getinnerframes(traceback, self.
context):
445 frames.append(frame.format(self.
formatter))
450 return '''A problem occurred in a Python script. Here is the 451 sequence of function calls leading up to the error, in the 452 order they occurred.''' 461 return ''.join(items)
472 value = self.formatter.repr(value)
473 attribtues.append(
'%s = %s' % (name, value))
474 return self.formatter.list(attribtues)
477 """ Return list of tuples [(name, value), ...] """ 480 for name
in dir(instance):
481 if name.startswith(
'_'):
483 value = getattr(instance, name)
484 attribtues.append((name, value))
489 return getattr(type,
'__name__', str(type))
493 return pydoc.html.escape(str(instance))
500 details = [
'Date: %s' % self.
date(),
502 'Python: %s' % self.
python(), ]
504 return (self.formatter.subTitle(
'System Details') +
505 self.formatter.list(details, {
'class':
'system'}))
509 rfc2822Date = time.strftime(
"%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
514 return pydoc.html.escape(
' '.join(os.uname()))
516 return pydoc.html.escape(
'%s (%s)' % (sys.platform, os.name))
519 return 'Python %s (%s)' % (sys.version.split()[0], sys.executable)
522 """ Override for your application """ 533 """ Separate to enable formatting multiple tracebacks. """ 535 return pydoc.html.escape(
''.join(traceback.format_exception(*info)))
540 <!-- The above is a description of an error in a Python program, 541 formatted for a Web browser. In case you are not reading this 542 in a Web browser, here is the original traceback: 550 """A hook to replace sys.excepthook that shows tracebacks in HTML.""" 552 def __init__(self, display=1, logdir=None, context=5, file=None,
553 format=
"html", viewClass=View, debug=0):
563 self.
handle((etype, evalue, etb))
566 info = info
or sys.exc_info()
567 if self.format.lower() ==
"html":
569 self.file.write(
reset())
576 doc = view.format(formatter, self.
context)
580 doc =
''.join(traceback.format_exception(*info))
585 doc = doc.replace(
'&',
'&').replace(
'<',
'<')
586 self.file.write(
'<pre>' + doc +
'</pre>\n')
588 self.file.write(doc +
'\n')
590 self.file.write(
'<p>A problem occurred in a Python script.\n')
592 if self.
logdir is not None:
594 suffix = [
'.txt',
'.html'][self.
format ==
"html"]
595 (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.
logdir)
597 file = os.fdopen(fd,
'w')
600 msg =
'<p> %s contains the description of this error.' % path
602 msg =
'<p> Tried to save traceback to %s, but failed.' % path
603 self.file.write(msg +
'\n')
611 def enable(display=1, logdir=None, context=5, format="html", viewClass=View, debug=0):
612 """Install an exception handler that formats tracebacks as HTML. 614 The optional argument 'display' can be set to 0 to suppress sending the 615 traceback to the browser, and 'logdir' can be set to a directory to cause 616 tracebacks to be written to files there.""" 617 sys.excepthook =
Hook(display=display, logdir=logdir, context=context,
618 format=format, viewClass=viewClass, debug=debug)
def formatTraceback(self)
def applicationDetails(self)
def exceptionMessage(self, info)
def formatNameValue(self, name, where, value)
def format(self, formatter)
def formatExceptionAttributes(self, info)
def formatSystemDetails(self)
def listItem(self, text, attributes=None)
def __init__(self, info=None, debug=0)
def subSubTitle(self, text, attributes=None)
def link(self, href, text, attributes=None)
def tracebackText(self, info)
def handle(self, info=None)
def list(self, items, attributes=None)
def exceptionAttributes(self, info)
def strong(self, text, attributes=None)
def scanVariables(self, reader)
def paragraph(self, text, attributes=None)
def section(self, text, attributes=None)
def formatVariables(self, vars)
def formatOneTraceback(self, info)
def formatExceptionTitle(self, info)
def enable(display=1, logdir=None, context=5, format="html", viewClass=View, debug=0)
def formatException(self, info)
def __init__(self, frame, file, lnum, func, lines, index)
def __init__(self, display=1, logdir=None, context=5, file=None, format="html", viewClass=View, debug=0)
def textTracebackTemplate(self)
def title(self, text, attributes=None)
def formatStylesheet(self)
def formatContext(self, highlight)
def orderedList(self, items, attributes=None)
def exceptionTitle(self, info)
def unsafe_name(self, name)
def formatTextTraceback(self)
def __call__(self, etype, evalue, etb)
def format(self, formatter, context=5)
def formatExceptionMessage(self, info)
def em(self, text, attributes=None)
def subTitle(self, text, attributes=None)
def formatArguments(self)
def formatOneTextTraceback(self, info)
def tracebackFrames(self, info)