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

2011-01-08 Thread Alice Bevan–McGregor
Warning: this assumes we're running on bizzaro-world PEP 444 that 
mandates applications are generators.  Please do not dismiss this idea 
out of hand but give it a good look and maybe some feedback.  ;)


--

Howdy!

I've finished touching up the p-code illustrating my idea of using 
generators to implement async functionality within a WSGI application 
and middleware, including the idea of a wsgi2ref-supplied decorator to 
simplify middleware.


https://gist.github.com/770743

There may be a few typos in there; I switched from the idea of passing 
back the returned value of the future to passing back the future itself 
in order to better handle exception handling (i.e. not requiring utter 
insanity in the middleware to determine the true source of an exception 
and the need to pass it along).


The second middleware demonstration (using a decorator) makes 
middleware look a lot more like an application: yielding futures, or a 
response, with the addition of yielding an application callable not 
explored in the first (long, but trivial) example.  I believe this 
should cover 99% of middleware use cases, including interactive 
debugging, request routing, etc. and the syntax isn't too bad, if you 
don't mind standardized decorators.


This should be implementable within the context of Marrow HTTPd 
(http://bit.ly/fLfamO) without too much difficulty.


As a side note, I'll be adding threading support to the server 
(actually, marrow.server, the underlying server/protocol abstraction 
m.s.http utilizes) using futures some time over the week-end by 
wrapping the async callback that calls the application with a call to 
an executor, making it immune to blocking, but I suspect the overhead 
will outweigh the benefit for speedy applications.


Testing multi-process vs. multi-threaded using 2 workers each and the 
prime calculation example, threading is 1.5x slower for CPU-intensive 
tasks under Python 2.7.  That's terrible.  It should be 2x; I have 2 
cores.  :/


- Alice.


___
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


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

2011-01-08 Thread Alice Bevan–McGregor
As a quick note, this proposal would signifigantly benefit from the 
simplified syntax offered by PEP 380 (Syntax for Delegating to a 
Subgenerator) [1] and possibly PEP 3152 (Cofunctions) [2].  The former 
simplifies delegation and exception passing, and the latter simplifies 
the async side of this.


Unfortunately, AFIK, both are affected by PEP 3003 (Python Language 
Moratorium) [3], which kinda sucks.


- Alice.

[1] http://www.python.org/dev/peps/pep-0380/
[2] http://www.python.org/dev/peps/pep-3152/
[3] http://www.python.org/dev/peps/pep-3003/


___
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


[Web-SIG] WSGI-1 Warts

2011-01-08 Thread David Stanek
Is there a list somewhere of the warts that need to be fixed? I found
some in this list's archive and on various blogs, but I was hoping to
find a more comprehensive grouping of them. This would help in
evaluating PEP 444.

-- 
David
blog: http://www.traceback.org
twitter: http://twitter.com/dstanek
___
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


Re: [Web-SIG] PEP 444 feature request - Futures executor

2011-01-08 Thread Alice Bevan–McGregor

On 2011-01-07 09:47:12 -0800, Timothy Farrell said:

However, I'm requesting that servers _optionally_ provide 
environ['wsgi.executor'] as a futures executor that applications can 
use for the purpose of doing something after the response is fully sent 
to the client.  This is feature request is designed to be concurrency 
methodology agnostic.


Done.  (In terms of implementation, not updating PEP 444.)  :3

The Marrow server now implements a thread pool executor using the 
concurrent.futures module (or equiv. futures PyPi package).  The 
following are the commits; the changes will look bigger than they are 
due to cutting and pasting of several previously nested blocks of code 
into separate functions for use as callbacks.  100% unit test coverage 
is maintained (without errors), an example application is added, and 
the benchmark suite updated to support the definition of thread count.


http://bit.ly/gUL33v
http://bit.ly/gyVlgQ

Testing this yourself requires Git checkouts of the 
marrow.server/threading branch and marrow.server.http/threading branch, 
and likely the latest marrow.io from Git as well:


https://github.com/pulp/marrow.io
https://github.com/pulp/marrow.server/tree/threaded
https://github.com/pulp/marrow.server.http/tree/threaded

This update has not been tested under Python 3.x yet; I'll do that 
shortly and push any fixes; I doubt there will be any.


On 2011-01-08 03:26:28 -0800, Alice Bevan–McGregor said in the [PEP 
444] Future- and Generator-Based Async Idea thread:


As a side note, I'll be adding threading support to the server... but I 
suspect the overhead will outweigh the benefit for speedy applications.


I was surprisingly quite wrong in this prediction.  The following is 
the output of a C25 pair of benchmarks, the first not threaded, the 
other with 30 threads  (enough so there would be no waiting).


https://gist.github.com/770893

The difference is the loss of 60 RSecs out of 3280.  Note that the 
implementation I've devised can pass the concurrent.futures executor to 
the WSGI application (and, in fact, does), fufilling the requirements 
of this discussion.  :D


The use of callbacks internally to the HTTP protocol makes a huge 
difference in overhead, I guess.


- Alice.


___
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


Re: [Web-SIG] WSGI-1 Warts

2011-01-08 Thread Alice Bevan–McGregor

On 2011-01-08 07:22:44 -0800, David Stanek said:

I'm going to take some time this weekend to create a consolidated list. 
I was hoping to find something like:


Issue: 
Discussion: http://
Summary of resolution: ...


I agree; that would be very, very nice to have, though it might be 
helpful (esp. considering the length some of these discussions go to 
and the mixing of ideas within single threads) to mirror the message 
nesting as a series of nested lists (if doing this in HTML) to more 
concisely collect posts vs. pointing to the head of a thread and having 
to go through literally everything.  Of course, that's more work, and 
should be restricted to threads that are, in fact, scattered or 
unfocused.


And that first sentance was waaay too long, indicating that I've now 
been up all night.  :(


- Alice.


___
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


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

2011-01-08 Thread P.J. Eby

I made a few errors in that massive post...

At 12:00 PM 1/8/2011 -0500, P.J. Eby wrote:
My major concern about the approach is still that it requires a fair 
amount of overhead on the part of both app developers and middleware 
developers, even if that overhead mostly consists of importing and 
decorating.  (More below.)


The above turned out to be happily wrong by the end of the post, 
since no decorators or imports are actually required for app and 
middleware developers.




You can then implement response-processing middleware like this:

def latinize_body(body_iter):
while True:
chunk = yield body_iter
if chunk is None:
break
else:
yield piglatin(yield body_iter)


The last line above is incorrect; it should've been yield 
piglatin(chunk), i.e.:


def latinize_body(body_iter):
while True:
chunk = yield body_iter
if chunk is None:
break
else:
yield piglatin(chunk)

It's still rather unintuitive, though.  There are also plenty of 
topics left to discuss, both of the substantial and bikeshedding varieties.


One big open question still in my mind is, are these middleware 
idioms any easier to get right than the WSGI 1 ones?  For things that 
don't process response bodies, the answer seems to be yes: you just 
stick in a yield and you're done.


For things that DO process response bodies, however, you have to have 
ugly loops like the one above.


I suppose it could be argued that, as unintuitive as that 
body-processing loop is, it's still orders of magnitude more 
intuitive than a piece of WSGI 1 middleware that has to handle both 
application yields and write()s!


I suppose my hesitance is due to the fact that it's not as simple as:

return (piglatin(chunk) for chunk in body_iter)

Which is really the level of simplicity that I was looking 
for.  (IOW, all response-processing middleware pays in this 
slightly-added complexity to support the subset of apps and 
response-processing middleware that need to wait for events during 
body output.)


___
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


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

2011-01-08 Thread P.J. Eby

At 05:39 AM 1/8/2011 -0800, Alice Bevan­McGregor wrote:
As a quick note, this proposal would signifigantly benefit from the 
simplified syntax offered by PEP 380 (Syntax for Delegating to a 
Subgenerator) [1] and possibly PEP 3152 (Cofunctions) [2].  The 
former simplifies delegation and exception passing, and the latter 
simplifies the async side of this.


Unfortunately, AFIK, both are affected by PEP 3003 (Python Language 
Moratorium) [3], which kinda sucks.


Luckily, neither PEP is necessary, since we do not need to support 
arbitrary protocols for the subgenerators being called.  This makes 
it possible to simply yield instead of yield from, and the 
trampoline functions take care of distinguishing a terminal 
(return) result from an intermediate one.


The Coroutine class I suggested, however, *does* accept explicit 
returns via raise StopIteration(value), so it is actually fully 
equivalent to supporting yield from, as long as it's used with an 
appropriate trampoline function.


(In fact, the structure of the Coroutine class I proposed was stolen 
from an earlier Python-Dev post I did in an attempt to show why PEP 
380 was unnecessary for doing coroutines.  ;-) )


In effect, the only thing that PEP 380 would add here is the syntax 
sugar for 'raise StopIteration(value)', but you can do that with:


def return_(value):
raise StopIteration(value)

In any case, my suggestion doesn't need this for either apps or 
response bodies, since the type of data yielded suffices to indicate 
whether the value is a return or not.  You only need a subgenerator 
to raise StopIteration if you want to return something to your caller 
that *isn't* a response or body chunk.


___
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


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

2011-01-08 Thread Paul Davis
On Sat, Jan 8, 2011 at 6:26 AM, Alice Bevan–McGregor
al...@gothcandy.com wrote:
 Warning: this assumes we're running on bizzaro-world PEP 444 that mandates
 applications are generators.  Please do not dismiss this idea out of hand
 but give it a good look and maybe some feedback.  ;)

 --

 Howdy!

 I've finished touching up the p-code illustrating my idea of using
 generators to implement async functionality within a WSGI application and
 middleware, including the idea of a wsgi2ref-supplied decorator to simplify
 middleware.

        https://gist.github.com/770743

 There may be a few typos in there; I switched from the idea of passing back
 the returned value of the future to passing back the future itself in order
 to better handle exception handling (i.e. not requiring utter insanity in
 the middleware to determine the true source of an exception and the need to
 pass it along).

 The second middleware demonstration (using a decorator) makes middleware
 look a lot more like an application: yielding futures, or a response, with
 the addition of yielding an application callable not explored in the first
 (long, but trivial) example.  I believe this should cover 99% of middleware
 use cases, including interactive debugging, request routing, etc. and the
 syntax isn't too bad, if you don't mind standardized decorators.

 This should be implementable within the context of Marrow HTTPd
 (http://bit.ly/fLfamO) without too much difficulty.

 As a side note, I'll be adding threading support to the server (actually,
 marrow.server, the underlying server/protocol abstraction m.s.http utilizes)
 using futures some time over the week-end by wrapping the async callback
 that calls the application with a call to an executor, making it immune to
 blocking, but I suspect the overhead will outweigh the benefit for speedy
 applications.

 Testing multi-process vs. multi-threaded using 2 workers each and the prime
 calculation example, threading is 1.5x slower for CPU-intensive tasks under
 Python 2.7.  That's terrible.  It should be 2x; I have 2 cores.  :/

        - Alice.


 ___
 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/paul.joseph.davis%40gmail.com


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

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?

IMO, the async middleware is quite more complex than the current state
of things with start_response. The ability to subtly miss invoking the
generator, or invoking it too many times and dropping part of a
response. Forcing every middleware to unwrap iterators and handle
their own StopExceptions is worrisome as well.

I can't decide if casting the complexity of the async middleware as a
side benefit of discouraging authors was a joke or not.

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.


[1] http://en.wikipedia.org/wiki/Duff's_device
___
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


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

2011-01-08 Thread P.J. Eby

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.)



___
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


Re: [Web-SIG] Server-side async API implementation sketches

2011-01-08 Thread Alice Bevan–McGregor

On 2011-01-08 17:22:44 -0800, Alex Grönholm said:


On 2011-01-08 13:16:52 -0800, P.J. Eby said:

I've written the sketches dealing only with PEP 3148 futures, but 
sockets were also proposed, and IMO there should be simple support for 
obtaining data from wsgi.input.


I'm a bit unclear as to how this will work with async. How do you 
propose that an asynchronous application receives the request body?


In my example https://gist.github.com/770743 (which has been simplified 
greatly by P.J. Eby in the Future- and Generator-Based Async Idea 
thread) for dealing with wsgi.input, I have:


   future = environ['wsgi.executor'].submit(environ['wsgi.input'].read, 4096)
   yield future

While ugly, if you were doing this, you'd likely:

submit = environ['wsgi.executor'].submit
input_ = environ['wsgi.input']

   future = yield submit(input_.read, 4096)
   data = future.

That's a bit nicer to read, and simplifies things if you need to make a 
number of async calls.


The idea here is that:

:: Your async server subclasses ThreadPoolExecutor.

:: The subclass overloads the submit method.

:: Your submit method detects bound methods on wsgi.input, sockets, and files.

:: If one of the above is detected, create a mock future that defines 
'fd' and 'operation' attributes or similar.


:: When yielding the mock future, your async reactor can detect 'fd' 
and do the appropriate thing for your async framework.  (Generally 
adding the fd to the appropriate select/epoll/kqueue readers/writers 
lists.)


:: When the condition is met, set_running_or_notify_cancel (when 
internally reading or writing data), set_result, saving the value, and 
return the future (filled with its data) back up to the application.


:: The application accepts the future instance as the return value of 
yield, and calls result across it to get the data.  (Obviously writes, 
if allowed, won't have data, but reads will.)


I hope that clearly identifies my idea on the subject.  Since async 
servers will /already/ be implementing their own executors, I don't see 
this as too crazy.


- Alice.


___
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


Re: [Web-SIG] Server-side async API implementation sketches

2011-01-08 Thread P.J. Eby

At 04:40 AM 1/9/2011 +0200, Alex Grönholm wrote:

09.01.2011 04:15, Alice Bevan­McGregor kirjoitti:
I hope that clearly identifies my idea on the subject. Since async 
servers will /already/ be implementing their own executors, I don't 
see this as too crazy.
-1 on this. Those executors are meant for executing code in a thread 
pool. Mandating a magical socket operation filter here would 
considerably complicate server implementation.


Actually, the *reverse* is true.  If you do it the way Alice 
proposes, my sketches don't get any more complex, because the 
filtering goes in the executor facade or submit function.


Truthfully, I don't really see the point of exposing the map() method 
(which is the only other executor method we'd expose), so it probably 
makes more sense to just offer a 'wsgi.submit' key...  which can be a 
function as follows:


  def submit(callable, *args, **kw):
  ob = getattr(callable, '__self__', None)
  if isinstance(ob, ServerProvidedSocket):  # could be an ABC
   future = MockFuture()
   if callable==ob.read:
   # set up read callback to fire future
   elif callable==ob.write:
   # set up write callback to fire future
   return future
  else:
  return real_executor.submit(callable, *args, **kw)

Granted, this might be a rather long function.  However, since it's 
essentially an optimization, a given server can decide how many 
functions can be shortcut in this way.  The spec may wish to offer a 
guarantee or recommendation for specific methods of certain 
stdlib-provided types (sockets in particular) and wsgi.input.


Personally, I do think it might be *better* to offer extended 
operations on wsgi.input that could be used via yield, e.g. yield 
input.nb_read().  But of course then the trampoline code has to 
recognize those values instead of futures.  Either way works, but 
somewhere there is going to be some type-testing (explicit or 
implicit) taking place to determine how to suspend and resume the app.


Note, too, that this complexity also only affects servers that want 
to offer a truly async API.  A synchronous server has no reason to 
pay particular attention to what's in a future, since it can't offer 
any performance improvement.


I do think that this sort of API discussion, though, is the most 
dangerous part of trying to do an async spec.  That is, I don't 
expect that everyone will spontaneously agree on the exact same 
API.  Alice's proposal (simply submitting object methods) has the 
advantage of severely limiting the scope of API discussions.  ;-)


___
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


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

2011-01-08 Thread Graham Dumpleton
On 9 January 2011 12:16, Alice Bevan–McGregor al...@gothcandy.com wrote:
 On 2011-01-08 09:00:18 -0800, P.J. Eby said:

 (The next interesting challenge would be to integrate this withGraham's
 proposal for adding cleanup handlers...)

 class MyApplication(object):
   def __init__(self):
       pass # process startup code

   def __call__(self, environ):
       yield None # must be a generator
       pass # request code

   def __enter__(self):
       pass # request startup code

   def __exit(exc_type, exc_val, exc_tb):
       pass # request shutdown code -- regardless of exceptions

 We could mandate context managers!  :D  (Which means you can still wrap a
 simple function in @contextmanager.)

Context managers don't solve the problem I am trying to address. The
'with' statement doesn't apply context managers to WSGI application
objects in way that is desirable and use of a decorator to achieve the
same means having to replace close() which is what am trying to avoid
because of extra complexity that causes for WSGI middleware just to
make sure wsgi.file_wrapper works. We want a world where it should
never be necessary for WSGI middleware, or proxy decorators, to have
to fudge up a generator and override the close() chain to add
cleanups.

Graham

        - Alice.


 ___
 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/graham.dumpleton%40gmail.com

___
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


Re: [Web-SIG] Server-side async API implementation sketches

2011-01-08 Thread P.J. Eby

At 06:15 PM 1/8/2011 -0800, Alice Bevan­McGregor wrote:

On 2011-01-08 17:22:44 -0800, Alex Grönholm said:

On 2011-01-08 13:16:52 -0800, P.J. Eby said:
I've written the sketches dealing only with PEP 3148 futures, but 
sockets were also proposed, and IMO there should be simple support 
for obtaining data from wsgi.input.
I'm a bit unclear as to how this will work with async. How do you 
propose that an asynchronous application receives the request body?


In my example https://gist.github.com/770743 (which has been 
simplified greatly by P.J. Eby in the Future- and Generator-Based 
Async Idea thread) for dealing with wsgi.input, I have:


   future = environ['wsgi.executor'].submit(environ['wsgi.input'].read, 4096)
   yield future

While ugly, if you were doing this, you'd likely:

submit = environ['wsgi.executor'].submit
input_ = environ['wsgi.input']

   future = yield submit(input_.read, 4096)
   data = future.


I don't quite understand the above -- in my sketch, the above would be:

data = yield submit(input._read, 4096)

It looks like your original sketch wants to call .result() on the 
future, whereas in my version, the return value of yielding a future 
is the result (or an error is thrown if the result was an error).


Is there some reason I'm missing, for why you'd want to explicitly 
fetch the result in a separate step?


Meanwhile, thinking about Alex's question, ISTM that if WSGI 2 is 
asynchronous, then the wsgi.input object should probably just have 
read(), readline() etc. methods that simply return (possibly-mock) 
futures.  That's *much* better than having to do all that submit() 
crud just to read data from wsgi.input().


OTOH, if you want to use the cgi module to parse a form POST from the 
input, you're going to need to write an async version of it in that 
case, or else feed the entire operation to an executor...  but then 
the methods would need to be synchronous...  *argh*.


I'm starting to not like this idea at all.  Alex has actually 
pinpointed a very weak spot in the scheme, which is that if 
wsgi.input is synchronous, you destroy the asynchrony, but if it's 
asynchronous, you can't use it with any normal code that operates on a stream.


I don't see any immediate fixes for this problem, so I'll let it 
marinate in the back of my mind for a while.  This might be the 
achilles heel for the whole idea of a low-rent async WSGI.


___
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


Re: [Web-SIG] Server-side async API implementation sketches

2011-01-08 Thread Alex Grönholm

09.01.2011 05:45, P.J. Eby kirjoitti:

At 06:15 PM 1/8/2011 -0800, Alice Bevan­McGregor wrote:

On 2011-01-08 17:22:44 -0800, Alex Grönholm said:

On 2011-01-08 13:16:52 -0800, P.J. Eby said:
I've written the sketches dealing only with PEP 3148 futures, but 
sockets were also proposed, and IMO there should be simple support 
for obtaining data from wsgi.input.
I'm a bit unclear as to how this will work with async. How do you 
propose that an asynchronous application receives the request body?


In my example https://gist.github.com/770743 (which has been 
simplified greatly by P.J. Eby in the Future- and Generator-Based 
Async Idea thread) for dealing with wsgi.input, I have:


   future = 
environ['wsgi.executor'].submit(environ['wsgi.input'].read, 4096)

   yield future

While ugly, if you were doing this, you'd likely:

submit = environ['wsgi.executor'].submit
input_ = environ['wsgi.input']

   future = yield submit(input_.read, 4096)
   data = future.


I don't quite understand the above -- in my sketch, the above would be:

data = yield submit(input._read, 4096)

It looks like your original sketch wants to call .result() on the 
future, whereas in my version, the return value of yielding a future 
is the result (or an error is thrown if the result was an error).
I cooked up a simple do-nothing middleware example which Alice decorated 
with some comments:

https://gist.github.com/771398

A new feature here is that the application itself yields a (status, 
headers) tuple and then chunks of the body (or futures).



Is there some reason I'm missing, for why you'd want to explicitly 
fetch the result in a separate step?


Meanwhile, thinking about Alex's question, ISTM that if WSGI 2 is 
asynchronous, then the wsgi.input object should probably just have 
read(), readline() etc. methods that simply return (possibly-mock) 
futures.  That's *much* better than having to do all that submit() 
crud just to read data from wsgi.input().


OTOH, if you want to use the cgi module to parse a form POST from the 
input, you're going to need to write an async version of it in that 
case, or else feed the entire operation to an executor...  but then 
the methods would need to be synchronous...  *argh*.


I'm starting to not like this idea at all.  Alex has actually 
pinpointed a very weak spot in the scheme, which is that if wsgi.input 
is synchronous, you destroy the asynchrony, but if it's asynchronous, 
you can't use it with any normal code that operates on a stream.
I liked the idea of having a separate async_read() method in wsgi.input, 
which would set the underlying socket in nonblocking mode and return a 
future. The event loop would watch the socket and read data into a 
buffer and trigger the callback when the given amount of data has been 
read. Conversely, .read() would set the socket in blocking mode. What 
kinds of problems would this cause?



I don't see any immediate fixes for this problem, so I'll let it 
marinate in the back of my mind for a while.  This might be the 
achilles heel for the whole idea of a low-rent async WSGI.


___
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/alex.gronholm%40nextday.fi


___
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