Search code examples
pythontornado

How do I stop Tornado web server?


I've been playing around a bit with the Tornado web server and have come to a point where I want to stop the web server (for example during unit testing). The following simple example exists on the Tornado web page:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

Once tornado.ioloop.IOLoop.instance().start() is called, it blocks the program (or current thread). Reading the source code for the IOLoop object gives this example in the documentation for the stop function:

To use asynchronous methods from otherwise-synchronous code (such as
unit tests), you can start and stop the event loop like this:
  ioloop = IOLoop()
  async_method(ioloop=ioloop, callback=ioloop.stop)
  ioloop.start()
ioloop.start() will return after async_method has run its callback,
whether that callback was invoked before or after ioloop.start.

However, I have no idea how to integrate this into my program. I actually have a class that encapsulates the web server (having it's own start and stop functions), but as soon as I call start, the program (or tests) will of course block anyway.

I've tried to start the web server in another process (using the multiprocessing package). This is the class that is wrapping the web server:

class Server:
    def __init__(self, port=8888):
        self.application = tornado.web.Application([ (r"/", Handler) ])

        def server_thread(application, port):
            http_server = tornado.httpserver.HTTPServer(application)
            http_server.listen(port)
            tornado.ioloop.IOLoop.instance().start()

        self.process = Process(target=server_thread,
                               args=(self.application, port,))

    def start(self):
        self.process.start()

    def stop(self):
        ioloop = tornado.ioloop.IOLoop.instance()
        ioloop.add_callback(ioloop.stop)

However, stop does not seem to entirely stop the web server since it is still running in the next test, even with this test setup:

def setup_method(self, _function):
    self.server = Server()
    self.server.start()
    time.sleep(0.5)  # Wait for web server to start

def teardown_method(self, _function):
    self.kstore.stop()
    time.sleep(0.5)

How can I start and stop a Tornado web server from within a Python program?


Solution

  • I just ran into this and found this issue myself, and using info from this thread came up with the following. I simply took my working stand alone Tornado code (copied from all the examples) and moved the actual starting code into a function. I then called the function as a threading thread. My case different as the threading call was done from my existing code where I just imported the startTornado and stopTornado routines.

    The suggestion above seemed to work great, so I figured I would supply the missing example code. I tested this code under Linux on a FC16 system (and fixed my initial type-o).

    import tornado.ioloop, tornado.web
    
    class Handler(tornado.web.RequestHandler):
        def get(self):
            self.write("Hello, world")
    
    application = tornado.web.Application([ (r"/", Handler) ])
    
    def startTornado():
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    
    def stopTornado():
        tornado.ioloop.IOLoop.instance().stop()
    
    if __name__ == "__main__":
        import time, threading
        threading.Thread(target=startTornado).start()
        print "Your web server will self destruct in 2 minutes"
        time.sleep(120)
        stopTornado()
    

    Hope this helps the next person.