Turning f(callback) into a generator
Peter Otten
__peter__ at web.de
Fri Dec 5 13:42:22 EST 2003
Jeff Epler wrote:
> On Wed, Dec 03, 2003 at 06:07:08PM -0800, Jimmy Retzlaff wrote:
>> My solution used threads. I'd love to know if someone comes up with a
>> solution not involving threads. Here's a simple example of my
>> thread-based approach:
> [...]
>
> In standard Python, this is the only approach that can work. It's
> possible that Stackless Python might provide a solution that doesn't
> involve OS threads, but I don't have any actual experience with
> Stackless.
>
> One problem with your code is the use of a sentinel value. Another
> poster suggested using multiple queues and a timeout to return
> exceptions or end-of-queue. Instead, you should use a queue but insert
> easily distinguishable items in it. For instance:
> None: end of iterator, raise StopIteration
> (0, a, b): exception in f, raise a, None, b (re-raise exception)
> (1, blah): callback got blah, return it from next()
>
>
> import sys, Queue, threading
>
> class CallbackGenerator:
> def __init__(self, func, pre=(), post=()):
> self.func = func
> if type(pre) is not tuple: pre = (pre,)
> if type(post) is not tuple: post = (post,)
> self.args = pre + (self.cb,) + post
> self.queue = Queue.Queue(maxsize=1)
> self.thread = threading.Thread(target=self.go)
> self.thread.start()
>
> def __iter__(self): return self
>
> def next(self):
> item = self.queue.get()
> if item is None:
> raise StopIteration
> if item[0] == 0:
> raise item[1], None, item[2]
> else:
> return item[1]
>
> def go(self):
> try:
> self.func(*self.args)
> except:
> info = sys.exc_info()
> self.queue.put((0, info[1], info[2]))
> else:
> self.queue.put(None)
>
> def cb(self, *args):
> self.queue.put((1, args))
>
> # Example 1: os.path.walk -> generator
> import os
> for i in CallbackGenerator(os.path.walk, "/tmp", None):
> print i
>
> # Example 2: Prints 1, 2 then shows a traceback
> def f(cb):
> cb(1)
> cb(2)
> 1/0
>
> for i in CallbackGenerator(f):
> print i
Your code looks far more concise than mine.
However, it suffers from the same problem:
# nasty example
maxRepeat = 0
for i in CallbackGenerator(os.path.walk, "/tmp", None):
if maxRepeat == 0:
break
print i
maxRepeat -= 1
How do you handle the case where the generator is not run to exhaustion?
Peter
More information about the Python-list
mailing list