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