用另一个函数包装tornado.gen.engine包装函数

时间:2013-05-28 06:14:55

标签: python asynchronous generator tornado

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

该功能,然后看起来像这样

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

等等。我理解,我绝对可以在yield周围使用try / except来捕获函数中发生的异常,它由gen.Task包装。如果我需要将函数func本身包含在另一个函数中(这是实际用例),在func中捕获所有“未捕获”异常而不引入“丑陋”(正确...)尝试该怎么办? / 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!

1 个答案:

答案 0 :(得分: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")