[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