[Checkins] SVN: zope.exceptions/trunk/src/zope/exceptions/ Add
capability for line-by-line formatting of exceptions.
Aaron Lehmann
aaron at zope.com
Tue Jan 22 11:24:17 EST 2008
Log message for revision 83090:
Add capability for line-by-line formatting of exceptions.
Changed:
U zope.exceptions/trunk/src/zope/exceptions/exceptionformatter.py
U zope.exceptions/trunk/src/zope/exceptions/log.py
U zope.exceptions/trunk/src/zope/exceptions/tests/test_exceptionformatter.py
-=-
Modified: zope.exceptions/trunk/src/zope/exceptions/exceptionformatter.py
===================================================================
--- zope.exceptions/trunk/src/zope/exceptions/exceptionformatter.py 2008-01-22 15:28:11 UTC (rev 83089)
+++ zope.exceptions/trunk/src/zope/exceptions/exceptionformatter.py 2008-01-22 16:24:16 UTC (rev 83090)
@@ -28,15 +28,16 @@
line_sep = '\n'
show_revisions = 0
- def __init__(self, limit=None, with_filenames=False):
+ def __init__(self, limit=None, with_filenames=False, format=None):
self.limit = limit
self.with_filenames = with_filenames
+ self.format = format
def escape(self, s):
return s
def getPrefix(self):
- return 'Traceback (most recent call last):'
+ return self.formatText('Traceback (most recent call last):')
def getLimit(self):
limit = self.limit
@@ -118,12 +119,12 @@
s = s + ', in %s' % name
result = []
- result.append(self.escape(s))
+ result.append(self.escape(self.formatText(s)))
# Append the source line, if available
line = linecache.getline(filename, lineno)
if line:
- result.append(" " + self.escape(line.strip()))
+ result.append(self.escape(self.formatText(" " + line.strip())))
# Output a traceback supplement, if any.
if '__traceback_supplement__' in locals:
@@ -145,7 +146,6 @@
if DEBUG_EXCEPTION_FORMATTER:
traceback.print_exc()
# else just swallow the exception.
-
try:
tbi = locals.get('__traceback_info__', None)
if tbi is not None:
@@ -158,13 +158,25 @@
return self.line_sep.join(result)
def formatExceptionOnly(self, etype, value):
- result = ''.join(traceback.format_exception_only(etype, value))
- return result.replace('\n', self.line_sep)
+ result = ''.join(
+ [self.formatText(line) for line in traceback.format_exception_only(etype, value)]
+ # traceback.format_exception_only(etype, value)
+ )
+ return result
def formatLastLine(self, exc_line):
return self.escape(exc_line)
- def formatException(self, etype, value, tb):
+ def formatText(self, text):
+ if self.format:
+ args = self.format[1].__dict__.copy()
+ args['message'] = text
+ line = self.format[0] % args
+ else:
+ line = text
+ return line
+
+ def formatException(self, etype, value, tb, format=None):
# The next line provides a way to detect recursion.
__exception_formatter__ = 1
result = [self.getPrefix() + '\n']
@@ -189,10 +201,11 @@
line_sep = '<br />\r\n'
def escape(self, s):
- return cgi.escape(s)
+ return cgi.escape(super(HTMLExceptionFormatter, self).escape(s))
def getPrefix(self):
- return '<p>Traceback (most recent call last):\r\n<ul>'
+ return '<p>%s\r\n<ul>' % super(
+ HTMLExceptionFormatter, self).getPrefix()
def formatSupplementLine(self, line):
return '<b>%s</b>' % self.escape(str(line))
@@ -207,11 +220,11 @@
return '<li>%s</li>' % line
def formatLastLine(self, exc_line):
- return '</ul>%s</p>' % self.escape(exc_line)
+ return '</ul>%s</p>' % self.escape(exc_line).replace('\n', self.line_sep)
def format_exception(t, v, tb, limit=None, as_html=False,
- with_filenames=False):
+ with_filenames=False, format=None):
"""Format a stack trace and the exception information.
Similar to 'traceback.format_exception', but adds supplemental
@@ -219,14 +232,14 @@
and 'with_filenames'.
"""
if as_html:
- fmt = HTMLExceptionFormatter(limit, with_filenames)
+ fmt = HTMLExceptionFormatter(limit, with_filenames, format=format)
else:
- fmt = TextExceptionFormatter(limit, with_filenames)
+ fmt = TextExceptionFormatter(limit, with_filenames, format=format)
return fmt.formatException(t, v, tb)
def print_exception(t, v, tb, limit=None, file=None, as_html=False,
- with_filenames=True):
+ with_filenames=True, format=None):
"""Print exception up to 'limit' stack trace entries from 'tb' to 'file'.
Similar to 'traceback.print_exception', but adds supplemental
@@ -235,6 +248,6 @@
"""
if file is None:
file = sys.stderr
- lines = format_exception(t, v, tb, limit, as_html, with_filenames)
+ lines = format_exception(t, v, tb, limit, as_html, with_filenames, format=format)
for line in lines:
file.write(line)
Modified: zope.exceptions/trunk/src/zope/exceptions/log.py
===================================================================
--- zope.exceptions/trunk/src/zope/exceptions/log.py 2008-01-22 15:28:11 UTC (rev 83089)
+++ zope.exceptions/trunk/src/zope/exceptions/log.py 2008-01-22 16:24:16 UTC (rev 83090)
@@ -13,7 +13,7 @@
##############################################################################
"""Log formatter that enhances tracebacks with extra information.
-$Id: $
+$Id$
"""
import logging
@@ -21,14 +21,55 @@
from zope.exceptions.exceptionformatter import print_exception
class Formatter(logging.Formatter):
+ def __init__(self, fmt=None, datefmt=None, as_html=0,):
+ logging.Formatter.__init__(self, fmt=fmt, datefmt=datefmt)
+ self.as_html = as_html
- def formatException(self, ei):
+ def format(self, record):
+ record.message = record.getMessage()
+ if self._fmt.find("%(asctime)") >= 0:
+ record.asctime = self.formatTime(record, self.datefmt)
+ cdict = record.__dict__.copy()
+ lines = []
+ for line in record.message.splitlines():
+ cdict['message'] = line
+ lines.append(self._fmt % cdict)
+ s = '\n'.join(lines)
+ if record.exc_info:
+ # Cache the traceback text to avoid converting it multiple times
+ # (it's constant anyway)
+ if not record.exc_text:
+ record.exc_text = self.formatException(record.exc_info, record=record)
+ if record.exc_text:
+ s = record.exc_text
+ else:
+ cdict = record.__dict__.copy()
+ lines = []
+ for line in record.message.splitlines():
+ cdict['message'] = line
+ lines.append(self._fmt % cdict)
+ s = '\n'.join(lines)
+ return s
+
+ def formatException(self, ei, record=None):
"""Format and return the specified exception information as a string.
Uses zope.exceptions.exceptionformatter to generate the traceback.
"""
sio = cStringIO.StringIO()
- print_exception(ei[0], ei[1], ei[2], file=sio, with_filenames=True)
+ if record:
+ format = (self._fmt, record)
+ else:
+ format = None
+
+ print_exception(
+ ei[0],
+ ei[1],
+ ei[2],
+ file=sio,
+ as_html=self.as_html,
+ with_filenames=True,
+ format=format)
s = sio.getvalue()
if s.endswith("\n"):
s = s[:-1]
Modified: zope.exceptions/trunk/src/zope/exceptions/tests/test_exceptionformatter.py
===================================================================
--- zope.exceptions/trunk/src/zope/exceptions/tests/test_exceptionformatter.py 2008-01-22 15:28:11 UTC (rev 83089)
+++ zope.exceptions/trunk/src/zope/exceptions/tests/test_exceptionformatter.py 2008-01-22 16:24:16 UTC (rev 83090)
@@ -16,19 +16,31 @@
$Id$
"""
import sys
-from unittest import TestCase, main, makeSuite
+import unittest
+import zope.testing.doctest
+import zope.exceptions.log
from zope.exceptions.exceptionformatter import format_exception
from zope.testing.cleanup import CleanUp # Base class w registry cleanup
+import logging
+import StringIO
-def tb(as_html=0):
+def tb(as_html=0, format=None):
+ log = logging.getLogger('')
+ fake_stdout = StringIO.StringIO()
+ hndler = logging.StreamHandler(fake_stdout)
+ hndler.setFormatter(zope.exceptions.log.Formatter(as_html=as_html, fmt=format))
+ log.addHandler(hndler)
+
t, v, b = sys.exc_info()
try:
- return ''.join(format_exception(t, v, b, as_html=as_html))
+ log.exception('')
+ s = fake_stdout.getvalue()
+ return s
finally:
del b
+ log.removeHandler(hndler)
-
class ExceptionForTesting (Exception):
pass
@@ -46,7 +58,7 @@
-class Test(CleanUp, TestCase):
+class Test(CleanUp, unittest.TestCase):
def testBasicNamesText(self, as_html=0):
try:
@@ -160,9 +172,47 @@
' ^',
'SyntaxError: unexpected EOF while parsing'])
+ def testFormattedExceptionText(self):
+ try:
+ exec 'syntax error'
+ except SyntaxError, se:
+ s = tb(format="Hello, World! %(message)s")
+ # Hello, World! Traceback (most recent call last):
+ # Hello, World! Module zope.exceptions.tests.test_exceptionformatter, line ??, in testFormattedExceptionText
+ # Hello, World! exec \'syntax error\'
+ # Hello, World! File "<string>", line 1
+ # Hello, World! syntax error
+ # Hello, World! ^
+ # Hello, World! SyntaxError: unexpected EOF while parsing
+ self.assertEquals(s.splitlines()[-3:],
+ ['Hello, World! syntax error',
+ 'Hello, World! ^',
+ 'Hello, World! SyntaxError: unexpected EOF while parsing'])
+ def testFormattedExceptionHTML(self):
+ try:
+ exec 'syntax error'
+ except SyntaxError, se:
+ s = tb(as_html=1, format="Hello, World! %(message)s")
+ # <p>Hello, World! Traceback (most recent call last):
+ # <ul>
+ # <li>Hello, World! File "/Users/aaron/work/projects/zope.exceptions-traceback-log-formatting/src/zope/exceptions/tests/test_exceptionformatter.py", line 197, in testFormattedExceptionHTML<br />
+ # Hello, World! exec 'syntax error'</li>
+ # </ul>Hello, World! File "<string>", line 1<br />
+ # Hello, World! syntax error<br />
+ # Hello, World! ^<br />
+ # Hello, World! SyntaxError: unexpected EOF while parsing<br />
+ # </p>
+ self.assertEquals(s.splitlines()[-4:],
+ ['Hello, World! syntax error<br />',
+ 'Hello, World! ^<br />',
+ 'Hello, World! SyntaxError: unexpected EOF while parsing<br />',
+ '</p>'])
+
def test_suite():
- return makeSuite(Test)
+ return unittest.TestSuite([
+ unittest.makeSuite(Test),
+ ])
if __name__=='__main__':
- main(defaultTest='test_suite')
+ unittest.main(defaultTest='test_suite')
More information about the Checkins
mailing list