как правильно использовать асинхронность с торнадо

в документах торнадо упоминается, что декораторы gen.coroutine из старых версий. новее следует использовать с aysnc. Пока мне не удалось преобразовать этот небольшой код для Tornado 6.0.3.

import tornado.web
import tornado.websocket
import tornado.httpserver

from random import randint
from tornado import gen
from tornado.ioloop import IOLoop

from web_handlers import HandlerIndexPage
from web_handlers import HandlerWebSocket

msg = 'none'

@gen.coroutine
def generate_random_int():
    global msg
    while True:
        msg = str(randint(0, 100))
        print('generated:', msg)
        yield gen.sleep(1.0)

@gen.coroutine
def generate_message_to_sockets():
    global msg
    while True:
        print ('new messageToCon: ', msg)
        yield [con.write_message(msg) for con in HandlerWebSocket.connections]
        yield gen.sleep(1.0)


class webApplication(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r'/', HandlerIndexPage),
            (r'/websocket', HandlerWebSocket)
        ]

        settings = {
            'template_path': 'templates'
        }
        tornado.web.Application.__init__(self, handlers, **settings)

if __name__ == '__main__':
    ws_app = webApplication()
    server = tornado.httpserver.HTTPServer(ws_app)
    port = 9090
    server.listen(port)
    print('websocket listening on port:'+ str(port))
    IOLoop.current().spawn_callback(generate_random_int)
    IOLoop.current().spawn_callback(generate_message_to_sockets)
    IOLoop.instance().start()

Как мне правильно использовать async ?


person user3732793    schedule 28.10.2019    source источник
comment
tornadoweb.org/en/stable/guide/coroutines.html   -  person Fine    schedule 28.10.2019
comment
конечно у меня есть красная документация. К сожалению, простая замена декоратора на async не запускала сопрограммы.   -  person user3732793    schedule 28.10.2019
comment
см.   -  person Milovan Tomašević    schedule 07.02.2021


Ответы (2)


В Tornado сопрограммы — это генераторы, которые можно подключить к Python2, в то время как асинхронные функции — это первоклассные граждане в Python3. У них разная семантика (например, вы можете return из асинхронной функции, но не из сопрограммы). Код, который вам нужно будет изменить, будет выглядеть так:

...

import asyncio

...

async def generate_random_int():
    global msg
    while True:
        msg = str(randint(0, 100))
        print('generated:', msg)
        await gen.sleep(1.0)

async def generate_message_to_sockets():
    global msg
    while True:
        print ('new messageToCon: ', msg)
        futures = [con.write_message(msg) for con in HandlerWebSocket.connections]
        if futures:
            await asyncio.wait(futures)
        await gen.sleep(1.0)
...

Вы можете увидеть пример, аналогичный вашему варианту использования, здесь: https://www.tornadoweb.org/en/stable/ioloop.html#ioloop-objects

person Chris Zacharias    schedule 28.10.2019
comment
спасибо за ответ, но просто изменение только того, что заканчивается ValueError: набор сопрограмм/фьючерсов пуст. Вероятно, я должен подготовить петлю как-то по-другому. Спасибо за ссылку, но я не совсем понимаю пример - person user3732793; 29.10.2019
comment
Возможно, вам придется защищаться от пустого набора соединений, а не так, как это сделал я. Я бы взял [con.write_message...] и присвоил его переменной, а затем проверил, пусто ли оно, прежде чем пытаться ждать его. - person Chris Zacharias; 29.10.2019
comment
о человек, я был слеп. Спасибо, что указали на это. И да работает :-) - person user3732793; 29.10.2019

Python 3.5 представил ключевые слова async и await (функции, использующие эти ключевые слова, также называются «собственными сопрограммами»).

# Decorated:                    # Native:

# Normal function declaration
# with decorator                # "async def" keywords
@gen.coroutine
def a():                        async def a():
    # "yield" all async funcs       # "await" all async funcs
    b = yield c()                   b = await c()
    # "return" and "yield"
    # cannot be mixed in
    # Python 2, so raise a
    # special exception.            # Return normally
    raise gen.Return(b)             return b
person Milovan Tomašević    schedule 07.02.2021
comment
это было решено более года назад, и я думаю, что уже есть нативное решение - person user3732793; 08.02.2021
comment
здесь у вас есть базовый пример для входа в систему. А в ответе написано как - официальные правила как до него добраться, что не имеет значения, сколько лет решению. - person Milovan Tomašević; 08.02.2021
comment
Спасибо за попытку - person user3732793; 08.02.2021