[Python-ideas] Cofunctions - Getting away from the iterator protocol

Ron Adam ron3200 at gmail.com
Tue Nov 1 05:58:26 CET 2011


On Tue, 2011-11-01 at 11:14 +1000, Nick Coghlan wrote:
> On Tue, Nov 1, 2011 at 10:22 AM, Ron Adam <ron3200 at gmail.com> wrote:
> > On Tue, 2011-11-01 at 10:06 +1300, Greg Ewing wrote:
> >> Ron Adam wrote:
> >> > If we put some strict requirements on the idea.
> >> >
> >> >     1. Only have a *SINGLE* exception type as being resumable.
> >> >
> >> >     2. That exception should *NEVER* occur naturally.
> >> >
> >> >     3. Only allow continuing after it's *EXPLICITLY RAISED* by a
> >> >        raised statement.
> >> >
> >> > All of the problem issues go away with those requirements in place, and
> >> > you only have the issue of how to actually write the patch.  Earlier
> >> > discussions indicated, it might not be that hard to do.
> >>
> >> I'm not familiar with these earlier discussions. Did they go
> >> as far as sketching a feasible implementation? It's all very
> >> well to propose things like this, but the devil is very much
> >> in the details.
> >
> > Yeah, there isn't very much about the details, but I think it is worth
> > looking into as it would pretty much does exactly what is needed. (IMHO)
> 
> It gave me another thought on an existing utility worth exploring in
> this context: pdb's post-mortem capabilities.
> 
> Now, those *don't* implement coroutines (when you do a postmortem, you
> end up in an emulation of the eval loop, not the eval loop itself).
> However, that exception instance *does* contain the full frame stack,
> all the way down to where the exception was thrown. Figuring out what
> hooks you would need in the core eval loop in order to reinstate an
> exception's frame stack as the "real" frame stack might be an
> interesting exercise.

Poking around a bit, it looks like 'raise' does most of the work and the
exception is just an envelope for what ever 'raise' puts in it.  Is that
right?



I'd like to be able to make this work.

class Suspend:
    def __init__(self, source):
        self.source = source
        self.suspend = True

    def __next__(self):
	nonlocal self.suspend
        if self.suspend:
            self.suspend = False
            raise SuspendException
        self.suspend = True
        return next(self.source)

There are two issues with it...

The "self.suspend = False" doesn't seem to work. The __next__ seems to
get it's own copies of the attributes at the time the generator is
created.

And after the SuspendException is raised, a StopIteratoion is issued on
the next next() call.  The StopIteration is from the whole chain.  The
only reason the scheduler doesn't stop is it catches the
Suspendexception.

I want to be able to stick something like this in the generator chained
pipe example below.

Cheers,
   Ron



*This is broken down into finer steps than you would normally do in
order to test how it behaves.

"""
   Basic scheduler test -- co-pipes version
"""
from co_pipes import *

def Person(args):
    name, count = args
    p = Producer(lambda:name)           # call function each time
    p = Limit(p, count)                 # exit after count yields.
    p = Enumerate(p)                    # -> (n, data)
    #p = Suspend(p)                     # suspend doesn't work.
    p = Apply(p, "{0[0]}: {0[1]} ".format)
    p = Apply(p, print)                 # consumer
    for _ in p:
        yield                           # pull data from here.

def main(data):
    p = Source(data)               # take an iterable
    p = Apply(p, Person)           # apply a callable to data
    p = Collect(p)                 # collect items in a list
    p = Scheduler(p)               # take a list of generators.
    next(p)                        # start the scheduler.
    
if __name__ == "__main__":
    data = [("John", 2), ("Micheal", 3), ("Terry", 4)]
    main(data)


Prints...

1: John 
1: Micheal 
1: Terry 
2: John 
2: Micheal 
2: Terry 
3: Micheal 
3: Terry 
4: Terry 







More information about the Python-ideas mailing list