2

说,我有一个函数,用 gen.engine 包装来“理顺”回调链,也就是说,使代码看起来是同步的/线性的/其他的。

函数,然后看起来像这样

@gen.engine
def func():
     ...
     yield gen.Task(...)
     ...
     yield gen.Task(...)

等等。我明白,我绝对可以在yields 周围使用 try / except 来捕获函数中发生的异常,该函数由gen.Task. 如果我需要将函数func本身包装在另一个函数中以(这是实际用例)捕获所有“未捕获”异常func而不引入“丑陋”(对..)try / except,这将跨越整个func

我想出了这个:

@gen.engine
def func(..., callback):
     ...
     callback()

@gen.engine
def outer():
    try:
        yield gen.Task(func)
    except Exception as e:
        # Log the exception
    # Stop ioloop (or something)

这为 . 添加了一些通用性func,但在func.

有没有其他方法可以做到这一点?请注意,出于这个问题的目的,“紧急异常捕获”或多或少是一个人为的用例(这可能可以通过其他方式完成),我正在寻找的是调用这些 tornado.gen 的正确方法。来自另一个函数的引擎包装函数。

编辑:愚蠢的我,我应该提到我仅限于龙卷风 2.x!

4

1 回答 1

6

@gen.coroutine是 Tornado 3 的新功能。来自http://www.tornadoweb.org/en/stable/releases/v3.0.0.html

新的装饰器 @gen.coroutine 可作为 @gen.engine 的替代品。它会自动返回一个 Future,并且在函数中而不是调用回调,而是使用 raise gen.Return(value) 返回一个值(或者在 Python 3.3 中简单地返回值)。

从文档(http://www.tornadoweb.org/en/stable/gen.html#tornado.gen.coroutine):

带有这个装饰器的函数返回一个 Future。此外,可以使用回调关键字参数调用它们,当它解析时将使用未来的结果调用它们。如果协程失败,回调将不会运行,并且会在周围的 StackContext 中引发异常。回调参数在装饰函数中不可见;它由装饰器本身处理。

因此没有理由担心回调,也没有必要将函数包装到tornado.gen.Task(). 链接现在很容易:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import logging

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.gen

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class MainHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def outer(self):
        logging.info('outer starts')
        yield self.inner()
        yield self.inner()  
        logging.info('outer ends')  
        raise tornado.gen.Return('hello')

    @tornado.gen.coroutine
    def inner(self):
        logging.info('inner runs')

    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self):
        res = yield self.outer()
        self.write(res)

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(handlers=[(r"/", MainHandler)])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

输出:

$ python test.py 
[I 130529 03:18:35 test:21] outer starts
[I 130529 03:18:35 test:29] inner runs
[I 130529 03:18:35 test:29] inner runs
[I 130529 03:18:35 test:24] outer ends
[I 130529 03:18:35 web:1514] 200 GET / (127.0.0.1) 1.48ms

从 Python 3.3 开始不需要使用gen.Result(),简单return就可以了。在旧版本中,会出现'return' with argument inside generator错误。

另外,检查:https ://github.com/facebook/tornado/issues/759

更新:

至于 Tornado 2.x,我认为没有简单的方法可以隐藏回调。文档指出:

在大多数情况下,用引擎装饰的函数应该接受一个回调参数,并在完成时使用其结果调用它。一个值得注意的例外是 RequestHandler get/post/etc 方法,它使用 self.finish() 代替回调参数。

所以恐怕这些都是不可避免的。例子:

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):
        res = yield tornado.gen.Task(self.outer)
        self.write(res)
        self.finish()

    def inner(self, callback):
        logging.info('inner runs')
        callback()

    @tornado.gen.engine
    def outer(self, callback):
        logging.info('outer starts')
        yield tornado.gen.Task(self.inner)
        yield tornado.gen.Task(self.inner)
        logging.info('outer ends')
        callback("hello")
于 2013-05-29T01:21:49.567 回答