我有一个带有很多静态方法的类,使用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
,但这会导致Future
个Future
个对象的对象和其他一些奇怪的不可预测的行为
答案 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
函数放在那里。只需将这些函数放在模块中的顶层即可。类用于组合方法和共享状态,而不是创建命名空间。使用模块来创建名称空间。