summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libcgitb.tex14
-rw-r--r--Lib/cgitb.py109
-rw-r--r--Misc/NEWS3
3 files changed, 108 insertions, 18 deletions
diff --git a/Doc/lib/libcgitb.tex b/Doc/lib/libcgitb.tex
index e0e4a01..dab34c5 100644
--- a/Doc/lib/libcgitb.tex
+++ b/Doc/lib/libcgitb.tex
@@ -12,9 +12,12 @@
\index{exceptions!in CGI scripts}
\index{tracebacks!in CGI scripts}
-The \module{cgitb} module provides a special exception handler for CGI
-scripts. After this module is activated, if an uncaught exception occurs,
-a detailed, formatted report will be sent to the Web browser. The report
+The \module{cgitb} module provides a special exception handler for Python
+scripts. (It's name is a bit misleading. It was originally designed to
+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,
as well as the values of the arguments and local variables to currently
running functions, to help you debug the problem. Optionally, you can
@@ -32,7 +35,7 @@ to a file for later analysis.
\begin{funcdesc}{enable}{\optional{display\optional{, logdir\optional{,
- context}}}}
+ context\optional{, format}}}}}
This function causes the \module{cgitb} module to take over the
interpreter's default handling for exceptions by setting the
value of \code{\refmodule{sys}.excepthook}.
@@ -46,6 +49,9 @@ to a file for later analysis.
The optional argument \var{context} is the number of lines of
context to display around the current line of source code in the
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}
\begin{funcdesc}{handler}{\optional{info}}
diff --git a/Lib/cgitb.py b/Lib/cgitb.py
index 200e8ee..4e0c9fb 100644
--- a/Lib/cgitb.py
+++ b/Lib/cgitb.py
@@ -1,20 +1,25 @@
-"""Handle exceptions in CGI scripts by formatting tracebacks into nice HTML.
+"""More comprehensive traceback formatting for Python scripts.
To enable this module, do:
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
logdir - if set, tracebacks are written to files in this directory
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
-for you, call cgitb.handler(). The optional argument to handler() is a 3-item
-tuple (etype, evalue, etb) just like the value of sys.exc_info()."""
+for you, call cgitb.handler(). The optional argument to handler() is a
+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'
__version__ = '$Revision$'
@@ -160,30 +165,105 @@ function calls leading up to the error, in the order they occurred.'''
-->
''' % ''.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:
"""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.logdir = logdir # log tracebacks to files if not None
self.context = context # number of source code lines per frame
self.file = file or sys.stdout # place to send the output
+ self.format = format
def __call__(self, etype, evalue, etb):
self.handle((etype, evalue, etb))
def handle(self, info=None):
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:
- text, doc = 0, html(info, self.context)
+ doc = formatter(info, self.context)
except: # just in case something goes wrong
import traceback
- text, doc = 1, ''.join(traceback.format_exception(*info))
+ doc = ''.join(traceback.format_exception(*info))
+ plain = True
if self.display:
- if text:
+ if plain:
doc = doc.replace('&', '&amp;').replace('<', '&lt;')
self.file.write('<pre>' + doc + '</pre>\n')
else:
@@ -193,8 +273,8 @@ class Hook:
if self.logdir is not None:
import os, tempfile
- (fd, path) = tempfile.mkstemp(suffix=['.html', '.txt'][text],
- dir=self.logdir)
+ suffix = ['.html', '.txt'][self.format=="html"]
+ (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
try:
file = os.fdopen(fd, 'w')
file.write(doc)
@@ -208,10 +288,11 @@ class Hook:
except: pass
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.
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
tracebacks to be written to files there."""
- sys.excepthook = Hook(display, logdir, context)
+ sys.excepthook = Hook(display=display, logdir=logdir,
+ context=context, format=format)
diff --git a/Misc/NEWS b/Misc/NEWS
index b8b9a9b..2d2c8a0 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -83,6 +83,9 @@ Extension modules
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
SourceForge) is now included as Lib/idlelib. The old Tools/idle is
no more.