How should I catch exceptions in a decorator function that can raise, if the decorator function is in a library I can't modify?
我正在谷歌应用引擎(GAE)上运行python statsd库。不幸的是,在使用套接字时,GAE可以不时地提高
statsd客户机已经设置为捕获
我专门与
1 2 3 4 5 6 7 | from statsd import StatsClient statsd = StatsClient() @statsd.timer('myfunc') def myfunc(a, b): """Calculate the most complicated thing a and b can do.""" |
我没有简单的能力修改
我应该如何编写一个包装器或额外的装饰器,它仍然允许像
这是我的代码库中的一个基础模块,它将被用于很多地方(无论我们想在什么地方使用时间)。因此,尽管这个答案可能有效,但我真的希望避免在我的代码库中强制所有
我想做如下的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def my_timer_wrapper(wrapped_func, *args, **kwargs): @functools.wraps(wrapped_func) class Wat(object): def __call__(self, *args, **kwargs): timer_instance = stats_client.timer(*args, **kwargs) try: return timer_instance.__call__(wrapped_func)(*args, **kwargs) except Exception: logger.warning("Caught exception", exc_info=True) def foo(): pass return foo return Wat() |
然后使用如下:
1 2 3 | @my_timer_wrapper('stastd_timer_name') def timed_func(): do_work() |
有更好或更多的Python疗法吗?
看起来这是一个"尽可能简单"的例子。新的装饰添加一个额外的尝试/除了围绕你的计时器装饰。
唯一的问题是,采用参数的修饰符需要要定义的两级嵌套函数,几乎总是使它们看起来很复杂,即使它们不是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | from functools import wraps def shielded_timer(statsd, exceptions=(apiproxy_errors.ApplicationError), name=None): def decorator(func): timer_decorator = statsd.timer(name or func.__name__) new_func = timer_decorator(func) @wraps(func) def wrapper(*args, **kw): try: return new_func(*args, **kw) except BaseException as error: if isinstance (error, exceptions): # Expected error (ApplicationError by default) ocurred pass else: raise return wrapper return decorator ##################### statsd = StatsClient() @shielded_timer(statsd) def my_func(a,b): ... |
正如您所看到的,它甚至很容易包括额外的细节——在本例中,我已经在装饰时配置了所需的异常,并且还可以选择在呼叫Statsd.Timer。