On his blog, Graham mentioned some skepticism about skipping WSGI 1.1 and going straight to 2.0, due to concern that people using write() would need to make major code changes to go to WSGI 2.0.

Now, if we ignore the part of the spec that says "New WSGI applications and frameworks *should not* use the write() callable if it is possible to avoid doing so," there does need to be some reasonable way for those people to make their apps work with the newer spec. On CPython at least, this can be implemented using greenlets, and on other Python implementations it could be done with threads. Here's a quick and dirty, untested sketch (no error checking, no version handling) of how it could be done with greenlets:

def two_to_one(app):
    def wrapper(environ):
        buffer = []
        header = []

        def start_response(status, headers):
            header.append(status)
            header.append(headers)
            return write

        def write(data):
            buffer.append(data)
            greenlet.getcurrent().parent.switch(None)

        child = greenlet(app)
        response = child.switch(environ, start_response)

        if not header:
            # XXX start_response wasn't called, error!

        if not buffer:
            # write wasn't called, so just pass it through
            return header[0], header[1], response

        def yield_all():
            response = None
            try:
                while buffer:
                    yield buffer.pop(0)
                    response = child.switch()
                # XXX check for response being non-empty
            finally:
                if hasattr(response, 'close'):
                    response.close()

        return header[0], header[1], yield_all()

    return wrapper

As you can see, I've stuck in some XXX comments for where there should be more error checking or handling, and there would probably be some other additions as well. However, this adapter handles both write()-using and non-write()-using WSGI 1 apps, and converts them to the WSGI 2 calling convention, by making the write() function call perform a non-local return from the application.

Doing this with threads would be similar, but there are more design decisions to make, i.e., will you use a single worker thread that you send requests to, or just start a new thread for each request? In either case, the start_response() and write() in that thread would simply write data to a Queue.Queue that's read by the adapter. The code running in the other thread would handle closing the app's response (if need be), after piping all the app's output to the queue. You'd also need to decide if you're going to support interrupting the application (e.g. by returning an error from write(), or by calling throw() on a generator) if the wrapper is closed before its time.

(Of course, none of these shenanigans are necessary for well-behaved apps and frameworks that don't use write(); the above adapter would lose its yield_all function and all the greenlet usage, substituting some error raise code for the body of write() in that case.)

_______________________________________________
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com

Reply via email to