2

たとえば、コールバック チェーンを「まっすぐにする」ために gen.engine でラップされた関数があるとします。

関数は、次のようになります

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

等々。yieldによってラップされた関数で発生する例外をキャッチするために、 s の周りで間違いなく try / except を使用できることを理解していgen.Taskます。関数自体を別の関数でラップする必要がある場合(これは実際の使用例です)、「醜い」(右..) を導入せずfuncにすべての「キャッチされていない」例外をキャッチするにはどうすればよいですか?funcfunc

私はこれを思いついた:

@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 2.x に制限されていることを言及する必要がありました。

4

1 に答える 1

6

@gen.coroutineTornado 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 に関しては、コールバックを非表示にする簡単な方法はないと思います。ドキュメントの状態:

ほとんどの場合、engine で装飾された関数は、コールバック引数を取り、終了時にその結果で呼び出す必要があります。注目すべき例外の 1 つは、コールバック引数の代わりに self.finish() を使用する RequestHandler の get/post/etc メソッドです。

ですから、それらは避けられないのではないかと心配しています。例:

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")
于 2013-05-29T01:21:49.567 に答える