[Zope3-dev] PEP 333 and Zope

Eckart Hertzler eckart at hertzler.de
Tue Nov 9 04:10:28 EST 2004


Hallo,

I am by no means a zope server guru - far from it.
But the WSGI specification seems to be a good thing, so I
decided to try just how hard it would be to convert Zope3
to a WSGI application.

It proved to be quite easy : gere is a link to a Zope3 server running
on Phillips WSGI reference server :

http://lentix-consulting.com:8090/

It is just a pre proof of concept.

I introduced a WSGI server type which starts the WSGI reference server as a zope ServerType
and uses the HTTPPublicationRequestFactory as is.

The entire source code is as follows :

[snip]
from string import split, strip
from StringIO import StringIO

from zope.interface import implements
from zope.publisher.publish import publish
from zope.server.http.httpserver import HTTPServer
from zope.server.interfaces import IHeaderOutput

from zope.app.publication.httpfactory import HTTPPublicationRequestFactory
from zope.app.server.servertype import ServerType
from zope.server.http.commonaccesslogger import CommonAccessLogger

from thread import start_new_thread

from wsgiref.simple_server import WSGIServer, WSGIRequestHandler

class WsgiHeaderOutput(object):

    implements(IHeaderOutput)

    def __init__(self):
 self._headers = {}
 self._accumulatedHeaders = []
 self._statusString = ""
 self._headersSent = False

    def setResponseStatus(self,status, reason):
        """Sets the status code and the accompanying message.
        """
 self._statusString = str(status)+' '+reason

    def setResponseHeaders(self, mapping):
        """Sets headers.  The headers must be Correctly-Cased.
        """
 self._headers.update(mapping)

    def appendResponseHeaders(self, lst):
        """Sets headers that can potentially repeat.

        Takes a list of strings.
        """
 accum = self._accumulatedHeaders
 if accum is None:
     self._accumulatedHeaders = accum = []
     accum.extend(lst)

    def wroteResponseHeader(self):
        """Returns a flag indicating whether the response

        header has already been sent.
        """
 return self._headersSent

    def setAuthUserName(self, name):
        """Sets the name of the authenticated user so the name can be logged.
        """
 pass

    def getStatusString(self):
 return self._statusString

    def getHeaders(self):
 response_headers = self._headers.items()
 accum = [ tuple(split(line,':')) for line in self._accumulatedHeaders]
 response_headers.extend(accum)
 return response_headers

class PublisherApp:

    request_factory = None

    # wsgi callable 
    def __init__(self, env, start_response):
        self.env = env
        self.start = start_response
        self._outstream = StringIO()

        self.request = PublisherApp.request_factory( \
            env['wsgi.input'], self._outstream, env)

        self.response = self.request.response

        # otherwise the headers are written directly to the outstream
        self.header_output = WsgiHeaderOutput()
        self.response.setHeaderOutput(self.header_output)

    def __iter__(self):

        publish(self.request)

        response_headers = self.header_output.getHeaders()

        self.start(
            self.header_output.getStatusString(), 
            response_headers)

        yield str(self._outstream.getvalue())

        #response.setHTTPTransaction(task)

    def close(self):
        self._outstream.close()
        self.request.close()


class WSGIHTTPServer(WSGIServer):
    """Zope Publisher-specific HTTP Server"""

    SERVER_IDENT = 'zope.server.wsgi'

    def __init__(self, request_factory, name, test, port, sub_protocol=None, *args, **kw):

        PublisherApp.request_factory = request_factory

        # An HTTP server is not limited to serving up HTML; it can be
        # used for other protocols, like XML-RPC, SOAP and so as well
        # Here we just allow the logger to output the sub-protocol type.
        if sub_protocol:
            self.SERVER_IDENT += ' (%s)' %str(sub_protocol)


        server_address = ('', 8090)
        if port is not None:
            server_address = ('', port)
        
        WSGIServer.__init__(self, server_address, WSGIRequestHandler)
        self.set_app(PublisherApp)

        sa = self.socket.getsockname()
        print "Serving HTTP on", sa[0], "port", sa[1], "..."

        start_new_thread(self.serve_forever, ())


wsgi = ServerType(WSGIHTTPServer,
                  HTTPPublicationRequestFactory,
                  CommonAccessLogger,
                  8090, True)

[snip]


On Monday 18 October 2004 17:44, Jim Fulton is believed to have said:
> Phillip J. Eby wrote:
> > This is just a little heads-up to mention that PEP 333 is approaching 
> > finalization, and if anybody who works on the server subsystems of Zope 
> > (e.g. ZServer, zope.server, etc.) could take a look at it, that would be 
> > good.
> > 
> > PEP 333 is a proposed standard for interaction between Python web 
> > applications/frameworks and a Python web server or gateway.  The idea is 
> > that an application or framework that is PEP 333-compliant can run in 
> > any web server that is PEP 333-compliant.
> > 
> > In principle, if ZServer/zope.server and ZPublisher/zope.publisher were 
> > modified to use a PEP 333-based interface as their communication 
> > machinery, then ZServer would in principle be able to run any PEP 
> > 333-based application, and Zope applications would in principle be able 
> > to run in any PEP 333-compliant server.
> > 
> > I'm not asking if anybody's willing to implement such a refactoring 
> > right now, but I would appreciate it if someone with the requisite 
> > experience can look over the PEP to see whether there are any major 
> > issues that would prevent such a refactoring later, so that the PEP can 
> > be revised if needed.  The URL is:
> > 
> >     http://www.python.org/peps/pep-0333.html
> 
> This looks good.  I can't see any obvious reason why there should be
> a problem either. It will be hard to tell until someone actually tried to
> implement the spec.
> 
> I'd love to see someone create a wrapper for zope.publisher that
> provides a WSGI application.
> 
> ...
> 
> > However, I'm only looking at the direct implementation issues, not 
> > configuration issues, compatibility with other servers' threading 
> > models, etc.  So, it would be nice if someone could read the PEP with an 
> > eye to what I might have missed.  Thanks!
> 
> I don't have time for more than an initial read.  The next step for me
> would be to try to implement something, but I don't have time for that
> either.
> 
> Given that people are creating server implementations for Twisted and
> mod_python, there would be great value in having someone build an application
> implementation for Zope and trying it out with Twisted and mod_python.
> 
> Jim
> 


More information about the Zope3-dev mailing list