2011-12-07 24 views
7

tengo este decorador:Python: ventanas equivalente de SIGALRM

def timed_out(timeout): 
    def decorate(f): 
     if not hasattr(signal, "SIGALRM"): 
      return f 

     def handler(signum, frame): 
      raise TimedOutExc() 

     @functools.wraps(f) 
     def new_f(*args, **kwargs): 
      old = signal.signal(signal.SIGALRM, handler) 
      signal.alarm(timeout) 
      try: 
       result = f(*args, **kwargs) 
      finally: 
       signal.signal(signal.SIGALRM, old) 
      signal.alarm(0) 
      return result 

     new_f.func_name = f.func_name 
     return new_f 

    return decorate 

El código sólo hace nada en Linux, sin embargo, como en las ventanas, no hay SIGALRM. ¿Cuál sería la forma más simple de hacer que este código también funcione en Windows?

Respuesta

8

No es muy bonito, pero tuve que hacer algo similar de forma multiplataforma, y ​​se me ocurrió usar un hilo por separado. Los sistemas basados ​​en señales no funcionaron en todas las plataformas de manera confiable.

El uso de esta clase podría incluirse en un decorador o en un controlador de contexto with.

YMMV.

#!/usr/bin/env python2.7 
import time, threading 

class Ticker(threading.Thread): 
    """A very simple thread that merely blocks for :attr:`interval` and sets a 
    :class:`threading.Event` when the :attr:`interval` has elapsed. It then waits 
    for the caller to unset this event before looping again. 

    Example use:: 

    t = Ticker(1.0) # make a ticker 
    t.start() # start the ticker in a new thread 
    try: 
     while t.evt.wait(): # hang out til the time has elapsed 
     t.evt.clear() # tell the ticker to loop again 
     print time.time(), "FIRING!" 
    except: 
     t.stop() # tell the thread to stop 
     t.join() # wait til the thread actually dies 

    """ 
    # SIGALRM based timing proved to be unreliable on various python installs, 
    # so we use a simple thread that blocks on sleep and sets a threading.Event 
    # when the timer expires, it does this forever. 
    def __init__(self, interval): 
    super(Ticker, self).__init__() 
    self.interval = interval 
    self.evt = threading.Event() 
    self.evt.clear() 
    self.should_run = threading.Event() 
    self.should_run.set() 

    def stop(self): 
    """Stop the this thread. You probably want to call :meth:`join` immediately 
    afterwards 
    """ 
    self.should_run.clear() 

    def consume(self): 
    was_set = self.evt.is_set() 
    if was_set: 
     self.evt.clear() 
    return was_set 

    def run(self): 
    """The internal main method of this thread. Block for :attr:`interval` 
    seconds before setting :attr:`Ticker.evt` 

    .. warning:: 
     Do not call this directly! Instead call :meth:`start`. 
    """ 
    while self.should_run.is_set(): 
     time.sleep(self.interval) 
     self.evt.set() 
0

Me parece que este código de tiempo de decodificación también es muy útil. (Originalmente lo encontré en esta respuesta a la pregunta: How to limit execution time of a function call in Python)

Para que funcione en Windows, utilizo el Python que se instala con Cygwin.

Ejecuto setup-x86_64.exe, luego selecciono el paquete python3 de la carpeta Python. (O, si lo prefiere Python 2, el paquete python.)

Para cambiar el nombre python3 a python2, defino el alias

alias python=python3 

desde el símbolo del sistema Cygwin. Como no uso esta funcionalidad muy a menudo, probablemente no la colocaré en .bashrc ni nada.

pregunta relacionada: Python signal don't work even on Cygwin?

+0

Podría alguien señalar lo que está mal con esta respuesta? No veo ningún problema con eso yo mismo. (Soy el autor) –