How to use async tornado API inside tornado.wsgi.WSGIContainer?

1.6k Views Asked by At

I try to use custom WSGIContainer that should work with asynchronous operations:

from tornado import httpserver, httpclient, ioloop, wsgi, gen

@gen.coroutine
def try_to_download():
    response = yield httpclient.AsyncHTTPClient().fetch("http://www.stackoverflow.com/")
    raise gen.Return(response.body)


def simple_app(environ, start_response):
    res = try_to_download()

    print 'done: ', res.done()
    print 'exec_info: ', res.exc_info()

    status = "200 OK"
    response_headers = [("Content-type", "text/html")]
    start_response(status, response_headers)
    return ['hello world']


container = wsgi.WSGIContainer(simple_app)
http_server = httpserver.HTTPServer(container)
http_server.listen(8888)
ioloop.IOLoop.instance().start()

But it isn't work. It seems that application does not wait try_to_download function result. Also code below doesn't work:

from tornado import httpserver, httpclient, ioloop, wsgi, gen


@gen.coroutine
def try_to_download():
    yield gen.Task(httpclient.AsyncHTTPClient().fetch, "http://www.stackoverflow.com/")


def simple_app(environ, start_response):

    res = try_to_download()
    print 'done: ', res.done()
    print 'exec_info: ', res.exc_info()

    status = "200 OK"
    response_headers = [("Content-type", "text/html")]
    start_response(status, response_headers)
    return ['hello world']


container = wsgi.WSGIContainer(simple_app)
http_server = httpserver.HTTPServer(container)
http_server.listen(8888)
ioloop.IOLoop.instance().start()

Do you have any ideas why it isn't works? Python version that I use is 2.7.

P.S. You may ask me why I don't want to use native tornado.web.RequestHandler. The main reason is that I have custom python library (WsgiDAV) that produce WSGI interface and allows to write custom adapters which I what to make asynchronous.

1

There are 1 best solutions below

2
A. Jesse Jiryu Davis On

WSGI doesn't work with async.

In general, for a function to wait for a Tornado coroutine to finish, the function itself must be a coroutine and must yield the coroutine's result:

@gen.coroutine
def caller():
    res = yield try_to_download()

But of course a WSGI function like simple_app cannot be a coroutine because WSGI doesn't understand coroutines. A more thorough explanation of the incompatibility between WSGI and async is in the Bottle documentation.

If you must support WSGI, don't use Tornado's AsyncHTTPClient, use a synchronous client like the standard urllib2 or PyCurl instead. If you must use Tornado's AsyncHTTPClient, don't use WSGI.