2016-02-18 76 views
1

我使用的代碼解決方案提到here
我是新裝修的,不明白爲什麼,如果我想要寫的東西像下面這種解決方案不起作用:Python超時修飾器

@timeout(10) 
def main_func(): 
    nested_func() 
    while True: 
     continue 

@timeout(5) 
def nested_func(): 
    print "finished doing nothing" 

=>這個結果將是沒有超時的。我們將陷入無限循環。
但是,如果我從nested_func刪除@timeout註釋,則會出現超時錯誤。
出於某種原因,我們不能在同一時間在函數和嵌套函數上使用裝飾器,任何想法爲什麼以及如何糾正它的工作,假定包含函數超時總是必須大於嵌套超時。

回答

2

這是模塊的定時功能的限制,您鏈接的裝飾器使用該功能。下面是有關piece of the documentation(由我加的):

signal.alarm(time)

如果時間爲非零,一個SIGALRM信號time秒被髮送到處理該功能的請求。 任何先前預定的警報都會取消(只能在任何時間安排一個警報)。然後返回的值是任何先前設置的警報傳遞之前的秒數。如果time爲零,則不安排警報,並且任何安排的警報都將被取消。如果返回值爲零,則當前不安排警報。 (請參閱Unix手冊頁警報(2)。)可用性:Unix。

所以,你看到的是當你的nested_func被調用時,它的計時器取消了外部函數的計時器。

您可以更新修飾器,以關注alarm調用的返回值(這將是上次警報(如果有)到期之前的時間)。因爲內部定時器需要跟蹤它的功能運行時間,所以它可以修改前一個定時器的剩餘時間,這對於獲取細節是件很複雜的事情。這裏的裝飾的未經測試的版本,我認爲得到它基本上是正確的(但我不能完全肯定它可以正確處理所有的異常情況):

import time 
import signal 

class TimeoutError(Exception): 
    def __init__(self, value = "Timed Out"): 
     self.value = value 
    def __str__(self): 
     return repr(self.value) 

def timeout(seconds_before_timeout): 
    def decorate(f): 
     def handler(signum, frame): 
      raise TimeoutError() 
     def new_f(*args, **kwargs): 
      old = signal.signal(signal.SIGALRM, handler) 
      old_time_left = signal.alarm(seconds_before_timeout) 
      if 0 < old_time_left < second_before_timeout: # never lengthen existing timer 
       signal.alarm(old_time_left) 
      start_time = time.time() 
      try: 
       result = f(*args, **kwargs) 
      finally: 
       if old_time_left > 0: # deduct f's run time from the saved timer 
        old_time_left -= time.time() - start_time 
       signal.signal(signal.SIGALRM, old) 
       signal.alarm(old_time_left) 
      return result 
     new_f.func_name = f.func_name 
     return new_f 
    return decorate 
+0

你是哪個異常情況下不知道? – JavaSa

+0

我想這不是例外情況,因爲'finally'塊能夠很好地清理事件,但是之前設置的警報的情況可能不會得到最好的處理。我也無法測試代碼,因爲'signal.alarm'在我的操作系統上不可用。 – Blckknght

+0

我想我們還需要添加一個檢查,old_time_left - = time.time() - start_time> 0,它可以是負數,我認爲 – JavaSa