Search code examples
pythontornadotornado-motor

How can I run multiple instances of Tornado's IOLoop?


I have basic web server written in Tornado. Now, I want to add some functionalities that require a database connection. I have chosen MongoDB with Motor library. After following official tutorial relevant part of my main.py looks something like:

class LoginHandler(BaseHandler):

    async def get(self):

        db = self.settings['db']
        collection = db.test

        async def do_find_one():
            document =  await collection.find_one({"ime" : "stefan"})
            pprint.pprint(document)

        self.render('login.html')



def main():
    parse_command_line()

    client = motor.motor_tornado.MotorClient('localhost',27017)
    db = client.test
    collection = db.test

    async def do_find_one():
        document =  await collection.find_one({"name" : "John"})
        pprint.pprint(document)

    settings = {
        "template_path": os.path.join(os.path.dirname(__file__),"templates"),
        "cookie_secret": "random_value_goes_here",
        "xsrf_cookies": True,
        "login_url": "/login",
        "db" : db,
        "debug" : True
    }

    app = tornado.web.Application([
        (r'/', WelcomeHandler),
        (r'/login', LoginHandler)
        ],
        **settings
    )

    app.listen(options.port)
    tornado.ioloop.IOLoop.current().start()


if __name__ == "__main__":
    main()

Following tutorial, it states that I can now run do_find_one using IOLoop.current().run_sync(do_find_one). However, if I do it as such:

tornado.ioloop.IOLoop.current().start()
tornado.ioloop.IOLoop.current().run_sync(do_find_one)

it doesn't change anything, and no adequate result is visible, but if switch lines, like:

tornado.ioloop.IOLoop.current().run_sync(do_find_one)
tornado.ioloop.IOLoop.current().start()

I get the following error:

Traceback (most recent call last):
  File "main.py", line 108, in <module>
    main()
  File "main.py", line 102, in main
    tornado.ioloop.IOLoop.current().run_sync(do_find_one)
NameError: name 'do_find_one' is not defined

Should I define do_find_one() function in main, and then just await it in get method of LoginHandler?

How can I allow server to work, and simultaneously manipulate on database?

Can theese IOLoops run on same thread, or I need to create multiple threads (one per IOLoop or such)?


Solution

  • When the loop has started, you should be able to just await the async function without having to know which is the current asyncio loop.

    async def do_find_one(collection):
        document = await collection.find_one({"ime": "stefan"})
        pprint.pprint(document)
        return 8
    
    
    class LoginHandler(BaseHandler):
        async def get(self):
            db = self.settings["db"]
            val = await do_find_one(collection=db.test)
            print(val)
            self.render("login.html")
    
    
    handlers = [(r"/", WelcomeHandler), (r"/login", LoginHandler)]
    
    
    def make_app(settings):
        return tornado.web.Application(handlers, **settings)
    
    
    def main():
        parse_command_line()
        client = motor.motor_tornado.MotorClient("localhost", 27017)
        db = client.test
    
        settings = {
            "template_path": os.path.join(os.path.dirname(__file__), "templates"),
            "cookie_secret": "random_value_goes_here",
            "xsrf_cookies": True,
            "login_url": "/login",
            "db": db,
            "debug": True,
        }
        app = make_app(settings)
        app.listen(options.port)
        tornado.ioloop.IOLoop.current().start()
    
    
    if __name__ == "__main__":
        main()