Search code examples
pythontornado

Exception ignored in Tornado WebSocket on_message method


I have the following class:

class SessionHandler(tornado.websocket.WebSocketHandler):

@tornado.gen.coroutine       
def on_message(self):
          yield self.application.c.check("[email protected]")

The check() function is something like

   @tornado.gen.coroutine
   def check(self,id):

      if id not in self.AUTH_METHOD.keys():
         raise InvalidXX

InvalidXX is a user defined exception that inherits from Exception class.

When this exception is raised nothing is displayed to the tornado console. However when I add try/except clauses around them the exception is picked up. I don't understand why this exception isn't being propagated to the console. Other exceptions E.g. Duplicate key in MongoDB are propagated and shown to the console.


Solution

  • [This answer applies to Tornado 4.4 and older. As of Tornado 4.5 on_message may be a coroutine and the code in the original question will work]

    Coroutines are called differently from regular functions (i.e. they must be called with yield). Therefore, when you are defining a method to be called by the framework, you should only use a coroutine when the docs say something like "this method may be a coroutine". WebSocketHandler.on_message may not be a coroutine (as of Tornado 4.3).

    Instead, you can use IOLoop.spawn_callback to start an independent coroutine from your on_message callback.

    def on_message(self, msg):
        IOLoop.current().spawn_callback(process_message, msg)
    
    @gen.coroutine
    def process_message(self, msg):
        ...
    

    An important distinction here is that spawn_callback detaches the coroutine from the code in WebSocketHandler that receives messages: you may get a second on_message call before the callback spawned in the first one has finished. Use the methods in the tornado.locks and tornado.queues modules to manage this concurrency. (By contrast, in RequestHandler.data_received, which may be a coroutine, you won't get a second chunk of data until the second has been processed, and an uncaught exception will abort the connection)