[Python-Dev] Withdrawn PEP 288 and thoughts on PEP 342
Phillip J. Eby
pje at telecommunity.com
Fri Jun 17 06:41:43 CEST 2005
At 12:07 AM 6/17/2005 -0400, Phillip J. Eby wrote:
> def schedule_coroutine(geniter, *arg):
> def resume():
> value = geniter.next(*arg)
> if value is not None:
> schedule_coroutine(value)
> reactor.callLater(0, resume)
Oops. I just realized that this is missing a way to return a value back to
a calling coroutine, and that I also forgot to handle exceptions:
def schedule_coroutine(coroutine, stack=(), *args):
def resume():
try:
if len(args)==3:
value = coroutine.throw(*args)
else:
value = coroutine.next(*args)
except:
if stack:
# send the error back to the "calling" coroutine
schedule_coroutine(stack[0], stack[1], *sys.exc_info())
else:
# Nothing left in this pseudothread, let the
# event loop handle it
raise
if isinstance(value,types.GeneratorType):
# Yielded to a specific coroutine, push the current
# one on the stack, and call the new one with no args
schedule_coroutine(value, (coroutine,stack))
elif stack:
# Yielded a result, pop the stack and send the
# value to the caller
schedule_coroutine(stack[0], stack[1], value)
# else: this pseudothread has ended
reactor.callLater(0, resume)
There, that's better. Now, if a coroutine yields a coroutine, the yielding
coroutine is pushed on a stack. If a coroutine yields a non-coroutine
value, the stack is popped and the value returned to the
previously-suspended coroutine. If a coroutine raises an exception, the
stack is popped and the exception is thrown to the previously-suspended
coroutine.
This little routine basically replaces a whole bunch of code in peak.events
that manages a similar coroutine stack right now, but is complicated by the
absence of throw() and next(arg); the generators have to be wrapped by
objects that add equivalent functionality, and the whole thing gets a lot
more complicated as a result.
Note that we could add a version of the above to the standard library
without using Twisted. A simple loop class could have a deque of
"callbacks to invoke", and the reactor.callLater() could be replaced by
appending the 'resume' closure to the deque. A main loop function would
then just peel items off the deque and call them, looping until an
unhandled exception (such as SystemExit) occurs, or some other way of
indicating an exit occurs.
More information about the Python-Dev
mailing list