Tornado coroutine

Я пытаюсь изучить сопроводительные письма торнадо, но у меня ошибка при использовании кода ниже.

Traceback (most recent call last):
  File "D:\projekty\tornado\env\lib\site-packages\tornado\web.py", line 1334, in _execute
    result = yield result
  File "D:\projekty\tornado\env\lib\site-packages\tornado\gen.py", line 628, in run
    value = future.result()
  File "D:\projekty\tornado\env\lib\site-packages\tornado\concurrent.py", line 109, in result
    raise_exc_info(self._exc_info)
  File "D:\projekty\tornado\env\lib\site-packages\tornado\gen.py", line 631, in run
    yielded = self.gen.throw(*sys.exc_info())
  File "index.py", line 20, in get
    x = yield 'test'
  File "D:\projekty\tornado\env\lib\site-packages\tornado\gen.py", line 628, in run
    value = future.result()
  File "D:\projekty\tornado\env\lib\site-packages\tornado\concurrent.py", line 111, in result
    raise self._exception
BadYieldError: yielded unknown object 'test'

Код:

from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application, url
from tornado import gen

class HelloHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        x = yield 'test'
        self.render('hello.html')


def make_app():
    return Application(
        [url(r"/", HelloHandler)], 
        debug = True
    )

def main():
    app = make_app()
    app.listen(8888)
    IOLoop.instance().start()

main()

Ответ 1

Как отметил Лутц Хорн, декоратор tornado.coroutine требует, чтобы вы приводили только объекты Future или определенные контейнеры, содержащие объекты Future. Поэтому попытка получить str вызовет ошибку. Я думаю, что фрагмент, который вам не хватает, заключается в том, что любое место внутри сопрограммы, в которой вы хотите вызвать yield something(), something, должно быть либо сопроводительным, либо возвращать Future. Например, вы можете исправить свой пример следующим образом:

from tornado.gen import Return

class HelloHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        x = yield self.do_test()
        self.render('hello.html')

    @gen.coroutine
    def do_test(self):
        raise Return('test')
        # return 'test' # Python 3.3+

Или даже это (хотя обычно вы не должны этого делать):

class HelloHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        x = yield self.do_test()
        self.render('hello.html')

    def do_test(self):
        fut = Future()
        fut.set_result("test")
        return fut

Конечно, это надуманные примеры; так как мы фактически не делаем асинхронного в do_test, нет никаких причин сделать его сопрограммой. Обычно вы будете делать какие-то асинхронные операции ввода-вывода. Например:

class HelloHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        x = yield self.do_test()
        self.render('hello.html')

    @gen.coroutine
    def do_test(self):
        http_client = AsyncHTTPClient()
        out = yield http_client.fetch("someurl.com") # fetch is a coroutine
        raise Return(out.body)
        # return out.body # Python 3.3+

Ответ 2

Из документации:

Большинство асинхронных функций в Tornado возвращают Future; сдача этого объекта возвращает его результат.

Вы также можете yield a list or dict of Futures, который будет запускаться одновременно и работать параллельно; список или описание результатов будут возвращены после их завершения:

Строка "test" не является Future. Попробуйте дать один.