如何使用Python 3.5样式异步并在Tornado中等待websockets?

Eve*_*len 11 python tornado websocket

考虑这个简短的片段:

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()
Run Code Online (Sandbox Code Playgroud)

OldWsHandler在Tornado中使用3.5之前的异步函数方式,它工作正常.但是,正如文档所述,最好使用PEP 0492来提高可读性和速度.

文件说:

只需使用装饰器async def foo()代替函数定义@gen.coroutine,而await不是代替yield.

所以我写了NewWsHandler.但是,在发送websocket消息时,它会发出警告:

/usr/lib/python3.5/site-packages/tornado/websocket.py:417: RuntimeWarning: coroutine 'on_message' was never awaited
  callback(*args, **kwargs)

我真的不知道如何(正确)修复它.我尝试过装饰它tornado.web.asynchronous,但这假设是一个HTTP动词方法.所以在我覆盖之后finish()(不允许websockets这样做),它似乎有点工作:

class NewWsHandler(tornado.websocket.WebSocketHandler):
    def finish(self):
        pass

    @tornado.web.asynchronous
    async def on_message(self, message):
        await self.write_message("echo " + message)
Run Code Online (Sandbox Code Playgroud)

但这仍然看起来很骇人听闻,似乎与文档相矛盾.这样做的正确方法是什么?

注意:我使用的是Python 3.5.1和Tornado 4.3.

Ben*_*ell 5

协同程序的调用方式与常规函数不同; 因此,当子类化和重写方法时,您无法将基类中的常规方法更改为子类中的协程(除非基类明确指出这是正常的).WebSocketHandler.on_message可能不是一个协程(从Tornado 4.3开始;将来可能会改变).

相反,如果您需要在响应消息时执行异步操作,请将异步部分放在单独的函数中并使用它进行调用IOLoop.current().spawn_callback.(或者,如果write_message你正在做的唯一的异步事情,只需同步调用它)


更新

这在Tornado 4.5中有所改变,WebSocketHandler.on_message现在可以用作协程.见http://www.tornadoweb.org/en/stable/releases/v4.5.0.html#tornado-websocket.