[Checkins] SVN: zope.server/branches/achapman-exc-info/src/zope/server/http/ Changed implementation to close the gap with the WSGI PEP.

Satchidanand Haridas satchit at zope.com
Fri May 13 11:22:15 EDT 2011


Log message for revision 121663:
  Changed implementation to close the gap with the WSGI PEP.
  
  

Changed:
  U   zope.server/branches/achapman-exc-info/src/zope/server/http/tests/test_wsgiserver.py
  U   zope.server/branches/achapman-exc-info/src/zope/server/http/wsgihttpserver.py

-=-
Modified: zope.server/branches/achapman-exc-info/src/zope/server/http/tests/test_wsgiserver.py
===================================================================
--- zope.server/branches/achapman-exc-info/src/zope/server/http/tests/test_wsgiserver.py	2011-05-13 00:52:51 UTC (rev 121662)
+++ zope.server/branches/achapman-exc-info/src/zope/server/http/tests/test_wsgiserver.py	2011-05-13 15:22:14 UTC (rev 121663)
@@ -12,6 +12,7 @@
 """Test Puvlisher-based HTTP Server
 """
 import StringIO
+import sys
 import unittest
 from asyncore import socket_map, poll
 from threading import Thread
@@ -45,7 +46,14 @@
     Pseudo ZODB conflict error.
     """
 
+ERROR_RESPONSE = "error occurred"
+RESPONSE = "normal response"
 
+class DummyException(Exception):
+    value = "Dummy Exception to test start_response"
+    def __str__(self):
+        return repr(self.value)
+
 class PublicationWithConflict(DefaultPublication):
 
     def handleException(self, object, request, exc_info, retry_allowed=1):
@@ -320,41 +328,57 @@
 
         self.server.application = orig_app
 
-    def test_exception_handling(self):
-        # some applications/middleware (like repoze.retry) might pass the
-        # exc_info to start_response.
+    def _getFakeAppAndTask(self):
 
-        orig_app = self.server.application
-
-        class DummyException(Exception):
-            value = "Dummy Exception to test start_response"
-            def __str__(self):
-                return repr(self.value)
-
         def app(environ, start_response):
             try:
-                start_response(
-                    "500 Error",
-                    [],
-                    (DummyException, DummyException.value, None))
+                raise DummyException()
             except DummyException as e:
-                return e.value.split()
+                start_response('500 Internal Error', [], sys.exc_info())
+                return ERROR_RESPONSE.split()
+            return RESPONSE.split()
 
         class FakeTask:
+            wrote_header = 0
+            status = None
+            reason = None
             response = []
             getCGIEnvironment = lambda _: {}
             class request_data:
                 getBodyStream = lambda _: StringIO.StringIO()
             request_data = request_data()
-            setResponseStatus = appendResponseHeaders = lambda *_: None
+            appendResponseHeaders = lambda *_: None
+            def setResponseStatus(self, status, reason):
+                self.status = status
+                self.reason = reason
+            def wroteResponseHeader(self):
+                return self.wrote_header
             def write(self, v):
                 self.response.append(v)
 
-        self.server.application = app
-        task = FakeTask()
+        return app, FakeTask()
+
+
+    def test_start_response_with_no_headers_sent(self):
+        # start_response exc_info if no headers have been sent
+        orig_app = self.server.application
+        self.server.application, task = self._getFakeAppAndTask()
+
         self.server.executeRequest(task)
 
-        self.assertEqual(task.response, DummyException.value.split())
+        self.assertEqual(task.status, "500")
+        self.assertEqual(task.response, ERROR_RESPONSE.split())
+
+
+    def test_start_response_with_headers_sent(self):
+        # If headers have been sent it raises the exception
+        orig_app = self.server.application
+        self.server.application, task = self._getFakeAppAndTask()
+
+        # If headers have already been written an exception is raised
+        task.wrote_header = 1
+        self.assertRaises(DummyException, self.server.executeRequest, task)
+
         self.server.application = orig_app
 
 class PMDBTests(Tests):
@@ -375,7 +399,29 @@
                               'wsgi.multiprocess', 'wsgi.handleErrors',
                               'wsgi.run_once']))
 
+    def test_start_response_with_headers_sent(self):
+        # If headers have been sent it raises the exception, which will
+        # be caught by the server and invoke pdb.post_mortem.
+        orig_app = self.server.application
+        self.server.application, task = self._getFakeAppAndTask()
+        task.wrote_header = 1
 
+        # monkey-patch pdb.post_mortem so we don't go into pdb session.
+        pm_traceback = []
+        def fake_post_mortem(tb):
+            import traceback
+            pm_traceback.extend(traceback.format_tb(tb))
+
+        import pdb
+        orig_post_mortem = pdb.post_mortem
+        pdb.post_mortem = fake_post_mortem
+
+        self.assertRaises(DummyException, self.server.executeRequest, task)
+        self.assertTrue("raise DummyException" in pm_traceback[-1])
+        self.server.application = orig_app
+        pdb.post_mortem = orig_post_mortem
+
+
 def test_suite():
     return unittest.TestSuite((
         unittest.makeSuite(Tests),

Modified: zope.server/branches/achapman-exc-info/src/zope/server/http/wsgihttpserver.py
===================================================================
--- zope.server/branches/achapman-exc-info/src/zope/server/http/wsgihttpserver.py	2011-05-13 00:52:51 UTC (rev 121662)
+++ zope.server/branches/achapman-exc-info/src/zope/server/http/wsgihttpserver.py	2011-05-13 15:22:14 UTC (rev 121663)
@@ -79,7 +79,10 @@
         def start_response(status, headers, exc_info=None):
             if exc_info:
                 try:
-                    raise exc_info[0], exc_info[1], exc_info[2]
+                    if task.wroteResponseHeader():
+                        raise exc_info[0], exc_info[1], exc_info[2]
+                    else:
+                        pass
                 finally:
                     exc_info = None
             # Prepare the headers for output
@@ -109,7 +112,10 @@
         def start_response(status, headers, exc_info=None):
             if exc_info:
                 try:
-                    raise exc_info[0], exc_info[1], exc_info[2]
+                    if task.wroteResponseHeader():
+                        raise exc_info[0], exc_info[1], exc_info[2]
+                    else:
+                        pass
                 finally:
                     exc_info = None
             # Prepare the headers for output



More information about the checkins mailing list