[Web-SIG] [PEP 444] Future- and Generator-Based Async Idea

P.J. Eby pje at telecommunity.com
Sat Jan 8 20:57:36 CET 2011


At 01:24 PM 1/8/2011 -0500, Paul Davis wrote:
>For contrast, I thought it might be beneficial to have a comparison
>with an implementation that didn't use async might look like:
>
>http://friendpaste.com/4lFbZsTpPGA9N9niyOt9PF

Compare your version with this one, that uses my revision of Alice's proposal:

def my_awesome_application(environ):
     # do stuff
     yield b'200 OK', [], ["Hello, World!"]

def my_middleware(app):
     def wrapper(environ):
         # maybe edit environ
         try:
             status, headers, body = yield app(environ)
             # maybe edit response:
             # body = latinize(body)
             yield status, headers, body
         except:
             # maybe handle error
         finally:
             # maybe release resources

def my_server(app, httpreq):
     environ = wsgi.make_environ(httpreq)

     def process_response(result):
         status, headers, body = result
         write_headers(httpreq, status, headers)
         Coroutine(body, body_trampoline, finish_response)

     def finish_response(result):
         # cleanup, if any

     Coroutine(app(environ), app_trampoline, process_response)


The primary differences are that the server needs to split some of 
its processing into separate routines, and response-processing done 
by middleware has to happen in a while loop rather than a for loop.


>If your implementation requires that people change source code (yield
>vs return) when they move code between sync and async servers, doesn't
>that pretty much violate the main WSGI goal of portability?

The idea here would be to have WSGI 2 use this protocol exclusively, 
not to have two different protocols.


>IMO, the async middleware is quite more complex than the current state
>of things with start_response.

Under the above proposal, it isn't, since you can't (only) do a for 
loop over the response body; you have to write a loop and a 
push-based handler as well.  In this case, it is reduced to just 
writing one loop.

I'm still not entirely convinced of the viability of the approach, 
but I'm no longer in the "that's just crazy talk" category regarding 
an async WSGI.  The cost is no longer crazy, but there's still some 
cost involved, and the use case rationale hasn't improved much.

OTOH, I can now conceive of actually *using* such an async API for 
something, and that's no small feat.  Before now, the idea held 
virtually zero interest for me.


>Either way this proposal reminds me quite a bit of Duff's device [1].
>On its own Duff's device is quite amusing and could even be employed
>in some situations to great effect. On the other hand, any WSGI spec
>has to be understandable and implementable by people from all skill
>ranges. If its a spec that only a handful of people comprehend, then I
>fear its adoption would be significantly slowed in practice.

Under my modification of Alice's proposal, nearly all of the 
complexity involved migrates to the server, mostly in the (shareable) 
Coroutine implementation.

For an async server, the "arrange for coroutine(result) to be called" 
operations are generally native to async APIs, so I'd expect them to 
find that simple to implement.  Synchronous servers just need to 
invoke the waited-on operation synchronously, then pass the value 
back into the coroutine.  (e.g. by returning "pause" from the 
trampoline, then calling coroutine(value, exc_info) to resume 
processing after the result is obtained.)




More information about the Web-SIG mailing list