[Web-SIG] PEP 444 / WSGI 2 Async
Alice Bevan–McGregor
alice at gothcandy.com
Fri Jan 7 10:02:49 CET 2011
On 2011-01-06 10:15:19 -0800, Antoine Pitrou said:
> Alice Bevan–McGregor <alice at ...> writes:
>>> Er, for the record, in Python 3 non-blocking file objects return None when
>>> read() would block.
>>
>> -1
>>
>> I'm aware, however that's not practically useful. How would you detect
>> from within the WSGI 2 application that the file object has become
>> readable? Implement your own async reactor / select / epoll loop?
>> That's crazy talk! ;)
>
> I was just pointing out that if you need to choose a convention for
> signaling blocking reads on a non-blocking object, it's already there.
I don't. I need a way to suspend execution of a WSGI application
pending some operation, often waiting for socket or file read or write
availability. (Just as often something entirely unrelated to file
descriptors, see my previous post from a few moments ago.)
> By the way, an event loop is the canonical implementation of
> asynchronous programming, so I'm not sure what you're complaining
> about. Or perhaps you're using "async" in a different meaning? (which
> one?)
If you use non-blocking sockets, and the WSGI server provides a way to
directly access the client socket (ack!), utilizing the none response
on reads would require you to utilize a tight loop within your
application to wait for actual data. That's really, really bad, and in
a single-threaded server, deadly.
> I don't understand why you want a "yield" at this level. IMHO, WSGI
> needn't involve generators. A higher-level wrapper (framework,
> middleware, whatever) can wrap fd-waiting in fancy generator stuff if
> so desired. Or, in some other environments, delegate it to a reactor
> with callbacks and deferreds. Or whatever else, such as futures.
WSGI already involves generators: the response body. In fact, the
templating engine I wrote (and extended to support flush semantics)
utilizes a generator to return the response body. Works like a hot
damn, too.
Yield is the Python language's native way to suspend execution of a
callable in a re-entrant way. A trivial example of this is an async
"ping-pong" reactor. I wrote one ("you aren't a real Python programmer
unless...") as an experiment and utilize it for server monitoring with
tasks being generally scheduled against time, vs. edge-triggered or
level-triggered fd operation availability.
Everyone has their own idea of what a "deferred" is, and there is only
one definition of a "future", which (in a broad sense) is the same as
the general idea of a "deferred". Deferreds just happen to be
implementation-specific and often require rewriting large portions of
external libraries to make them compatible with that specific deferred
implementation. That's not a good thing.
Hell; an extension to the futures spec to handle file descriptor events
might not be a half-bad idea. :/
> By the way, the concurrent.futures module is new. Though it will be
> there in 3.2, it's not guaranteed that its API and semantics will be
> 100% stable while people start to really flesh it out.
Ratification of PEP 444 is a long way off itself. Also, Alex Grönholm
maintains a pypi backport of the futures module compatible with 2.x+
(not sure of the specific minimum version) and < 3.2. I'm fairly
certain deprecation warnings wouldn't kill the usefulness of that
implementation. Worrying about instability, at this point, may be
premature.
>> +1 for pure futures which (in theory) eliminate the need for dedicated
>> async versions of absolutely everything at the possible cost of
>> slightly higher overhead.
>
> I don't understand why futures would solve the need for a low-level
> async facility.
You mis-interpreted; I didn't mean to infer that futures would replace
an async core reactor, just that long-running external library calls
could be trivially deferred using futures.
> You still need to define a way for the server and the app to wake each
> other (and for the server to wake multiple apps).
Futures is a pretty convienent way to have a server wake an app; using
a future completion callback wrapped (using partial) with the paused
application generator would do it. (The reactor Marrow uses, a
modified Tornado IOLoop, would require calling
reactor.add_callback(partial(worker, app_gen)) followed by
reactor._wake() in the future callback.)
"Waking up the server" would be accomplished by yielding a futures
instance (or fd magical value, etc).
> This isn't done "naturally" in Python (except perhaps with stackless or
> greenlets). Using fds give you well-known flexible possibilities.
Yield is the natural way for one side of that, re-entering the
generator on future completion covers the other side. Stackless and
greenlets are alternate ideas, but yield is built-in (and soon, so will
futures).
> If you want to put the futures API in WSGI, think of the poor authors
> of a WSGI server written in C who will have to write their own executor
> and future implementation. I'm sure they have better things to do.
If they embed a Python interpreter via C, they can utilize native
implementations of future executors, though these will obviously be
slightly less performant than a native C implementation. (That is,
unless the stdlib version in 3.2 will have C backing.)
- Alice.
More information about the Web-SIG
mailing list