Подтвердить что ты не робот

Как использовать асинхронный стиль Python 3.5 и ждать в Tornado для websockets?

Рассмотрим этот короткий фрагмент:

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 использует pre-3.5 способ делать асинхронные функции в Tornado, и он отлично работает. Однако как указано в документации, рекомендуется использовать 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)

Но это все еще выглядит хакерским и, кажется, противоречит документации. Каков правильный способ сделать это?

Примечание. Я использую Python 3.5.1 и Tornado 4.3.

4b9b3361

Ответ 1

Корутины называются иначе, чем регулярные; поэтому при подклассификации и переопределении методов вы не можете изменить обычный метод в базовом классе на сопрограмму в вашем подклассе (если только базовый класс не говорит об этом, это нормально). WebSocketHandler.on_message может не быть сопрограммой (с Tornado 4.3, это может измениться в будущем). Вместо этого, если вам нужно сделать что-то асинхронное в ответ на сообщение, поместите асинхронные части в отдельную функцию и вызовите ее с помощью IOLoop.current().spawn_callback. (или если write_message - единственная асинхронная вещь, которую вы делаете, просто назовите ее синхронно)