[Python-Dev] Withdrawn PEP 288 and thoughts on PEP 342
Phillip J. Eby
pje at telecommunity.com
Fri Jun 17 06:07:22 CEST 2005
At 08:03 PM 6/16/2005 -0700, Guido van Rossum wrote:
>Someone should really come up with some realistic coroutine examples
>written using PEP 342 (with or without "continue EXPR").
How's this?
def echo(sock):
while True:
try:
data = yield nonblocking_read(sock)
yield nonblocking_write(sock, data)
except ConnectionLost:
pass
def run_server(sock, handler):
while True:
connected_socket = yield nonblocking_accept(sock)
schedule_coroutine(handler(connected_socket))
schedule_coroutine(
run_server(
setup_listening_socket("localhost","echo"),
echo
)
Of course, I'm handwaving a lot here, but this is a much clearer example
than anything I tried to pull out of the coroutines I've written for actual
production use. That is, I originally started this email with a real
routine from a complex multiprocess application doing lots of IPC, and
quickly got bogged down in explaining all the details of things like
yielding to semaphores and whatnot. But I can give you that example too,
if you like.
Anyway, the handwaving above is only in explanation of details, not in
their implementability. It would be pretty straightforward to use
Twisted's callback facilities to trigger next() or throw() calls to resume
the coroutine in progress. In fact, schedule_coroutine is probably
implementable as something like this in Twisted:
def schedule_coroutine(geniter, *arg):
def resume():
value = geniter.next(*arg)
if value is not None:
schedule_coroutine(value)
reactor.callLater(0, resume)
This assumes, of course, that you only yield between coroutines. A better
implementation would need to be more like the events.Task class in
peak.events, which can handle yielding to Twisted's "Deferreds" and various
other kinds of things that can provide callbacks. But this snippet is
enough to show that yield expressions let you write event-driven code
without going crazy writing callback functions.
And of course, you can do this without yield expressions today, with a
suitably magic function, but it doesn't read as well:
yield nonblocking_accept(sock); connected_socket = events.resume()
This is how I actually do this stuff today. 'events.resume()' is a magic
function that uses sys._getframe() to peek at the argument passed to the
equivalent of 'next()' on the Task that wraps the
generator. events.resume() can also raise an error if the equivalent of
'throw()' was called instead.
With yield expressions, the code in those Task methods would just do
next(arg) or throw(*sys.exc_info()) on the generator-iterator, and
'events.resume()' and its stack hackery could go away.
More information about the Python-Dev
mailing list