2009-02-23 69 views
3

我想在Python中編寫TTL裝飾器。 基本上,如果功能在選定時間內沒有 回答,我會提出異常。在Python中編寫TTL裝飾器

您可以在http://sebulba.wikispaces.com/recipe+thread2

from thread2 import Thread 

""" A TTL decorator. """ 
class Worker(Thread): 
    def __init__(self, q, f, args, kvargs): 
     Thread.__init__(self) 

     self.q = q 
     self.f = f 
     self.args = args 
     self.kvargs = kvargs 

    def run(self,): 
     try: 
      res = (True, self.f(*self.args, **self.kvargs)) 
      self.q.put(res) 
     except Exception, e: 
      self.q.put((False, e)) 

class Referee(Thread): 
    def __init__(self,q, ttl,exception_factory): 
     Thread.__init__(self) 

     self.exception_factory=exception_factory  
     self.q=q 
     self.ttl=ttl 

    def run(self): 
     time.sleep(self.ttl) 
     res = (False, self.exception_factory()) 
     self.q.put(res) 

def raise_if_too_long(ttl, exception_factory=lambda :RuntimeError("Timeout")): 
    def raise_if_too_long_aux(f): 
     def ritl(*args,**kvargs): 
      q = Queue.Queue(2) 

      referee = Referee(q, ttl, exception_factory) 
      worker = Worker(q,f,args,kvargs) 

      worker.start() 
      referee.start() 

      (valid, res)= q.get(1) 

      q.task_done() 

      referee.terminate() 
      worker.terminate() 

      if valid: 
       return res 
      else: 
       raise res 

     return ritl 

    return raise_if_too_long_aux 

找到thead2片斷不過,我得到一些非常糟糕的結果。 似乎有時函數返回正常 裝飾器不會返回,直到達到TTL並且錯誤 引發。

您是否在此代碼中看到錯誤? 有沒有一種常見的方法/庫在python中用TTL編寫函數?

回答

5

提供的代碼有點難以遵循 - 如何在恰當的時間在正確的線程中在適當的位置引發異常?

考慮這個粗略的流程:

調用目標函數裝飾功能。返回的一個函數:

  • 啓動線程,調用目標函數
  • 聯接使用的Thread.join線程([超時])
  • 如果收到超時,引發異常,並忽略線程的結果。
  • 如果你沒有得到超時,捕獲線程的結果並返回它。
  • (您將需要找到一種方法,捕捉線程的輸出...)

    對線程超時信息請參見http://docs.python.org/library/threading.html ...

    (或只是開始使用erlang:)

    +1

    謝謝你真的幫了忙。我不知道thread.join(超時)。 – fulmicoton 2009-02-23 07:48:11

    0

    如果你想終止函數的執行後已超出時間限制,你可能想嘗試的代碼,哈哈那種能力。要使用該模塊,需要完成的所有工作就是將函數作爲add_timeout的參數進行調用,並且可以運行返回的值。一旦被調用,對象的ready屬性可以被輪詢,並且可以通過value屬性訪問任何返回的內容。代碼的文檔應該提供對其餘可用API的解釋。

    ## {{{ http://code.activestate.com/recipes/577045/ (r2) 
    #! /usr/bin/env python 
    """Provide way to add timeout specifications to arbitrary functions. 
    
    There are many ways to add a timeout to a function, but no solution 
    is both cross-platform and capable of terminating the procedure. This 
    module use the multiprocessing module to solve both of those problems.""" 
    
    ################################################################################ 
    
    __author__ = 'Stephen "Zero" Chappell <[email protected]>' 
    __date__ = '11 February 2010' 
    __version__ = '$Revision: 3 $' 
    
    ################################################################################ 
    
    import inspect 
    import sys 
    import time 
    import multiprocessing 
    
    ################################################################################ 
    
    def add_timeout(function, limit=60): 
        """Add a timeout parameter to a function and return it. 
    
        It is illegal to pass anything other than a function as the first 
        parameter. If the limit is not given, it gets a default value equal 
        to one minute. The function is wrapped and returned to the caller.""" 
        assert inspect.isfunction(function) 
        if limit <= 0: 
         raise ValueError() 
        return _Timeout(function, limit) 
    
    class NotReadyError(Exception): pass 
    
    ################################################################################ 
    
    def _target(queue, function, *args, **kwargs): 
        """Run a function with arguments and return output via a queue. 
    
        This is a helper function for the Process created in _Timeout. It runs 
        the function with positional arguments and keyword arguments and then 
        returns the function's output by way of a queue. If an exception gets 
        raised, it is returned to _Timeout to be raised by the value property.""" 
        try: 
         queue.put((True, function(*args, **kwargs))) 
        except: 
         queue.put((False, sys.exc_info()[1])) 
    
    class _Timeout: 
    
        """Wrap a function and add a timeout (limit) attribute to it. 
    
        Instances of this class are automatically generated by the add_timeout 
        function defined above. Wrapping a function allows asynchronous calls 
        to be made and termination of execution after a timeout has passed.""" 
    
        def __init__(self, function, limit): 
         """Initialize instance in preparation for being called.""" 
         self.__limit = limit 
         self.__function = function 
         self.__timeout = time.clock() 
         self.__process = multiprocessing.Process() 
         self.__queue = multiprocessing.Queue() 
    
        def __call__(self, *args, **kwargs): 
         """Execute the embedded function object asynchronously. 
    
         The function given to the constructor is transparently called and 
         requires that "ready" be intermittently polled. If and when it is 
         True, the "value" property may then be checked for returned data.""" 
         self.cancel() 
         self.__queue = multiprocessing.Queue(1) 
         args = (self.__queue, self.__function) + args 
         self.__process = multiprocessing.Process(target=_target, 
                   args=args, 
                   kwargs=kwargs) 
         self.__process.daemon = True 
         self.__process.start() 
         self.__timeout = self.__limit + time.clock() 
    
        def cancel(self): 
         """Terminate any possible execution of the embedded function.""" 
         if self.__process.is_alive(): 
          self.__process.terminate() 
    
        @property 
        def ready(self): 
         """Read-only property indicating status of "value" property.""" 
         if self.__queue.full(): 
          return True 
         elif not self.__queue.empty(): 
          return True 
         elif self.__timeout < time.clock(): 
          self.cancel() 
         else: 
          return False 
    
        @property 
        def value(self): 
         """Read-only property containing data returned from function.""" 
         if self.ready is True: 
          flag, load = self.__queue.get() 
          if flag: 
           return load 
          raise load 
         raise NotReadyError() 
    
        def __get_limit(self): 
         return self.__limit 
    
        def __set_limit(self, value): 
         if value <= 0: 
          raise ValueError() 
         self.__limit = value 
    
        limit = property(__get_limit, __set_limit, 
            doc="Property for controlling the value of the timeout.") 
    ## end of http://code.activestate.com/recipes/577045/ }}}