Proposal for adding Shallow Threads and a Main Loop to Python

Rhamphoryncus rhamph at gmail.com
Fri Mar 18 10:45:19 EST 2005


Diez B. Roggisch wrote:
> Hi,
>
> a few questions:
>
> > A shallow thread is just a generator modified in the most obvious
way
> > possible.  The yield statement is replaced with a waitfor
expression.
> > You give it the object you wish to "wait for".  Then when it's
ready
> > you get back a return value or an exception.  These waitfor
expressions
> > are the only points where your shallow thread may get suspended, so
> > it's always explicit.  If you call another function it will be
treated
> > exactly as a normal function call from a generator (this is why
they're
> > 'shallow'), so there's no issues with calling C functions.
> >
> > On the other end of things, your shallow thread object would have
> > __resume__ and __resumeexc__ methods (or perhaps a single
__resume__
> > method with a default raise_=False argument).  They return a tuple
of
> > (mode,value) where mode is a string of 'waitfor', 'exception', or
> > 'return' and value corresponds to that.  These methods will be used
by
> > a main loop to resume the shallow thread, like next() is used with
a
> > generator.
>
> So basically a shallow thread could be written in today's python like
this?
>
> class ShallowThread(object):
>     def __init__(self, data=None):
>         self.data = data
>
>     def run(self, initial_data=None):
>         while True:
>             ....
>             yield <some_state>
>             new_data = self.data
>             ....
>
> st = ShallowThread().run(<some_initial_data>)
> while True:
>     result = st.next()
>     st.data = <some_subsequent_data>
>
> Basically <some_state> is your above mentioned tuple, and waitfor is
like
> yield with a return value that I modeled with explicit state called
data.
> Is that correct so far?

Yes, that's the general idea.  I would give the following example
however, using Twisted 2.0's deferredGenerator facility (and assuming
all the functions I depend on are still modified the same way, just for
a fair comparison).

from twisted.internet.defer import *
from twisted.internet import reactor

def get_and_save(path):
    thingy = waitForDeferred(urllib.urlopen(path, async=True))
    yield thingy
    infile = thingy.getResult()
    thingy = waitForDeferred(open(split('/')[-1], async=True)
    yield thingy
    outfile = thingy.getResult()
    thingy = waitForDeferred(infile.read(async=True))
    yield thingy
    data = thingy.getResult()
    thingy = waitForDeferred(outfile.write(data, async=True))
    yield thingy
    thingy.getResult()  # Still needed to raise exceptions
    infile.close()
    outfile.close()
get_and_save = deferredGenerator(get_and_save)

def main():
    a = get_and_save("http://python.org/pics/PyBanner021.gif")
    b = get_and_save("http://python.org/pics/pythonHi.gif")
    c = get_and_save("http://python.org/pics/PythonPoweredSmall.gif")

    thingy = waitForDeferred(allDone(a, b, c))
    yield thingy
    thingy.getResult()
main = deferredGenerator(main)

if __name__ == "__main__":
    d = main()
    d.addBoth(lambda _: reactor.stop())
    reactor.run()

>
> > import mainloop, urllib
> >
> > def get_and_save(path):
> >     infile = waitfor urllib.urlopen(path, async=True)
> >     outfile = waitfor open(path.split('/')[-1], async=True)
> >     waitfor outfile.write(waitfor infile.read(async=True),
async=True)
> >     infile.close()
> >     outfile.close()
> >
> > def main():
> >     a = get_and_save("http://python.org/pics/PyBanner021.gif")
> >     b = get_and_save("http://python.org/pics/pythonHi.gif")
> >     c =
get_and_save("http://python.org/pics/PythonPoweredSmall.gif")
> >
> >     waitfor allDone(a, b, c)
> >
> > if __name__ == "__main__":
> >     mainloop.runUntil(main())
> >
> > Well there you have it.  I've glossed over many details but they
can be
> > cleared up later.  What I need to know now is how everybody else
thinks
> > about it.  Is this something you would use?  Does it seem like the
> > right way to do it?  And of course the all important one, can I get
it
> > in to python core?  <0.5 wink>
>
> I've difficulties grasping where the actual work is done - the event
> notifier thingies are sort of generators themselves, and the mainloop
gets
> them and calls some execute method on them?

It depends.  For files, the mainloop would have something like a
select-based polling loop.  Once something is ready, it would set the
appropriate event notifier to the "done" state and notify everything
waiting on it.


> And now the final $1000,0000.00 question - why all this? No offense
intended
> - it's a little bit more comfortable than the generators approach
sketched
> by others (e.g. David Mertz if I recall corretly) - but to my view,
it
> _could_ be done in today python because we have generators. Or not?

Go and compare the two get_and_save functions.  Would you call that a
"little bit more comfortable"?  Now imagine the function was 20 lines
to begin with, involving a couple loops and some list comprehensions.
Then just for kicks, imagine it without generators, instead turning it
into a state machine.

Yes, you can *technically* do it using generators (and they're what
make the implementation easy).  But you could also do it using a state
machine.  Doesn't mean it's practical though.

--
Adam Olsen, aka Rhamphoryncus




More information about the Python-list mailing list