Search code examples
pythonasynchronoustornadocoroutine

Can't get raise Return to work when using coroutines


I've been experimenting with Python 2.7 and Tornado 3.2. I've been trying to get a simple coroutine example to work, but without much luck:

import tornado.web
from tornado.gen import coroutine
from tornado.httpclient import AsyncHTTPClient
from tornado.gen import Return

class MainHandler(tornado.web.RequestHandler):

    # Tried with and without @asynchronous
    @tornado.web.asynchronous
    def get(self):
            data = MainService().get_google_data() 
            self.write(data)


class MainService:

    @coroutine
    def get_google_data(self):
            response = yield AsyncHTTPClient().fetch("http://www.google.com")
            raise Return(value = 'hello')

I expected this would write out 'hello' when cURLing the URL. Instead, I get:

...
File "/vagrant/venv/lib/python2.7/site-packages/tornado/web.py", line 656, in write
raise TypeError("write() only accepts bytes, unicode, and dict objects")
TypeError: write() only accepts bytes, unicode, and dict objects

Apparently, a Future is being returned, but calling result() on the future throws another exception: DummyFuture does not support blocking for results

The Tornado docs say that in order to return a value from a coroutine, you raise a Return exception. Looking at the source code, that indeed seems to be what's expected. Yet, when I run it, it doesn't seem to work.

Appreciate any insights on this!


Solution

  • You need to yield the call to get_google_data():

    class MainHandler(tornado.web.RequestHandler):
    
        @coroutine
        def get(self):
            data = yield MainService().get_google_data() 
            self.write(data)
    

    Tornado coroutines always return a Future. You wait for the result of that Future by calling yield on it. Without the yield, you just end up getting the Future back immediately, without waiting for the coroutine to complete. You should also use the @coroutine decorator on the get method, in addition to get_google_data. The @asynchronous decorator is generally used if you want to use callbacks, rather than coroutines.