Search code examples
pythontornadotornado-motor

When will yield actually yield in the function call stack?


I am working on tornado and motor in python 3.4.3.

I got three files. Lets name it like main.py, model.py, core.py

I have three functions, one in each...

main.py

def getLoggedIn(request_handler):
    # request_handler = tornado.web.RequestHandler()
    db = request_handler.settings["db"]
    uid = request_handler.get_secure_cookie("uid")
    result = model.Session.get(db, uid=uid)
    return result.get("_id", None) if result else None

model.py

@classmethod
    def get(cls, db, user_id=None, **kwargs):
        session = core.Session(db)
        return session.get(user_id, **kwargs)

core.py

@gen.coroutine
    def get(self, user_id, **kwargs):
        params = kwargs
        if user_id:
            params.update({"_id": ObjectId(user_id)}) #This does not exist in DB
        future = self.collection.find_one(params)
        print(future) #prints <tornado.concurrent.Future object at 0x04152A90>
        result = yield future
        print(result) #prints None
        return result

The calls look like getLoggedIn => model.get => core.get

core.get is decorated with @gen.coroutine and I call yield self.collection.find_one(params) The print(result) prints None but if I return result and try to print the return value in getLoggedIn function it prints .

I believe this is related to asynchronous nature of tornado and the print gets called before yield but I am not sure. It would be a great help if someone could explain about coroutine/generators principles and behavior in different possible cases.


Solution

  • Every call to a coroutine must be yielded, and the caller must also be a coroutine. So getLoggedIn must be a coroutine that calls:

    result = yield model.Session.get(db, uid=uid)
    

    And so on. See my article on refactoring Tornado coroutines for a detailed example and explanation.