[Zope3-checkins] SVN: Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/ Fixed zope.app.wsgi implementation and tests. See how much get deleted? So

Stephan Richter srichter at cosmos.phy.tufts.edu
Fri Sep 2 17:10:41 EDT 2005


Log message for revision 38264:
  Fixed zope.app.wsgi implementation and tests. See how much get deleted? So 
  the pain of formalizing the publisher was worth it!
  
  

Changed:
  U   Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/publication/httpfactory.py
  U   Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/README.txt
  U   Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/__init__.py

-=-
Modified: Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/publication/httpfactory.py
===================================================================
--- Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/publication/httpfactory.py	2005-09-02 21:08:14 UTC (rev 38263)
+++ Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/publication/httpfactory.py	2005-09-02 21:10:40 UTC (rev 38264)
@@ -63,7 +63,7 @@
         self._db = db
         self._publication_cache = {}
 
-    def __call__(self, input_stream, output_steam, env):
+    def __call__(self, input_stream, env):
         """See `zope.app.publication.interfaces.IPublicationRequestFactory`"""
         method = env.get('REQUEST_METHOD', 'GET').upper()
         request_class, publication_class = chooseClasses(method, env)
@@ -73,7 +73,7 @@
             publication = publication_class(self._db)
             self._publication_cache[publication_class] = publication
 
-        request = request_class(input_stream, output_steam, env)
+        request = request_class(input_stream, env)
         request.setPublication(publication)
         if IBrowserRequest.providedBy(request):
             # only browser requests have skins

Modified: Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/README.txt
===================================================================
--- Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/README.txt	2005-09-02 21:08:14 UTC (rev 38263)
+++ Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/README.txt	2005-09-02 21:10:40 UTC (rev 38264)
@@ -3,22 +3,14 @@
 =====================
 
 This package contains an interpretation of the WSGI specification (PEP-0333)
-for the Zope application server by providing a WSGI application object. First,
-we create a stream that will contain the response:
+for the Zope application server by providing a WSGI application object. The
+first step is to initialize the WSGI-compliant Zope application that is called
+from the server. To do that, we first have to create and open a ZODB
+connection:
 
-  >>> import StringIO
-  >>> data = StringIO.StringIO('')
-
-Usually this stream is created by the HTTP server and refers to the output
-stream sent to the server client.
-
-Now that we have our output stream, we need to initialize the WSGI-compliant
-Zope application that is called from the server. To do that, we first have to
-create and open a ZODB connection:
-
   >>> from ZODB.MappingStorage import MappingStorage
   >>> from ZODB.DB import DB
-  
+
   >>> storage = MappingStorage('test.db')
   >>> db = DB(storage, cache_size=4000)
 
@@ -31,44 +23,37 @@
 and the function that initializes the response and returns a function with
 which the output data can be written.
 
-Even though this is commonly done by the server, our first task is to create
-an appropriate environment for the request.
+Even though this is commonly done by the server, we now have to create an
+appropriate environment for the request.
 
+  >>> import cStringIO
   >>> environ = {
   ...     'PATH_INFO': '/',
-  ...     'wsgi.input': StringIO.StringIO('')}
+  ...     'wsgi.input': cStringIO.StringIO('')}
 
 Next we create a WSGI-compliant ``start_response()`` method that accepts the
 status of the response to the HTTP request and the headers that are part of
-the response stream. The headers are expected to be a list of
-2-tuples. However, for the purpose of this demonstration we simply ignore all
-the arguments and push a simple message to the stream. The
-``start_response()`` funtion must also return a ``write()`` callable that
-accepts further data.
+the response stream. The headers are expected to be a list of 2-tuples. The
+``start_response()`` method must also return a ``write()`` function that
+directly writes the output to the server. However, the Zope 3 implementation
+will not utilize this function, since it is strongly discouraged by
+PEP-0333. The second method of getting data to the server is by returning an
+iteratable from the application call. Sp we simply ignore all the arguments
+and return ``None`` as the write method.
 
   >>> def start_response(status, headers):
-  ...     data.write('status and headers.\n\n')
-  ...     return data.write
-  ...
+  ...     return None
 
 Now we can send the fabricated HTTP request to the application for processing:
 
-  >>> app(environ, start_response)
-  ''
-
-We expect the output of this call to be always an empty string, since all the
-output is written to the output stream directly. Looking at the output 
-
-  >>> print data.getvalue()
-  status and headers.
-  <BLANKLINE>
+  >>> print ''.join(app(environ, start_response))
   <html><head><title>Unauthorized</title></head>
   <body><h2>Unauthorized</h2>
   A server error occurred.
   </body></html>
   <BLANKLINE>
 
-we can see that application really crashed and did not know what to do. This
+We can see that application really crashed and did not know what to do. This
 is okay, since we have not setup anything. Getting a request successfully
 processed would require us to bring up a lot of Zope 3's system, which would
 be just a little bit too much for this demonstration.
@@ -108,93 +93,6 @@
   i.e. by calling a method on the server that sets the application.
 
 
-The ``IWSGIOutput`` Component
------------------------------
-
-Under the hood the WSGI support uses a component that implements
-``IWSGIOutput`` that manages the response headers and provides the output
-stream by implementing the ``write()`` method. In the following text the
-functionality of this class is introduced:  
-
-First, we reset our output stream:
-
-  >>> data.__init__('')
-
-Then we initialize an instance of the WSGI output object:
-
-  >>> output = wsgi.WSGIOutput(start_response)
-
-You can set the response status
-
-  >>> output.setResponseStatus("200", "OK")
-  >>> output._statusString
-  '200 OK'
-
-or set arbitrary headers as a mapping:
-
-  >>> output.setResponseHeaders({'a':'b', 'c':'d'})
-
-The headers must be returned as a list of tuples:
-
-  >>> output.getHeaders()
-  [('a', 'b'), ('c', 'd')]
-  
-Calling ``setResponseHeaders()`` again adds new values:
-
-  >>> output.setResponseHeaders({'x':'y', 'c':'d'})
-  >>> h = output.getHeaders()
-  >>> h.sort()
-  >>> h
-  [('a', 'b'), ('c', 'd'), ('x', 'y')]
-
-Headers that can potentially repeat are added using
-``appendResponseHeaders()``:
-
-  >>> output.appendResponseHeaders(['foo: bar'])
-  >>> h = output.getHeaders()
-  >>> h.sort()
-  >>> h    
-  [('a', 'b'), ('c', 'd'), ('foo', ' bar'), ('x', 'y')]
-  >>> output.appendResponseHeaders(['foo: bar'])
-  >>> h = output.getHeaders()
-  >>> h.sort()
-  >>> h    
-  [('a', 'b'), ('c', 'd'), ('foo', ' bar'), ('foo', ' bar'), ('x', 'y')]
-
-Headers containing a colon should also work
-
-  >>> output.appendResponseHeaders(['my: brain:hurts'])
-  >>> h = output.getHeaders()
-  >>> h.sort()
-  >>> h
-  [('a', 'b'), ('c', 'd'), ('foo', ' bar'), ('foo', ' bar'), 
-   ('my', ' brain:hurts'), ('x', 'y')]
-
-The headers should not be written to the output
-
-  >>> output.wroteResponseHeader()
-  False
-  >>> data.getvalue()
-  ''
-
-Let's now write something to the output stream:
-
-  >>> output.write('Now for something')
-
-The headers should be sent and the data written to the stream:
-
-  >>> output.wroteResponseHeader()
-  True
-  >>> data.getvalue()
-  'status and headers.\n\nNow for something'
-
-Calling write again the headers should not be sent again
-
-  >>> output.write(' completly different!')
-  >>> data.getvalue()
-  'status and headers.\n\nNow for something completly different!'
-
-
 About WSGI
 ----------
 

Modified: Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/__init__.py
===================================================================
--- Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/__init__.py	2005-09-02 21:08:14 UTC (rev 38263)
+++ Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/__init__.py	2005-09-02 21:10:40 UTC (rev 38264)
@@ -22,61 +22,7 @@
 from zope.app.publication.httpfactory import HTTPPublicationRequestFactory
 from zope.app.wsgi import interfaces
 
-class WSGIOutput(object):
-    """This class handles the output generated by the publisher. It is used to
-    collect the headers and as an outstream to output the response body.
-    """
-    implements(interfaces.IWSGIOutput)
 
-    def __init__(self, start_response):
-        self._headers = {}
-        self._accumulatedHeaders = []
-        self._statusString = ""
-        self._headersSent = False
-
-        self.wsgi_write = None
-        self.start_response = start_response
-
-    def setResponseStatus(self, status, reason):
-        """See zope.publisher.interfaces.http.IHeaderOutput"""
-        self._statusString = str(status) + ' ' + reason
-
-    def setResponseHeaders(self, mapping):
-        """See zope.publisher.interfaces.http.IHeaderOutput"""
-        self._headers.update(mapping)
-
-    def appendResponseHeaders(self, lst):
-        """See zope.publisher.interfaces.http.IHeaderOutput"""
-        self._accumulatedHeaders.extend(lst)
-
-    def wroteResponseHeader(self):
-        """See zope.publisher.interfaces.http.IHeaderOutput"""
-        return self._headersSent
-
-    def setAuthUserName(self, name):
-        """See zope.publisher.interfaces.http.IHeaderOutput"""
-        pass
-
-    def getHeaders(self):
-        """See zope.app.wsgi.interfaces.IWSGIOutput"""
-        response_headers = self._headers.items()
-
-        accum = [tuple(line.split(':', 1))
-                 for line in self._accumulatedHeaders]
-
-        response_headers.extend(accum)
-        return response_headers
-
-    def write(self, data):
-        """See zope.app.wsgi.interfaces.IWSGIOutput"""
-        if not self._headersSent:
-            self.wsgi_write = self.start_response(self._statusString,
-                                                  self.getHeaders())
-            self._headersSent = True
-
-        self.wsgi_write(data)
-
-
 class WSGIPublisherApplication(object):
     """A WSGI application implemenation for the zope publisher
 
@@ -85,7 +31,7 @@
     The class relies on a properly initialized request factory.
     """
     implements(interfaces.IWSGIApplication)
-    
+
     def __init__(self, db=None, factory=HTTPPublicationRequestFactory):
         self.requestFactory = None
 
@@ -94,17 +40,12 @@
 
     def __call__(self, environ, start_response):
         """See zope.app.wsgi.interfaces.IWSGIApplication"""
-        # wsgiOutput has two purposes: (1) it is the response headers output
-        # manager and (2) it functions as the output stream for the publisher. 
-        wsgiOutput = WSGIOutput(start_response)
-
-        request = self.requestFactory(
-            environ['wsgi.input'], wsgiOutput, environ)
-
-        request.response.setHeaderOutput(wsgiOutput)
-
+        request = self.requestFactory(environ['wsgi.input'], environ)
+        response = request.response
         publish(request)
 
-        # since the response is written using the WSGI ``write()`` callable
-        # return an empty iterable (see PEP 333).
-        return ""
+        # Start the WSGI server response
+        start_response(response.getStatusString(), response.getHeaders())
+
+        # Return the result body iterable.
+        return response.result.body



More information about the Zope3-Checkins mailing list