8

I am attempting to implement get_current_user in the RequestHandler for Tornado, but I need the call to block while waiting on the asynchronous call to my database. Decorating the call with @tornado.web.asynchronous will not work because either way the get_current_user method returns before the async query completes and the query callback is executed.

For example:

class MyHandler(BaseHandler):
  @tornado.web.asynchronous
  @tornado.web.authenticated
  def get(self):
    self.write('example')
    self.finish()

class BaseHandler(tornado.web.RequestHandler):
  def get_current_user(self):
    def query_cb(self, doc):
      return doc or None

    database.get(username='test', password='t3st', callback=query_cb)

@tornado.web.authenticated calls get_current_user, but always receives "None" because the BaseHandler does not have time to respond. Is there a way, using tornado, to temporarily block for a call such as the one above?

Jarrod
  • 81
  • 1
  • 3

3 Answers3

4

Do a blocking database operation instead of the non blocking described above (There is a blocking mysql lib shipped with tornado).

From the Tornado wiki page about threads and concurrency: "Do it synchronously and block the IOLoop. This is most appropriate for things like memcache and database queries that are under your control and should always be fast. If it's not fast, make it fast by adding the appropriate indexes to the database, etc."

https://github.com/facebook/tornado/wiki/Threading-and-concurrency

Schildmeijer
  • 20,702
  • 12
  • 62
  • 79
  • I am basically asking if there is a way to block at the tornado level because my database api for couchdb is asynchronous and has no non-blocking calls. I prefer this for the rest of the site except for this single operation. That is why I am asking. – Jarrod Feb 08 '11 at 22:20
  • Thanks. I know I can switch libraries, but I prefer to remain non-blocking through most of my code. The other method is definitely an option, but creates a lot of code in the handlers, because then on_auth has to have another async call, etc, etc. I know what I want may not be possible -- I am just ranting. – Jarrod Feb 08 '11 at 22:43
2

How about having get_current_user return a Future that you signal when the asynchronous response from your database is returned?

class BaseHandler(tornado.web.RequestHandler):
    def get_current_user(self):
        future = Future()
        def query_cb(user):
            future.set_result(user or None)
        database.get(username='test', password='t3st', callback=query_cb)
        return future


class MainHandler(BaseHandler):
    @gen.coroutine
    def get(self):
        user = yield self.get_current_user()
        self.write('user: ' + user)
        # ... actual request processing
Myk Willis
  • 12,306
  • 4
  • 45
  • 62
0

I thought Tornado allowed you to make either blocking or non-blocking requests.

Here is Tornado being used for both: https://bitbucket.org/nephics/tornado-couchdb/src/147579581b47/couch.py

Disclaimer: I know very little of Python and Tornado.

dgo.a
  • 2,634
  • 23
  • 35