如何编写一个可以获取函数或修饰函数的超时装饰器?

时间:2017-07-31 21:05:04

标签: python

我有以下超时创建装饰器功能:

class TimeoutError(Exception): pass


def timeout(seconds, error_message = 'Function call timed out'):
    def decorated(func):
        print "timeout: \t" + func.__name__
        def _handle_timeout(signum, frame):
            raise TimeoutError(error_message)


        def wrapper(*args, **kwargs):
            signal.signal(signal.SIGALRM, _handle_timeout)
            signal.alarm(seconds)
            try:
                print "timeout wrapper: \t" + func.__name__
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result

        return functools.wraps(func)(wrapper)

    return decorated

另一位装饰师:

import inspect

class withHostAndToken(object):

    __name__ = "withHostAndToken"
    __doc__ = "Get the Host and Token for the API call"

    def __init__(self, func):
        print "withHostAndToken: \t" + func.__name__
        self.func = func
        self.HOST = ''
        self.TOKEN = ''

    def __call__(self,*args, **kwds):

        if self.HOST == '':
            self.HOST = "HOST"
        if self.TOKEN == '':
            self.TOKEN = "TOKEN"

        argsspec = inspect.getargspec(self.func)
        function_args = argsspec[0]
        if 'HOST' in function_args:
            if 'TOKEN' in function_args:
                return self.func(self.HOST , self.TOKEN , *args, **kwds)
            else:
                return self.func(self.HOST  , *args, **kwds)
        elif 'TOKEN' in function_args:
            return self.func(self.TOKEN, *args, **kwds)

当我尝试将两者都应用于函数时,我没有得到要调用的函数代码:

@timeout(2)
@withHostAndToken
def testDecorators():
    print __name__
    while True:
        print '.'

testDecorators()
  

输出是:

     

withHostAndToken:testDecorators
  超时:withHostAndToken
  超时包装器:withHostAndToken

     

处理完成,退出代码为0

1 个答案:

答案 0 :(得分:0)

你的问题并不存在,装饰的链接工作正常。

以下是一个使用装饰器演示的示例代码:

>>> @timeout(2)
@withHostAndToken
def bar(*args):
    print(*args)
    i = 0;
    while True:
        dummy = sys.stderr.write('.')


>>> bar('foo')
host token foo
....................................................................................................................................................................................................................................................................................................................................................................................................................Traceback (most recent call last):
  File "<pyshell#48>", line 1, in <module>
    bar('foo')
  File "<pyshell#2>", line 10, in wrapper
    result = func(*args, **kwargs)
  File "<pyshell#5>", line 19, in __call__
    return self.func(self.HOST , self.TOKEN , *args, **kwds)
  File "<pyshell#47>", line 7, in bar
    dummy = sys.stderr.write('.')
... message list truncate for brievety ...
  File "<pyshell#2>", line 4, in _handle_timeout
    raise TimeoutError(error_message)
TimeoutError: Function call timed out
>>> 

因此,按预期大约2秒后,函数会正确中断。

但在您的用例中,您在最内部函数中使用了time.sleep。在Linux和其他Unix中,sleep是通过...... SIGALRM

实现的

所以这是发生的事情:

  • 外部装饰者要求在10秒钟内发出警报
  • 内部装饰器将附加参数传递给函数
  • 调用该函数并调用sleep(20)
    • sleep函数调用将警报超时重置为20秒!

这就是为什么这个功能实际上持续20秒而不是10 ...

的原因