I'm trying to get my head around Tornado. I'm writing a chat application backed by mongodb and I'm using motor
for non-blocking access to it.
What I'm trying to achieve is:
- Create a decorator that uses
motor
to asynchronously pull the user's record from mongo
- Validate their credentials (username & token)
- Create another decorator that checks that the user_id retrieved in 1. above is permitted access to the chat room. This requires another asynchronous call to mongo with
motor
to retrieve the 'ChatRoom' record.
- Subscribe to the chat room if all is OK
I have decorator 1. working (basically taken from http://tornadogists.org/5251927/):
def authenticated_async(f):
@functools.wraps(f)
@gen.engine
def wrapper(self, *args, **kwargs):
self.current_user = yield gen.Task(self.get_current_user_async)
if self.current_user:
logging.info("User '%s' (%s) successfully authenticated" %
(self.current_user['username'],
self.current_user['_id']))
f(self, *args, **kwargs)
else:
raise tornado.web.HTTPError(401, "User not authenticated, "
"aborting")
return wrapper
The trouble is that for the second decorator, I need to access self.current_user
. Because this is set in an asynchronous callback, it's not available when I get into my validation
decorator (i.e the validation decorator is called before the auth decorator completes).
Is it just not possible for me to use decorators in this way with asynchronous functions? Do I just need to call the validation method inside the above method after making sure that self.current_user
is True so it's more like a callback?
I'd ideally like to have my methods in my Handler wrapped with both of these decorators so I can reuse them elsewhere, i.e.:
class ChatSocketHandler(tornado.websocket.WebSocketHandler):
@gen.coroutine
@validate_invitation_access_async
@authenticated_async
def open(self, invitation_id):
# do stuff here...
Update
In fact, there is no dependency. user_id is provided as a parameter, and that could be used to run both decorators in parallel - one to confirm authentication, the other to see whether the user with that ID is allowed access to the room. The open()
method would then only proceed if self.auth_check == True and self.room_check == True
.
Could open()
ever be called before the async decorators complete though?