|
|
@@ -1,100 +0,0 @@ |
|
|
|
import Queue |
|
|
|
import threading |
|
|
|
import sys |
|
|
|
import contextlib |
|
|
|
|
|
|
|
# This file provides a context manager that converts a function |
|
|
|
# that takes a callback into a generator that returns an iterable. |
|
|
|
# This is done by running the function in a new thread. |
|
|
|
|
|
|
|
# Based partially on http://stackoverflow.com/questions/9968592/ |
|
|
|
|
|
|
|
class IteratorizerThread(threading.Thread): |
|
|
|
def __init__(self, queue, function, curl_hack): |
|
|
|
""" |
|
|
|
function: function to execute, which takes the |
|
|
|
callback (provided by this class) as an argument |
|
|
|
""" |
|
|
|
threading.Thread.__init__(self) |
|
|
|
self.name = "Iteratorizer-" + function.__name__ + "-" + self.name |
|
|
|
self.function = function |
|
|
|
self.queue = queue |
|
|
|
self.die = False |
|
|
|
self.curl_hack = curl_hack |
|
|
|
|
|
|
|
def callback(self, data): |
|
|
|
try: |
|
|
|
if self.die: |
|
|
|
raise Exception() # trigger termination |
|
|
|
self.queue.put((1, data)) |
|
|
|
except: |
|
|
|
if self.curl_hack: |
|
|
|
# We can't raise exceptions, because the pycurl |
|
|
|
# extension module will unconditionally print the |
|
|
|
# exception itself, and not pass it up to the caller. |
|
|
|
# Instead, just return a value that tells curl to |
|
|
|
# abort. (-1 would be best, in case we were given 0 |
|
|
|
# bytes, but the extension doesn't support that). |
|
|
|
self.queue.put((2, sys.exc_info())) |
|
|
|
return 0 |
|
|
|
raise |
|
|
|
|
|
|
|
def run(self): |
|
|
|
try: |
|
|
|
result = self.function(self.callback) |
|
|
|
except: |
|
|
|
self.queue.put((2, sys.exc_info())) |
|
|
|
else: |
|
|
|
self.queue.put((0, result)) |
|
|
|
|
|
|
|
@contextlib.contextmanager |
|
|
|
def Iteratorizer(function, curl_hack = False): |
|
|
|
""" |
|
|
|
Context manager that takes a function expecting a callback, |
|
|
|
and provides an iterable that yields the values passed to that |
|
|
|
callback instead. |
|
|
|
|
|
|
|
function: function to execute, which takes a callback |
|
|
|
(provided by this context manager) as an argument |
|
|
|
|
|
|
|
with iteratorizer(func) as it: |
|
|
|
for i in it: |
|
|
|
print 'callback was passed:', i |
|
|
|
print 'function returned:', it.retval |
|
|
|
""" |
|
|
|
queue = Queue.Queue(maxsize = 1) |
|
|
|
thread = IteratorizerThread(queue, function, curl_hack) |
|
|
|
thread.daemon = True |
|
|
|
thread.start() |
|
|
|
|
|
|
|
class iteratorizer_gen(object): |
|
|
|
def __init__(self, queue): |
|
|
|
self.queue = queue |
|
|
|
self.retval = None |
|
|
|
|
|
|
|
def __iter__(self): |
|
|
|
return self |
|
|
|
|
|
|
|
def next(self): |
|
|
|
(typ, data) = self.queue.get() |
|
|
|
if typ == 0: |
|
|
|
# function has returned |
|
|
|
self.retval = data |
|
|
|
raise StopIteration |
|
|
|
elif typ == 1: |
|
|
|
# data is available |
|
|
|
return data |
|
|
|
else: |
|
|
|
# callback raised an exception |
|
|
|
raise data[0], data[1], data[2] |
|
|
|
|
|
|
|
try: |
|
|
|
yield iteratorizer_gen(queue) |
|
|
|
finally: |
|
|
|
# Ask the thread to die, if it's still running. |
|
|
|
thread.die = True |
|
|
|
while thread.isAlive(): |
|
|
|
try: |
|
|
|
queue.get(True, 0.01) |
|
|
|
except: # pragma: no cover |
|
|
|
pass |