Commit a00b7e89 authored by Skip Montanaro's avatar Skip Montanaro

SF Patch 569574 - enhancements to cgitb for plain text display

parent 0c88e1c4
...@@ -12,9 +12,12 @@ ...@@ -12,9 +12,12 @@
\index{exceptions!in CGI scripts} \index{exceptions!in CGI scripts}
\index{tracebacks!in CGI scripts} \index{tracebacks!in CGI scripts}
The \module{cgitb} module provides a special exception handler for CGI The \module{cgitb} module provides a special exception handler for Python
scripts. After this module is activated, if an uncaught exception occurs, scripts. (It's name is a bit misleading. It was originally designed to
a detailed, formatted report will be sent to the Web browser. The report display extensive traceback information in HTML for CGI scripts. It was
later generalized to also display this information in plain text.) After
this module is activated, if an uncaught exception occurs, a detailed,
formatted report will be displayed. The report
includes a traceback showing excerpts of the source code for each level, includes a traceback showing excerpts of the source code for each level,
as well as the values of the arguments and local variables to currently as well as the values of the arguments and local variables to currently
running functions, to help you debug the problem. Optionally, you can running functions, to help you debug the problem. Optionally, you can
...@@ -32,7 +35,7 @@ to a file for later analysis. ...@@ -32,7 +35,7 @@ to a file for later analysis.
\begin{funcdesc}{enable}{\optional{display\optional{, logdir\optional{, \begin{funcdesc}{enable}{\optional{display\optional{, logdir\optional{,
context}}}} context\optional{, format}}}}}
This function causes the \module{cgitb} module to take over the This function causes the \module{cgitb} module to take over the
interpreter's default handling for exceptions by setting the interpreter's default handling for exceptions by setting the
value of \code{\refmodule{sys}.excepthook}. value of \code{\refmodule{sys}.excepthook}.
...@@ -46,6 +49,9 @@ to a file for later analysis. ...@@ -46,6 +49,9 @@ to a file for later analysis.
The optional argument \var{context} is the number of lines of The optional argument \var{context} is the number of lines of
context to display around the current line of source code in the context to display around the current line of source code in the
traceback; this defaults to \code{5}. traceback; this defaults to \code{5}.
If the optional argument \var{format} is \code{"html"}, the output is
formatted as HTML. Any other value forces plain text output. The default
value is \code{"html"}.
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{handler}{\optional{info}} \begin{funcdesc}{handler}{\optional{info}}
......
"""Handle exceptions in CGI scripts by formatting tracebacks into nice HTML. """More comprehensive traceback formatting for Python scripts.
To enable this module, do: To enable this module, do:
import cgitb; cgitb.enable() import cgitb; cgitb.enable()
at the top of your CGI script. The optional arguments to enable() are: at the top of your script. The optional arguments to enable() are:
display - if true, tracebacks are displayed in the web browser display - if true, tracebacks are displayed in the web browser
logdir - if set, tracebacks are written to files in this directory logdir - if set, tracebacks are written to files in this directory
context - number of lines of source code to show for each stack frame context - number of lines of source code to show for each stack frame
format - 'text' or 'html' controls the output format
By default, tracebacks are displayed but not saved, and context is 5. By default, tracebacks are displayed but not saved, the context is 5 lines
and the output format is 'html' (for backwards compatibility with the
original use of this module)
Alternatively, if you have caught an exception and want cgitb to display it Alternatively, if you have caught an exception and want cgitb to display it
for you, call cgitb.handler(). The optional argument to handler() is a 3-item for you, call cgitb.handler(). The optional argument to handler() is a
tuple (etype, evalue, etb) just like the value of sys.exc_info().""" 3-item tuple (etype, evalue, etb) just like the value of sys.exc_info().
The default handler displays output as HTML.
"""
__author__ = 'Ka-Ping Yee' __author__ = 'Ka-Ping Yee'
__version__ = '$Revision$' __version__ = '$Revision$'
...@@ -160,30 +165,105 @@ function calls leading up to the error, in the order they occurred.''' ...@@ -160,30 +165,105 @@ function calls leading up to the error, in the order they occurred.'''
--> -->
''' % ''.join(traceback.format_exception(etype, evalue, etb)) ''' % ''.join(traceback.format_exception(etype, evalue, etb))
def text((etype, evalue, etb), context=5):
"""Return a plain text document describing a given traceback."""
import os, types, time, traceback, linecache, inspect, pydoc
if type(etype) is types.ClassType:
etype = etype.__name__
pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
date = time.ctime(time.time())
head = "%s\n%s\n%s\n" % (str(etype), pyver, date) + '''
A problem occurred in a Python script. Here is the sequence of
function calls leading up to the error, in the order they occurred.
'''
frames = []
records = inspect.getinnerframes(etb, context)
for frame, file, lnum, func, lines, index in records:
file = file and os.path.abspath(file) or '?'
args, varargs, varkw, locals = inspect.getargvalues(frame)
call = ''
if func != '?':
call = 'in ' + func + \
inspect.formatargvalues(args, varargs, varkw, locals,
formatvalue=lambda value: '=' + pydoc.text.repr(value))
highlight = {}
def reader(lnum=[lnum]):
highlight[lnum[0]] = 1
try: return linecache.getline(file, lnum[0])
finally: lnum[0] += 1
vars = scanvars(reader, frame, locals)
rows = [' %s %s' % (file, call)]
if index is not None:
i = lnum - index
for line in lines:
num = '%5d ' % i
rows.append(num+line.rstrip())
i += 1
done, dump = {}, []
for name, where, value in vars:
if name in done: continue
done[name] = 1
if value is not __UNDEF__:
if where == 'global': name = 'global ' + name
elif where == 'local': name = name
else: name = where + name.split('.')[-1]
dump.append('%s = %s' % (name, pydoc.text.repr(value)))
else:
dump.append(name + ' undefined')
rows.append('\n'.join(dump))
frames.append('\n%s\n' % '\n'.join(rows))
exception = ['%s: %s' % (str(etype), str(evalue))]
if type(evalue) is types.InstanceType:
for name in dir(evalue):
value = pydoc.text.repr(getattr(evalue, name))
exception.append('\n%s%s = %s' % (" "*4, name, value))
import traceback
return head + ''.join(frames) + ''.join(exception) + '''
The above is a description of an error in a Python program. Here is
the original traceback:
%s
''' % ''.join(traceback.format_exception(etype, evalue, etb))
class Hook: class Hook:
"""A hook to replace sys.excepthook that shows tracebacks in HTML.""" """A hook to replace sys.excepthook that shows tracebacks in HTML."""
def __init__(self, display=1, logdir=None, context=5, file=None): def __init__(self, display=1, logdir=None, context=5, file=None,
format="html"):
self.display = display # send tracebacks to browser if true self.display = display # send tracebacks to browser if true
self.logdir = logdir # log tracebacks to files if not None self.logdir = logdir # log tracebacks to files if not None
self.context = context # number of source code lines per frame self.context = context # number of source code lines per frame
self.file = file or sys.stdout # place to send the output self.file = file or sys.stdout # place to send the output
self.format = format
def __call__(self, etype, evalue, etb): def __call__(self, etype, evalue, etb):
self.handle((etype, evalue, etb)) self.handle((etype, evalue, etb))
def handle(self, info=None): def handle(self, info=None):
info = info or sys.exc_info() info = info or sys.exc_info()
self.file.write(reset()) if self.format == "html":
self.file.write(reset())
formatter = (self.format=="html") and html or text
plain = False
try: try:
text, doc = 0, html(info, self.context) doc = formatter(info, self.context)
except: # just in case something goes wrong except: # just in case something goes wrong
import traceback import traceback
text, doc = 1, ''.join(traceback.format_exception(*info)) doc = ''.join(traceback.format_exception(*info))
plain = True
if self.display: if self.display:
if text: if plain:
doc = doc.replace('&', '&amp;').replace('<', '&lt;') doc = doc.replace('&', '&amp;').replace('<', '&lt;')
self.file.write('<pre>' + doc + '</pre>\n') self.file.write('<pre>' + doc + '</pre>\n')
else: else:
...@@ -193,8 +273,8 @@ class Hook: ...@@ -193,8 +273,8 @@ class Hook:
if self.logdir is not None: if self.logdir is not None:
import os, tempfile import os, tempfile
(fd, path) = tempfile.mkstemp(suffix=['.html', '.txt'][text], suffix = ['.html', '.txt'][self.format=="html"]
dir=self.logdir) (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
try: try:
file = os.fdopen(fd, 'w') file = os.fdopen(fd, 'w')
file.write(doc) file.write(doc)
...@@ -208,10 +288,11 @@ class Hook: ...@@ -208,10 +288,11 @@ class Hook:
except: pass except: pass
handler = Hook().handle handler = Hook().handle
def enable(display=1, logdir=None, context=5): def enable(display=1, logdir=None, context=5, format="html"):
"""Install an exception handler that formats tracebacks as HTML. """Install an exception handler that formats tracebacks as HTML.
The optional argument 'display' can be set to 0 to suppress sending the The optional argument 'display' can be set to 0 to suppress sending the
traceback to the browser, and 'logdir' can be set to a directory to cause traceback to the browser, and 'logdir' can be set to a directory to cause
tracebacks to be written to files there.""" tracebacks to be written to files there."""
sys.excepthook = Hook(display, logdir, context) sys.excepthook = Hook(display=display, logdir=logdir,
context=context, format=format)
...@@ -83,6 +83,9 @@ Extension modules ...@@ -83,6 +83,9 @@ Extension modules
Library Library
------- -------
- The cgitb module has been extended to support plain text display (SF patch
569574).
- A brand new version of IDLE (from the IDLEfork project at - A brand new version of IDLE (from the IDLEfork project at
SourceForge) is now included as Lib/idlelib. The old Tools/idle is SourceForge) is now included as Lib/idlelib. The old Tools/idle is
no more. no more.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment