views:

3189

answers:

6

I'm calling a function in Python which I know may stall and force me to restart the script. How do I call the function or what do I wrap it in so that if it takes longer than 5 seconds the script cancels it and does something else.

Thanks

+2  A: 

Maybe try to call it from other thread, which You could easily terminate.

Jacek Ławrynowicz
There is no method in the thread API for terminating a thread. The function must terminate normally for the thread to end, unless you want to resort to platform-specific hacks.
Brandon Corfman
oops :) that's a pitty
Jacek Ławrynowicz
+2  A: 

What yabcok said - start a new thread to call the function. In the original thread, sleep for 5 seconds, then terminate the function thread if it hasn't already ended.

Maybe there is a better approach to your problem? Why might the function take longer than 5 seconds?

Claudiu
+3  A: 

If this is some kind of network or file operation, you might also consider using nonblocking IO. This can be a better option if you're doing a lot of these types of operations at once (otherwise, you can bog your system down fairly quickly with a lot of threads). Here's a socket howto that covers nonblocking IO (in the context of network operations).

The downside? Well, it can be a pain to program. Sometimes even moreso than just using a thread.

Jason Baker
+7  A: 

I'm making some local xmlrpc calls with a timeout using the following code, borrowed from an ActiveState Cookbook recipe:

def timeout(func, args=(), kwargs={}, timeout_duration=10, default=None):
    """This function will spawn a thread and run the given function
    using the args, kwargs and return the given default value if the
    timeout_duration is exceeded.
    """ 
    import threading
    class InterruptableThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.result = default
        def run(self):
            self.result = func(*args, **kwargs)
    it = InterruptableThread()
    it.start()
    it.join(timeout_duration)
    if it.isAlive():
        return it.result
    else:
        return it.result

Invoking it with a 5 second timeout:

result = timeout(remote_calculate, (myarg,), timeout_duration=5)
Jeff Bauer
Doesn't Thread.join() just block until the timeout? If the thread doesn't terminate normally, it will continue to run in the background consuming CPU, perhaps for the lifetime of the app?
Brandon Corfman
is something wrong with last 4 lines? return result regardless of thread state? why check it then? should it be raise in the first branch?
roddik
Agree with Brandon Corfman here.
xitrium
A: 

I would use the time() method from time to compare the time while you're running your function, but clearly this only works if you'd be hitting an infinite loop, not a function hanging.

def meth():
    start_time = time()
    while(whatever):
        do_something
        if time() - smart_time > 5:
            return

But I'm just a small fry.

vgm64
+8  A: 

You may use the signal package

In [1]: import signal

# Register an handler for the timeout
In [2]: def handler(signum, frame):
   ...:     print "Forever is over!"
   ...:     raise Exception("end of time")
   ...: 

# This function *may* run for an indetermined time...
In [3]: def loop_forever():
   ...:     import time
   ...:     while 1:
   ...:         print "sec"
   ...:         time.sleep(1)
   ...:         
   ...:         

# Register the signal function handler
In [4]: signal.signal(signal.SIGALRM, handler)
Out[4]: 0

# Define a timeout for your function
In [5]: signal.alarm(10)
Out[5]: 0

In [6]: try:
   ...:     loop_forever()
   ...: except Exception, exc: 
   ...:     print exc
   ....: 
sec
sec
sec
sec
sec
sec
sec
sec
Forever is over!
end of time

# Cancel the timer if the function returned before timeout
# (ok, mine won't but yours maybe will :)
In [7]: signal.alarm(0)
Out[7]: 0

10 seconds after the call alarm.alarm(10), the handler is called. This raises an exception that you can intercept from the regular Python code.

This module doesn't play well with threads (but then, who does?)

piro
VHDL plays pretty well with the idea of threading because _everything_ is concurrent ;)
Teifion
Great solution. The advantage of this approach is that it can interrupt almost anything. The disadvantage is it requires python 2.5 or newer... all you luddites better use an alternative method.
Salim Fadhley
You should probably also record and restore the old value of the signal handler, returned from the signal.signal call. Also this seems to work for 2.4 also (yay RHEL...).
xitrium