2017-07-31 61 views
3

我有以下的超時創作裝飾功能:如何編寫可以獲取函數或裝飾函數的超時裝飾器?

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完

+3

是不是這裏的問題,你的*內*裝飾類不換行的財產?它應該保留'__name__'和'__doc__';見例如https://docs.python.org/3/library/functools.html#functools.wraps – jonrsharpe

+0

'__name__'和'__doc__'不應該是方法。使它們成爲常規的實例屬性或屬性。 – user2357112

回答

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的原因...

+0

謝謝,但我測試它的方式是在睡眠線上放一個斷點,我從來沒有到過這個斷點。 –

+0

@EranWitkon:IDE盡力模擬睡眠和SIGALRM,但結果並不總是如預期的那樣... –

+0

只需更新問題以表明它與睡眠無關。新格式的代碼不會打印任何東西 –