Search code examples
pythonexceptioncallbacktornado

Except exception in IOLoop add_timeout callback function


So I am scheduling a callback using

ioloop.IOLoop.instance().add_timeout(time, callback_func)

But my callback_func may throw Exception which I want to catch.

Tried whats suggested in this answer but doesn't seem to work. Or maybe I am not doing it the right way. Any help on this would be great.

Code is somewhat like this:

start.py

class Start:
    # ... other methods ...
    @staticmethod
    def initialize():
        OtherClass.initialize()

def main():
    Start.initialize()

if __name__ == "__main__":
    main()

ioloop.IOLoop.instance().start()

other_class.py

class OtherClass:
    @staticmethod
    def initialize():
        ioloop.IOLoop.instance().add_timeout(time, callback_func)

    @staticmethod
    def callback_func():
        # Need to catch any exception which occurs here.

Solution

  • If the callback_func is your own code, then by far the simplest way to catch all exceptions there is to simply wrap the whole function body in try / except:

    @staticmethod
    def callback_func():
        try:
            # ... your code ...
        except Exception as exc:
            # handle it
    

    It's simple and everyone who reads your code will understand it, no surprises.

    If you want to do something exotic and Tornado-specific, use an ExceptionStackContext:

    from tornado import ioloop
    from tornado.stack_context import ExceptionStackContext
    
    
    class OtherClass:
        @staticmethod
        def initialize():
            ioloop.IOLoop.instance().add_timeout(1, OtherClass.callback_func)
    
        @staticmethod
        def callback_func():
            # Need to catch any exception which occurs here.
            1 / 0
    
    class Start:
        # ... other methods ...
        @staticmethod
        def initialize():
            with ExceptionStackContext(Start.handler):
                OtherClass.initialize()
    
        @staticmethod
        def handler(exc_type, exc_value, exc_traceback):
            print("Caught %r in Handler" % exc_type)
            return True  # Tell Tornado that we handled it.
    
    def main():
        Start.initialize()
    
    if __name__ == "__main__":
        main()
    
    ioloop.IOLoop.instance().start()
    

    Best of all, use coroutines instead of callbacks. Coroutines are as efficient as callbacks, but give you regular Python exception handling semantics. See my article Refactoring Tornado Coroutines and the Tornado guide.