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

Ron Adam ron3200 at gmail.com
Tue Nov 1 06:10:46 CET 2011


On Mon, 2011-10-31 at 23:58 -0500, Ron Adam wrote:
> 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)

LOL... Need to recheck my cut and copy between edits.

Remove the nonlocal self.suspend.  It was just to see what if anything
acted different and I forgot to remove it.

> 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.

Ok, the self.suspend reference does work as it should.  I was just not
putting my print statement in the right place.  Time to call it a night.

The rest is ok.

Cheers,
   Ron


> 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)                 # stop 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