【问题标题】:Wrapping a tornado.gen.engine-wrapped function with another function用另一个函数包装一个 tornado.gen.engine 包装的函数
【发布时间】:2013-05-23 00:15:37
【问题描述】:

比方说,我有一个函数,用 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 的正确方法。来自另一个函数的引擎封装函数。

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

【问题讨论】:

    标签: python asynchronous generator tornado


    【解决方案1】:

    @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")
    

    【讨论】:

    • weeeell,是的,感谢您的回答,但不幸的是我忘了提及,我仅限于 tornado 2.x。否则,我很高兴知道,一个人必须在龙卷风 3 中跳过更少的圈。感谢您的努力
    • 好的,非常感谢。基本上,这证实了我在问题中提出的方法是唯一的,很高兴知道。
    猜你喜欢
    • 1970-01-01
    • 2021-04-06
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    相关资源
    最近更新 更多