[Zope-Checkins] CVS: Zope3/lib/python/Zope/Exceptions - ExceptionFormatter.py:1.1.2.1 ITracebackSupplement.py:1.1.2.1
Shane Hathaway
shane@cvs.zope.org
Thu, 14 Mar 2002 16:41:45 -0500
Update of /cvs-repository/Zope3/lib/python/Zope/Exceptions
In directory cvs.zope.org:/tmp/cvs-serv12053
Added Files:
Tag: Zope-3x-branch
ExceptionFormatter.py ITracebackSupplement.py
Log Message:
Added the custom exception formatter, with tests. Differences from the
traceback module:
- It prints module names instead of source file names, which results in
shorter lines and avoids disclosing filesystem paths.
- It can print application-specific traceback information.
- It can output in HTML format, which will make it possible to include
direct links from the traceback to the templates and scripts where the
exception occurred, without wrapping exceptions. (Wrapping exceptions
tends to obscure information.)
=== Added File Zope3/lib/python/Zope/Exceptions/ExceptionFormatter.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""An exception formatter that shows traceback supplements and traceback info,
optionally in HTML.
$Id: ExceptionFormatter.py,v 1.1.2.1 2002/03/14 21:41:45 shane Exp $
"""
import sys
import cgi
DEBUG_EXCEPTION_FORMATTER = 1
class TextExceptionFormatter:
line_sep = '\n'
show_revisions = 0
def __init__(self, limit=None):
self.limit = limit
def escape(self, s):
return s
def getPrefix(self):
return 'Traceback (innermost last):'
def getLimit(self):
limit = self.limit
if limit is None:
limit = getattr(sys, 'tracebacklimit', None)
return limit
def getRevision(self, globals):
if not self.show_revisions:
return None
revision = globals.get('__revision__', None)
if revision is None:
# Incorrect but commonly used spelling
revision = globals.get('__version__', None)
if revision is not None:
try:
revision = str(revision).strip()
except:
revision = '???'
return revision
def getObjectPath(self, o):
"""Returns an informal path to an object.
"""
try:
from Zope.ContextWrapper import wrapper
except ImportError:
# Not available.
return None
res = []
while o is not None:
d = wrapper.getdict(o)
if d:
name = d.get('name', None)
if name:
res.append(name)
o = wrapper.getcontext(o)
res.reverse()
return res
def formatSupplementLine(self, line):
return ' - %s' % line
def formatObjectInfo(self, mo):
"""Returns a line about the object.
Someday this should be replaced with code that
produces a URL for editing the object.
"""
result = []
class_ = getattr(mo, '__class__', None)
ob_type = getattr(class_, '__name__', 'Object')
s = repr(mo)
path = self.getObjectPath(mo)
if path:
s = s + ' at ' + '/'.join(path)
result.append(self.formatSupplementLine(s))
return result
def formatSupplement(self, supplement, tb):
result = []
fmtLine = self.formatSupplementLine
mo = getattr(supplement, 'manageable_object', None)
if mo is not None:
result.extend(self.formatObjectInfo(mo))
line = getattr(supplement, 'line', 0)
if line == -1:
line = tb.tb_lineno
col = getattr(supplement, 'column', -1)
if line:
if col is not None and col >= 0:
result.append(fmtLine('Line %s, Column %s' % (
line, col)))
else:
result.append(fmtLine('Line %s' % line))
elif col is not None and col >= 0:
result.append(fmtLine('Column %s' % col))
expr = getattr(supplement, 'expression', None)
if expr:
result.append(fmtLine('Expression: %s' % expr))
warnings = getattr(supplement, 'warnings', None)
if warnings:
for warning in warnings:
result.append(fmtLine('Warning: %s' % warning))
getInfo = getattr(supplement, 'getInfo', None)
if getInfo is not None:
extra = getInfo()
if extra:
result.append(extra)
return result
def formatTracebackInfo(self, tbi):
return self.formatSupplementLine('__traceback_info__: %s' % tbi)
def formatLine(self, tb):
f = tb.tb_frame
lineno = tb.tb_lineno
co = f.f_code
filename = co.co_filename
name = co.co_name
locals = f.f_locals
globals = f.f_globals
modname = globals.get('__name__', filename)
s = ' Module %s, line %d' % (modname, lineno)
revision = self.getRevision(globals)
if revision:
s = s + ', rev. %s' % revision
s = s + ', in %s' % name
result = []
result.append(self.escape(s))
try:
# Output a traceback supplement, if any.
if locals.has_key('__traceback_supplement__'):
# Use the supplement defined in the function.
tbs = locals['__traceback_supplement__']
elif globals.has_key('__traceback_supplement__'):
# Use the supplement defined in the module.
# This is used by Scripts (Python).
tbs = globals['__traceback_supplement__']
else:
tbs = None
if tbs is not None:
factory = tbs[0]
args = tbs[1:]
supp = factory(*args)
result.extend(self.formatSupplement(supp, tb))
except:
if DEBUG_EXCEPTION_FORMATTER:
import traceback
traceback.print_exc()
# else just swallow the exception.
try:
tbi = locals.get('__traceback_info__', None)
if tbi is not None:
result.append(self.formatTracebackInfo(tbi))
except:
pass
return self.line_sep.join(result)
def formatExceptionOnly(self, etype, value):
import traceback
return self.line_sep.join(
traceback.format_exception_only(etype, value))
def formatLastLine(self, exc_line):
return self.escape(exc_line)
def formatException(self, etype, value, tb):
# The next line provides a way to detect recursion.
__exception_formatter__ = 1
result = [self.getPrefix()]
limit = self.getLimit()
n = 0
while tb is not None and (limit is None or n < limit):
if tb.tb_frame.f_locals.get('__exception_formatter__'):
# Stop recursion.
result.append('(Recursive formatException() stopped)')
break
line = self.formatLine(tb)
result.append(line)
tb = tb.tb_next
n = n + 1
exc_line = self.formatExceptionOnly(etype, value)
result.append(self.formatLastLine(exc_line))
return result
class HTMLExceptionFormatter (TextExceptionFormatter):
line_sep = '<br />\r\n'
def escape(self, s):
return cgi.escape(s)
def getPrefix(self):
return '<p>Traceback (innermost last):\r\n<ul>'
def formatSupplementLine(self, line):
return '<b>%s</b>' % self.escape(str(line))
def formatTracebackInfo(self, tbi):
s = self.escape(str(tbi))
s = s.replace('\n', self.line_sep)
return '__traceback_info__: %s' % s
def formatLine(self, tb):
line = TextExceptionFormatter.formatLine(self, tb)
return '<li>%s</li>' % line
def formatLastLine(self, exc_line):
return '</ul>%s</p>' % exc_line
limit = 200
if hasattr(sys, 'tracebacklimit'):
limit = min(limit, sys.tracebacklimit)
text_formatter = TextExceptionFormatter(limit)
html_formatter = HTMLExceptionFormatter(limit)
def format_exception(t, v, tb, as_html=0):
if as_html:
fmt = html_formatter
else:
fmt = text_formatter
return fmt.formatException(t, v, tb)
=== Added File Zope3/lib/python/Zope/Exceptions/ITracebackSupplement.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""ITracebackSupplement interface definition.
$Id: ITracebackSupplement.py,v 1.1.2.1 2002/03/14 21:41:45 shane Exp $
"""
from Interface import Interface
from Interface.Attribute import Attribute
class ITracebackSupplement(Interface):
"""Provides valuable information to supplement an exception traceback.
The interface is geared toward providing meaningful feedback when
exceptions occur in user code written in mini-languages like
Zope page templates and restricted Python scripts.
"""
manageable_object = Attribute(
'manageable_object',
"""Optional. Set to the script where the exception occurred.
Normally this generates a URL in the traceback that the user
can visit to manage the object. Set to None if unknown or
not available.
"""
)
line = Attribute(
'line',
"""Optional. Set to the line number (>=1) where the exception
occurred.
Set to 0 or None if the line number is unknown.
"""
)
column = Attribute(
'column',
"""Optional. Set to the column offset (>=0) where the exception
occurred.
Set to None if the column number is unknown.
"""
)
expression = Attribute(
'expression',
"""Optional. Set to the expression that was being evaluated.
Set to None if not available or not applicable.
"""
)
warnings = Attribute(
'warnings',
"""Optional. Set to a sequence of warning messages.
Set to None if not available, not applicable, or if the exception
itself provides enough information.
"""
)
def getInfo(as_html=0):
"""Optional. Returns a string containing any other useful info.
If as_html is set, the implementation must HTML-quote the result
(normally using cgi.escape()). Returns None to provide no
extra info.
"""