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!
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.