说,我有一个函数,用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!
答案 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")