Consider this short snippet:
import tornado
import tornado.websocket
import tornado.ioloop
import tornado.gen
import tornado.web
class NewWsHandler(tornado.websocket.WebSocketHandler):
async def on_message(self, message):
await self.write_message("echo " + message)
class OldWsHandler(tornado.websocket.WebSocketHandler):
@tornado.gen.coroutine
def on_message(self, message):
yield self.write_message("echo " + message)
app = tornado.web.Application([(r'/', OldWsHandler)])
app.listen(8080)
tornado.ioloop.IOLoop.current().start()
OldWsHandler
uses the pre-3.5 way of doing asynchronous functions in Tornado, and it works fine. However, as the documentation states, it is preferred to use PEP 0492 for readability and speed.
The documentation says:
Simply use
async def foo()
in place of a function definition with the@gen.coroutine
decorator, andawait
in place ofyield
.
So I wrote NewWsHandler
. However, when sending a websocket message, it raises a warning:
/usr/lib/python3.5/site-packages/tornado/websocket.py:417: RuntimeWarning: coroutine 'on_message' was never awaited callback(*args, **kwargs)
I don't really know how to (properly) fix it. I tried decorating it in tornado.web.asynchronous
, but that assumes a HTTP verb method. So after I override finish()
(websockets are not allowed to do that), it seems to be kind of working:
class NewWsHandler(tornado.websocket.WebSocketHandler):
def finish(self):
pass
@tornado.web.asynchronous
async def on_message(self, message):
await self.write_message("echo " + message)
But this still looks hackerish, and seems to be contradicting the documentation. What is the right way of doing this?
Note: I am using Python 3.5.1 and Tornado 4.3.
Coroutines are called differently than regular functions; therefore when subclassing and overriding methods you cannot change a regular method in the base class into a coroutine in your subclass (unless the base class specifically says this is OK). WebSocketHandler.on_message
may not be a coroutine (as of Tornado 4.3; this may change in the future).
Instead, if you need to do something asynchronous in response to a message, put the asynchronous parts in a separate function and call it with IOLoop.current().spawn_callback
. (or if write_message
is the only async thing you're doing, just call it synchronously)
This changed in Tornado 4.5, WebSocketHandler.on_message
can now be used as a coroutine. See http://www.tornadoweb.org/en/stable/releases/v4.5.0.html#tornado-websocket.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With