[Checkins] SVN: zope.publisher/trunk/ Merge changes from unauth-exc-handler branch into trunk.

Uli Fouquet uli at gnufix.de
Tue Jul 7 07:28:40 EDT 2009


Log message for revision 101690:
  Merge changes from unauth-exc-handler branch into trunk.

Changed:
  U   zope.publisher/trunk/CHANGES.txt
  U   zope.publisher/trunk/src/zope/publisher/interfaces/__init__.py
  U   zope.publisher/trunk/src/zope/publisher/publish.py
  U   zope.publisher/trunk/src/zope/publisher/tests/test_publisher.py

-=-
Modified: zope.publisher/trunk/CHANGES.txt
===================================================================
--- zope.publisher/trunk/CHANGES.txt	2009-07-07 11:11:33 UTC (rev 101689)
+++ zope.publisher/trunk/CHANGES.txt	2009-07-07 11:28:39 UTC (rev 101690)
@@ -4,10 +4,20 @@
 3.8.1 (unreleased)
 ------------------
 
+- Introduced ``IReRaiseException`` interface. If during publishing an
+  exception occurs and for this exception an adapter is available that
+  returns ``False`` on being called, the exception won't be reraised
+  by the publisher. This happens only if ``handle_errors`` parameter
+  of the ``publish()`` method is set to ``False``. Fixes problems when
+  acting in a WSGI pipeline with a debugger middleware enabled.
+
+  See https://bugs.launchpad.net/grok/+bug/332061 for details.
+
 - Fix #98471: Restrict redirects to current host. This causes a ValueError to
   be raised in the case of redirecting to a different host. If this is
   intentional, the parameter `trusted` can be given.
 
+
 3.8.0 (2009-05-23)
 ------------------
 

Modified: zope.publisher/trunk/src/zope/publisher/interfaces/__init__.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/interfaces/__init__.py	2009-07-07 11:11:33 UTC (rev 101689)
+++ zope.publisher/trunk/src/zope/publisher/interfaces/__init__.py	2009-07-07 11:28:39 UTC (rev 101690)
@@ -498,3 +498,20 @@
     A default view name is used to select a view when a user hasn't
     specified one.
     """
+
+class IReRaiseException(Interface):
+    """An exception that should be reraised, when handled in publisher.
+
+    Under some circumstances (for instance if acting in a WSGI
+    pipeline with debugger middleware) certain exceptions occuring
+    while publishing should be handled by the Zope machinery and never
+    reach the 'outside world'.
+
+    Adapters providing this interface for a certain exception type
+    which also return ``False`` when being called, indicate by this
+    that the exception should not be reraised during publishing.
+
+    This makes it possible, for instance, to authenticate with
+    basic-auth when a debugger middleware is used and `IUnauthorized`
+    is raised.
+    """

Modified: zope.publisher/trunk/src/zope/publisher/publish.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/publish.py	2009-07-07 11:11:33 UTC (rev 101689)
+++ zope.publisher/trunk/src/zope/publisher/publish.py	2009-07-07 11:28:39 UTC (rev 101690)
@@ -18,10 +18,10 @@
 $Id$
 """
 import sys
-from zope.publisher.interfaces import Retry
+from zope import component
+from zope.publisher.interfaces import Retry, IReRaiseException
 from zope.proxy import removeAllProxies
 
-
 _marker = object()  # Create a new marker object.
 
 def unwrapMethod(obj):
@@ -138,11 +138,18 @@
                             publication.afterCall(request, obj)
 
                         except:
+                            exc_info = sys.exc_info()
                             publication.handleException(
-                                obj, request, sys.exc_info(), True)
+                                obj, request, exc_info, True)
 
                             if not handle_errors:
-                                raise
+                                # Reraise only if there is no adapter
+                                # indicating that we shouldn't
+                                reraise = component.queryAdapter(
+                                    exc_info[1], IReRaiseException,
+                                    default=None)
+                                if reraise is None or reraise():
+                                    raise
                     finally:
                         publication.endRequest(request, obj)
 

Modified: zope.publisher/trunk/src/zope/publisher/tests/test_publisher.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/tests/test_publisher.py	2009-07-07 11:11:33 UTC (rev 101689)
+++ zope.publisher/trunk/src/zope/publisher/tests/test_publisher.py	2009-07-07 11:28:39 UTC (rev 101690)
@@ -17,21 +17,18 @@
 """
 import unittest
 
+from zope import component
 from zope.publisher.publish import publish
 from zope.publisher.base import TestRequest
 from zope.publisher.base import DefaultPublication
 from zope.publisher.interfaces import Unauthorized, NotFound, DebugError
-from zope.publisher.interfaces import IPublication
+from zope.publisher.interfaces import IPublication, IReRaiseException
 
 from zope.interface.verify import verifyClass
 from zope.interface import implementedBy
 
 from StringIO import StringIO
 
-class TestPublication(DefaultPublication):
-    # Override handleException to reraise for testing purposes
-    def handleException(self, object, request, exc_info, retry_allowed=1):
-        raise exc_info[0], exc_info[1], exc_info[2]
 
 class PublisherTests(unittest.TestCase):
     def setUp(self):
@@ -58,7 +55,7 @@
         self.app.noDocString = NoDocstringItem()
 
     def _createRequest(self, path, **kw):
-        publication = TestPublication(self.app)
+        publication = DefaultPublication(self.app)
         path = path.split('/')
         path.reverse()
         request = TestRequest(StringIO(''), **kw)
@@ -72,6 +69,15 @@
         publish(request, handle_errors=False)
         return response._result
 
+    def _registerExcAdapter(self, factory):
+        component.provideAdapter(factory, (Unauthorized,), IReRaiseException)
+
+    def _unregisterExcAdapter(self, factory):
+        gsm = component.getGlobalSiteManager()
+        gsm.unregisterAdapter(
+            factory=factory, required=(Unauthorized,),
+            provided=IReRaiseException)
+
     def testImplementsIPublication(self):
         self.failUnless(IPublication.providedBy(
                             DefaultPublication(self.app)))
@@ -97,6 +103,31 @@
     def testDebugError(self):
         self.assertRaises(DebugError, self._publisherResults, '/noDocString')
 
+    def testIReRaiseExceptionAdapters(self):
+
+        def dontReRaiseAdapter(context):
+            def shouldBeReRaised():
+                return False
+            return shouldBeReRaised
+
+        self._registerExcAdapter(dontReRaiseAdapter)
+        try:
+            self._publisherResults('/_item')
+        except Unauthorized:
+            self.fail('Unauthorized raised though this should '
+                            'not happen')
+        finally:
+            self._unregisterExcAdapter(dontReRaiseAdapter)
+
+        def doReRaiseAdapter(context):
+            def shouldBeReRaised():
+                return True
+            return shouldBeReRaised
+
+        self._registerExcAdapter(doReRaiseAdapter)
+        self.failUnlessRaises(Unauthorized, self._publisherResults, '/_item')
+        self._unregisterExcAdapter(doReRaiseAdapter)
+            
 def test_suite():
     loader = unittest.TestLoader()
     return loader.loadTestsFromTestCase(PublisherTests)



More information about the Checkins mailing list