[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