2013-05-28 187 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

相關問題