I have a python generator function which yields chunks of text. I would like to write a get
method for a tornado.web.RequestHandler
subclass that will iterate over the generator, writing the chunks out to the response as it goes.
Since this is Tornado, and since the generator may take over a second to process, I thought it would be nice to make the handler asynchronous, using this generator as a co-routine and passing off control to the IOLoop after every chunk. However, I can't make heads or tails of how to do this.
Here's my example (blocking) code:
class TextHandler(web.RequestHandler):
@web.asynchronous
def get(self, n):
generator = self.generate_text(100000)
# Clearly, this will block. How to make it asynchronous?
for text in generator:
self.write(text)
def generate_text(n):
for x in xrange(n):
if not x % 15:
yield "FizzBuzz\n"
elif not x % 5:
yield "Buzz\n"
elif not x % 3:
yield "Fizz\n"
else:
yield "%s\n" % x
How can I make this handler work asynchronously?
It is also possible to use the new tornado's gen interface to async processes:
Here's a basic version of what you are describing. To avoid blocking you can pass your generator to the IOLoop via a callback function. The trick here is since you are not using a process that does actual IO and so has no os level process/file handler to add to the IOLoop via
add_handler
, you can instead use a simpleadd_callback
call and call it repeatedly from within the callback function to keep the function in the IOLoop callback queue until the generator has finished.