2013-05-28 189 views
2

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

的功能,那么像这样

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

等。我明白,我绝对可以使用try /除了在yields之外来捕捉函数中发生的异常,这是由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!

回答

6

@gen.coroutine是龙卷风3的新特性从http://www.tornadoweb.org/en/stable/releases/v3.0.0.html

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

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

与这个装饰

函数返回一个未来。此外,他们可能会使用回调关键字参数调用 ,该参数将在解析后与未来的结果一起调用。如果协程失败,则回调将不会运行,并且会在周围的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

更新:

至于龙卷风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") 
+0

weeeell,是的,谢谢你的回答,但不幸的是我忘了提,我被限制在龙卷风2.x.否则,我很高兴知道,必须在龙卷风3中跳过更少的箍。感谢您的努力 – shylent

+0

@shylent,我更新了我的答案。 – Nykakin

+0

没错,非常感谢。基本上,这证实了,我在我的问题中提出的方法是唯一的方法,这很好理解。 – shylent

相关问题