Python,Tornado:gen.coroutine装饰器打破了另一个装饰器中的try-catch

时间:2018-01-11 20:21:40

标签: python exception tornado decorator

我有一个带有很多静态方法的类,使用Tornado coroutine装饰器。我想添加另一个装饰器,以捕获异常并将它们写入文件:

# my decorator
def lifesaver(func):
    def silenceit(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as ex:
            # collect info and format it
            res = ' ... '
            # writelog(res)
            print(res)
            return None

    return silenceit

但是,它不适用于gen.coroutine装饰器:

class SomeClass:

    # This doesn't work!
    # I tried to pass decorators in different orders,
    # but got no result.
    @staticmethod
    @lifesaver
    @gen.coroutine
    @lifesaver
    def dosomething1():
        raise Exception("Test error!")


    # My decorator works well
    # if it is used without gen.coroutine.
    @staticmethod
    @gen.coroutine
    def dosomething2():
        SomeClass.dosomething3()

    @staticmethod
    @lifesaver
    def dosomething3():
        raise Exception("Test error!")

据我所知,Tornado使用的raise Return(...)方法可能基于 Exceptions ,也许它会以某种方式阻止其他装饰器的 try-catches ......那么,我如何使用我的装饰器来处理与Tornado协同程序的 Exceptions

答案

感谢Martijn Pieters,我得到了这段代码:

def lifesaver(func):
    def silenceit(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except (gen.Return, StopIteration):
            raise
        except Exception as ex:
            # collect info and format it
            res = ' ... '
            # writelog(res)
            print(res)

            raise gen.Return(b"")

    return silenceit

所以,我只需要指定Tornado Return。我尝试将@gen.coroutine装饰器添加到silenceit函数并在其中使用yield,但这会导致FutureFuture个对象的对象和其他一些奇怪的不可预测的行为

1 个答案:

答案 0 :(得分:2)

您正在装饰gen.coroutine的输出,因为装饰器从下到上应用(因为它们从上到下嵌套在另一个中)。

不是装饰协程,而是装饰你的函数并将gen.coroutine装饰器应用于那个结果:

@gen.coroutine
@lifesaver
def dosomething1():
    raise Exception("Test error!")

你的装饰者无法真正处理@gen.coroutine修饰函数产生的输出。 Tornado依赖于异常来传达结果(因为在Python 2中,生成器不能使用return来返回结果)。您需要确保通过Tornado所依赖的例外。你还应该重新包装你的包装函数:

from tornado import gen

def lifesaver(func):
    @gen.coroutine
    def silenceit(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except (gen.Return, StopIteration):
            raise
        except Exception as ex:
            # collect info and format it
            res = ' ... '
            # writelog(res)
            print(res)
            raise gen.Return(b"")

    return silenceit

在异常时,会引发一个空的Return()对象;根据需要调整它。

帮自己一个忙,不要只使用staticmethod函数放在那里。只需将这些函数放在模块中的顶层即可。类用于组合方法和共享状态,而不是创建命名空间。使用模块来创建名称空间。