2

I'm trying to put a timeout on a function send.

I have found some elements in these posts :

The first one seems to apply to every function and not to a precise one, this is why I chose a decorator implementation like the second one.

I tried to mix it up and I have this :

from functools import wraps
import os
import signal

class TimeoutError(Exception):
    pass

def timeout_func(error_message="Timeout in send pipe!"):
    def decorator(func):
        def _handle_timeout(signum, frame):
            if args[0].action=="warn":
                print "WARNING : ",error_message
            elif args[0].action=="kill":
                raise TimeoutError(error_message)

        def wrapper(*args, **kwargs):
            print args
            signal.signal(signal.SIGALRM, _handle_timeout,args[0].action)
            signal.alarm(args[0].seconds)
            print str(args)
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result
        return wraps(func)(wrapper)
    return decorator

class Link(object):
    def __init__(self,timeout=1,action="warn"):
        self.timeout=timeout
        self.action=action

    @timeout_func
    def send(self,value):
        print "working : ", value

It gives me this :

In [6]: l=Link()

In [7]: l.send(1) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in () ----> 1 l.send(1)

TypeError: decorator() takes exactly 1 argument (2 given)

My issue is that I would like to pass the timeout value second to the decorator through the Link's self. I don't fully understand the whole decorator mechanism here, and can't figure out what is wrong.

Can someone explain me how this decorator works, and what should I modify to fix it? Or if you think of a simpler/more explicit solution to implement it ?

Community
  • 1
  • 1
CoMartel
  • 3,521
  • 4
  • 25
  • 48

1 Answers1

0

So I've been debugging my issue, and I found a working solution:

from functools import wraps
import signal

class TimeoutError(Exception):
    pass

def timeout_func(f):
    def _handle_timeout(signum, frame):
        raise TimeoutError("timeout error")

    def wrapper(*args):
        signal.signal(signal.SIGALRM, _handle_timeout)
        signal.setitimer(signal.ITIMER_REAL,args[0].timeout) #args[0] is self of Link class here
        try:
            result = f(*args,**kwargs)
        finally:
            signal.alarm(0)
        return result
    return wrapper


class Link(object):
    def __init__(self,timeout=0.1,action="warn"):
        self.timeout=timeout
        self.action=action

    def send(self,value): # I use this func to handle the exceptions
        try:
            self.send_timeout(value) # call the timeout function
        except TimeoutError as e: # handle Timeout exception
            if self.action=="warn":
                print "WARNING : Timeout error in pipe send!"
            elif self.action=="kill":
                print "Killing Link : ", e
                raise
        except (Exception,KeyboardInterrupt) as e:
            print "Exception in link : ", e
            raise

    @timeout_func # apply timeout decorator to this function
    def send_timeout(self,value):
        # DO STUFF HERE 

To call it :

l=Link()
l.send("any value")

I use signal.setitimer(signal.ITIMER_REAL,args[0].timeout) because it allows setting a timeout < 1 second, wich was not the case with signal.signal(), wich only accept integer as timer.

CoMartel
  • 3,521
  • 4
  • 25
  • 48