Get current user Async in Tornado

1.2k views Asked by At

When I use get_current_user() I need to check few things in Redis (use tornado-redis) asynchronously.

I am doing the following:

def authenticated_async(method):

    @gen.coroutine
    def wrapper(self, *args, **kwargs):
        self._auto_finish = False
        self.current_user = yield gen.Task(self.get_current_user_async)
        if not self.current_user:
            self.redirect(self.reverse_url('login'))
        else:
            result = method(self, *args, **kwargs) # updates
            if result is not None:
                yield result
    return wrapper

class BaseClass():

    @gen.coroutine
    def get_current_user_async(self,):

        auth_cookie = self.get_secure_cookie('user') # cfae7a25-2b8b-46a6-b5c4-0083a114c40e
        user_id = yield gen.Task(c.hget, 'auths', auth_cookie) # 16
        print(123, user_id)
        return auth_cookie if auth_cookie else None

For example, I want to use authenticated_async decorator:

class IndexPageHandler(BaseClass, RequestHandler):

    @authenticated_async
    def get(self):
        self.render("index.html")

But I have in console only 123.

Whats wrong? How to fix that?

Thanks!

UPDATE

I have updated the code with yield result.

In auth_cookie I have cfae7a25-2b8b-46a6-b5c4-0083a114c40e.

Then I go to terminal:

127.0.0.1:6379> hget auths cfae7a25-2b8b-46a6-b5c4-0083a114c40e
"16"

So,

user_id = yield gen.Task(c.hget, 'auths', auth_cookie)
print(123, user_id)

Must return

123 16

But it returns one 123

UPDATE 1

With

class IndexPageHandler(BaseClass, RequestHandler):
    @gen.coroutine
    def get(self):
        print('cookie', self.get_secure_cookie('user'))
        user_async = yield self.get_current_user_async()
        print('user_async', user_async)
        print('current user', self.current_user)
        self.render("index.html",)

In console I have:

cookie b'cfae7a25-2b8b-46a6-b5c4-0083a114c40e'
123 
user_async b'cfae7a25-2b8b-46a6-b5c4-0083a114c40e'
current user None
3

There are 3 answers

0
Ben Darnell On BEST ANSWER

get_secure_cookie() returns a byte string; since the cookie printed out with a b' prefix you must be on Python 3. On Python 3, tornado-redis appears to expect unicode strings instead of byte strings; any input that is not a unicode string will be converted to one with the str() function. This adds the b' prefix seen above, so you are querying for b'cfae7a25-2b8b-46a6-b5c4-0083a114c40e', not cfae7a25-2b8b-46a6-b5c4-0083a114c40e

The solution is to convert the cookie to a str before sending it to redis: auth_cookie = tornado.escape.native_str(auth_cookie)

1
NeoWang On

Taking a look at the doc string of gen.Task:

Takes a function (and optional additional arguments) and runs it with those arguments plus a callback keyword argument.

Does c.hget accept a callback argument? If not, the callback will throw an exception, the future won't be set a result, hence the print statement doesn't print the user id.

0
Danver Braganza On

The answer is back in your wrapper coroutine.

        if result is not None:
            yield result

You don't want to yield result, but to "return" it from the coroutine. However, since Python doesn't let you use a non-empty return from a generator, you need to raise it as a wrapped exception that Tornado can unwrap, and pass the result on to the caller (get_current_user)

if result is not None:
    raise gen.Return(result)

Having done that, you should find it works.