[Checkins] SVN: zope.exceptions/trunk/ Added TextExceptionFormatter.extractStack and extract_stack

Adam Groszer cvs-admin at zope.org
Wed Mar 28 13:58:14 UTC 2012


Log message for revision 124764:
  Added TextExceptionFormatter.extractStack and extract_stack

Changed:
  U   zope.exceptions/trunk/CHANGES.txt
  U   zope.exceptions/trunk/src/zope/exceptions/exceptionformatter.py
  U   zope.exceptions/trunk/src/zope/exceptions/tests/test_exceptionformatter.py

-=-
Modified: zope.exceptions/trunk/CHANGES.txt
===================================================================
--- zope.exceptions/trunk/CHANGES.txt	2012-03-28 13:55:01 UTC (rev 124763)
+++ zope.exceptions/trunk/CHANGES.txt	2012-03-28 13:58:11 UTC (rev 124764)
@@ -5,7 +5,7 @@
 3.6.3 (unreleased)
 ------------------
 
-- Nothing changed yet.
+- Added TextExceptionFormatter.extractStack and extract_stack
 
 
 3.6.2 (2012-03-28)

Modified: zope.exceptions/trunk/src/zope/exceptions/exceptionformatter.py
===================================================================
--- zope.exceptions/trunk/src/zope/exceptions/exceptionformatter.py	2012-03-28 13:55:01 UTC (rev 124763)
+++ zope.exceptions/trunk/src/zope/exceptions/exceptionformatter.py	2012-03-28 13:58:11 UTC (rev 124764)
@@ -98,9 +98,14 @@
     def formatTracebackInfo(self, tbi):
         return self.formatSupplementLine('__traceback_info__: %s' % (tbi, ))
 
-    def formatLine(self, tb):
-        f = tb.tb_frame
-        lineno = tb.tb_lineno
+    def formatLine(self, tb=None, f=None):
+        if tb and not f:
+            f = tb.tb_frame
+            lineno = tb.tb_lineno
+        elif not tb and f:
+            lineno = f.f_lineno
+        else:
+            raise ValueError("tb or f needs to be passed")
         co = f.f_code
         filename = co.co_filename
         name = co.co_name
@@ -174,7 +179,7 @@
                 result.append('(Recursive formatException() stopped, trying traceback.format_tb)\n')
                 result.extend(traceback.format_tb(tb))
                 break
-            line = self.formatLine(tb)
+            line = self.formatLine(tb=tb)
             result.append(line + '\n')
             tb = tb.tb_next
             n = n + 1
@@ -182,7 +187,26 @@
         result.append(self.formatLastLine(exc_line))
         return result
 
+    def extractStack(self, f=None):
+        if f is None:
+            try:
+                raise ZeroDivisionError
+            except ZeroDivisionError:
+                f = sys.exc_info()[2].tb_frame.f_back
 
+        # The next line provides a way to detect recursion.
+        __exception_formatter__ = 1
+        result = []
+        limit = self.getLimit()
+        n = 0
+        while f is not None and (limit is None or n < limit):
+            line = self.formatLine(f=f)
+            result.append(line + '\n')
+            f = f.f_back
+            n = n + 1
+        return result
+
+
 class HTMLExceptionFormatter(TextExceptionFormatter):
 
     line_sep = '<br />\r\n'
@@ -201,8 +225,8 @@
         s = s.replace('\n', self.line_sep)
         return '__traceback_info__: %s' % (s, )
 
-    def formatLine(self, tb):
-        line = TextExceptionFormatter.formatLine(self, tb)
+    def formatLine(self, tb=None, f=None):
+        line = TextExceptionFormatter.formatLine(self, tb, f)
         return '<li>%s</li>' % line
 
     def formatLastLine(self, exc_line):
@@ -237,3 +261,17 @@
     lines = format_exception(t, v, tb, limit, as_html, with_filenames)
     for line in lines:
         file.write(line)
+
+def extract_stack(f, limit=None, as_html=False,
+                  with_filenames=True):
+    """Format a stack trace and the exception information.
+
+    Similar to 'traceback.format_exception', but adds supplemental
+    information to the traceback and accepts two options, 'as_html'
+    and 'with_filenames'.
+    """
+    if as_html:
+        fmt = HTMLExceptionFormatter(limit, with_filenames)
+    else:
+        fmt = TextExceptionFormatter(limit, with_filenames)
+    return fmt.extractStack(f)

Modified: zope.exceptions/trunk/src/zope/exceptions/tests/test_exceptionformatter.py
===================================================================
--- zope.exceptions/trunk/src/zope/exceptions/tests/test_exceptionformatter.py	2012-03-28 13:55:01 UTC (rev 124763)
+++ zope.exceptions/trunk/src/zope/exceptions/tests/test_exceptionformatter.py	2012-03-28 13:58:11 UTC (rev 124764)
@@ -18,6 +18,7 @@
 from unittest import TestCase, makeSuite
 
 from zope.exceptions.exceptionformatter import format_exception
+from zope.exceptions.exceptionformatter import extract_stack
 
 
 def tb(as_html=0):
@@ -27,6 +28,12 @@
     finally:
         del b
 
+def st(as_html=0):
+    f = sys.exc_info()[2].tb_frame
+    try:
+        return ''.join(extract_stack(f, as_html=as_html))
+    finally:
+        del f
 
 class ExceptionForTesting (Exception):
     pass
@@ -60,6 +67,19 @@
     def testBasicNamesHTML(self):
         self.testBasicNamesText(1)
 
+    def testBasicNamesText_stack(self, as_html=0):
+        try:
+            raise ExceptionForTesting
+        except ExceptionForTesting:
+            s = st(as_html)
+            # The stack trace should include the name of this function.
+            self.assertTrue(s.find('testBasicNamesText_stack') >= 0)
+        else:
+            self.fail('no exception occurred')
+
+    def testBasicNamesHTML_stack(self):
+        self.testBasicNamesText_stack(1)
+
     def testSupplement(self, as_html=0):
         try:
             __traceback_supplement__ = (TestingTracebackSupplement,
@@ -83,6 +103,29 @@
     def testSupplementHTML(self):
         self.testSupplement(1)
 
+    def testSupplement_stack(self, as_html=0):
+        try:
+            __traceback_supplement__ = (TestingTracebackSupplement,
+                                        "You're one in a million")
+            raise ExceptionForTesting
+        except ExceptionForTesting:
+            s = st(as_html)
+            # The source URL
+            self.assertTrue(s.find('/somepath') >= 0, s)
+            # The line number
+            self.assertTrue(s.find('634') >= 0, s)
+            # The column number
+            self.assertTrue(s.find('57') >= 0, s)
+            # The expression
+            self.assertTrue(s.find("You're one in a million") >= 0, s)
+            # The warning
+            self.assertTrue(s.find("Repent, for the end is nigh") >= 0, s)
+        else:
+            self.fail('no exception occurred')
+
+    def testSupplementHTML_stack(self):
+        self.testSupplement_stack(1)
+
     def testTracebackInfo(self, as_html=0):
         try:
             __traceback_info__ = "Adam & Eve"
@@ -100,6 +143,23 @@
     def testTracebackInfoHTML(self):
         self.testTracebackInfo(1)
 
+    def testTracebackInfo_stack(self, as_html=0):
+        try:
+            __traceback_info__ = "Adam & Eve"
+            raise ExceptionForTesting
+        except ExceptionForTesting:
+            s = st(as_html)
+            if as_html:
+                # Be sure quoting is happening.
+                self.assertTrue(s.find('Adam &amp; Eve') >= 0, s)
+            else:
+                self.assertTrue(s.find('Adam & Eve') >= 0, s)
+        else:
+            self.fail('no exception occurred')
+
+    def testTracebackInfoHTML_stack(self):
+        self.testTracebackInfo_stack(1)
+
     def testTracebackInfoTuple(self):
         try:
             __traceback_info__ = ("Adam", "Eve")
@@ -157,9 +217,9 @@
 
         class FormatterException(Exception):
             pass
-            
+
         class FailingFormatter(TextExceptionFormatter):
-            def formatLine(self, tb):
+            def formatLine(self, tb=None, f=None):
                 raise FormatterException("Formatter failed")
 
         fmt = FailingFormatter()
@@ -176,5 +236,6 @@
         self.assertEqual(s.splitlines()[-2], '    raise FormatterException("Formatter failed")')
         self.assertTrue('FormatterException: Formatter failed' in s.splitlines()[-1])
 
+
 def test_suite():
     return makeSuite(Test)



More information about the checkins mailing list