16

I am new to tornado web server. When I start the tornado web server using python main_tornado.py It is working. Please see the below code.

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()

When I stop the server using CTRL+C it gave the following error.

    ^CTraceback (most recent call last):
  File "main_tornado.py", line 19, in <module>
    tornado.ioloop.IOLoop.instance().start()
  File "/home/nyros/Desktop/NewWeb/venv/lib/python3.2/site-packages/tornado/ioloop.py", line 301, in start
    event_pairs = self._impl.poll(poll_timeout)
KeyboardInterrupt

Please solve my problem. Thanks..

dhana
  • 6,487
  • 4
  • 40
  • 63

4 Answers4

29

You can stop Tornado main loop with tornado.ioloop.IOLoop.instance().stop(). To have this method called after passing signal with Ctrl+C you can periodically check global flag to test if main loop should end and register handler for SIGINT signal which will change value of this flag:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import signal
import logging

import tornado.ioloop
import tornado.web
import tornado.options


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


class MyApplication(tornado.web.Application):
    is_closing = False

    def signal_handler(self, signum, frame):
        logging.info('exiting...')
        self.is_closing = True

    def try_exit(self):
        if self.is_closing:
            # clean up here
            tornado.ioloop.IOLoop.instance().stop()
            logging.info('exit success')


application = MyApplication([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    tornado.options.parse_command_line()
    signal.signal(signal.SIGINT, application.signal_handler)
    application.listen(8888)
    tornado.ioloop.PeriodicCallback(application.try_exit, 100).start()
    tornado.ioloop.IOLoop.instance().start()

Output:

$ python test.py 
[I 181209 22:13:43 web:2162] 200 GET / (127.0.0.1) 0.92ms
^C[I 181209 22:13:45 test:21] exiting...
[I 181209 22:13:45 test:28] exit success

UPDATE

I've just saw in question Tornado long polling requests this simple solution:

try:
    tornado.ioloop.IOLoop.instance().start()
except KeyboardInterrupt:
    tornado.ioloop.IOLoop.instance().stop()

Obviously, this is a less safe way.


UPDATE

Edited the code to remove use of global.

Nykakin
  • 8,657
  • 2
  • 29
  • 42
  • Thanks for reply. Your code is perfect. I am using tornado httpserver.When I press ctrl+c then it is take long time for kill the server. Any ideas. – dhana Jun 14 '13 at 10:56
  • You probably have some blocking task which has to be ended first before `try_exit()` callback is called. You can try to use `tornado.ioloop.IOLoop.instance().stop()` directly in signal handler but it seems much less secure for me. If you want to break infinite loop brutally in the middle of processing it will be hard to avoid error messages. – Nykakin Jun 14 '13 at 11:22
  • The original solution (handling signal.SIGINT) works great for me on Windows, but the "UPDATE" to your answer (simply catching KeyboardInterrupt) does not. Ctrl+C on Windows will not generate the KeyboardInterrupt and some people (like me, with a compact keyboard) don't have a CTRL+Pause Break on their keyboard. – Myk Willis Jan 20 '15 at 19:04
  • I'm not an expert in python but the use of a global variable in a scripting language is maybe a bad idea... why you don't do `def signal_handler(signum, frame): tornado.ioloop.IOLoop.instance().stop()` directly ? signals are events, you don't need a flag... anyway, none of the solutions work for me (Boot2Docker on Windows) – DestyNova Aug 04 '15 at 12:06
  • 1
    try: tornado.ioloop.IOLoop.instance().start() except KeyboardInterrupt: tornado.ioloop.IOLoop.instance().stop() does not work for me. – Josh Usre May 09 '17 at 19:41
  • On Windows, if we pass `debug=True` when creating `Application`, I found that there is no way to kill the Python instance. Save the file and the server automatic restart even though I have Ctrl-C it before (on VSCode intergrated terminal). The only way is to kill it in Task Manager. – shioko Apr 19 '20 at 01:29
8

You can simply stop the Tornado ioloop from a signal handler. It should be safe thanks to add_callback_from_signal() method, the event loop will exit nicely, finishing any eventually concurrently running task.

import tornado.ioloop
import tornado.web
import signal

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

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

def sig_exit(signum, frame):
    tornado.ioloop.IOLoop.instance().add_callback_from_signal(do_stop)

def do_stop(signum, frame):
    tornado.ioloop.IOLoop.instance().stop()

if __name__ == "__main__":
    application.listen(8888)
    signal.signal(signal.SIGINT, sig_exit)
    tornado.ioloop.IOLoop.instance().start()
Ales Teska
  • 1,198
  • 1
  • 17
  • 38
3

The code is OK. The CTRL+C generates KeyboardInterrupt. To stop the server you can use CTRL+Pause Break(on windows) instead of CTRL+C. On linux CTRL+C also generates the KeyboardInterrupt also. If you will use CTRL+Z program will stop but port gets busy.

Ansuman Bebarta
  • 7,011
  • 6
  • 27
  • 44
  • When you say '`CTRL+` instead of `CTRL+C`' do you actually mean Control and the 'plus' key? Because that does nothing for me – puk Oct 17 '13 at 00:36
  • 1
    `CTRL+Z` won't stop it, but simply move the process into background. – WhyNotHugo Jun 16 '14 at 04:00
  • 1
    I'm on Boot2Docker Windows and `CTRL+C` or `CTRL+PAUSE` kill the process, but the port is still in use... – DestyNova Aug 04 '15 at 10:14
0

I'd say the cleanest, safest and most portable solution would be to put all closing and clean-up calls in a finally block instead of relying on KeyboardInterrupt exception:

import tornado.ioloop
import tornado.web

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

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

# .instance() is deprecated in Tornado 5
loop = tornado.ioloop.IOLoop.current()

if __name__ == "__main__":
    try:
        print("Starting server")
        application.listen(8888)
        loop.start()
    except KeyboardInterrupt:
        pass
    finally:
        loop.stop()       # might be redundant, the loop has already stopped
        loop.close(True)  # needed to close all open sockets
    print("Server shut down, exiting...")
MestreLion
  • 12,698
  • 8
  • 66
  • 57