[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 "&lt;string&gt;", 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