This recipe presents two ways to time out the execution of a callable. It relies on signal.SIGALRM; I've only tested in on MacOSX. One way (TimedOutFn) works on Python 2.3.4, and the second uses the decorator syntax introduced in 2.4a. In this version, I've used the code from John Speno's page (http://www.pycs.net/users/0000231/). He's done a better job of handling the signals, and I like the try/finally expression.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | import signal, time
class TimedOutExc(Exception):
def __init__(self, value = "Timed Out"):
self.value = value
def __str__(self):
return repr(self.value)
def TimedOutFn(f, timeout, *args, **kwargs):
def handler(signum, frame):
raise TimedOutExc()
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
def timed_out(timeout):
def decorate(f):
def handler(signum, frame):
raise TimedOutExc()
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
def fn_1(secs):
time.sleep(secs)
return "Finished"
@timed_out(4)
def fn_2(secs):
time.sleep(secs)
return "Finished"
@timed_out(2)
def fn_3(secs):
time.sleep(secs)
return "Finished"
@timed_out(2)
def fn_4(secs):
try:
time.sleep(secs)
return "Finished"
except TimedOutExc:
print "(Caught TimedOutExc, so cleaining up, and re-raising it) - ",
raise TimedOutExc
if __name__ == '__main__':
try:
print "fn_1 (sleep 2, timeout 4): ",
print TimedOutFn(fn_1, 4, 2)
except TimedOutExc:
print "took too long"
try:
print "fn_2 (sleep 2, timeout 4): ",
print fn_2(2)
except TimedOutExc:
print "took too long"
try:
print "fn_1 (sleep 4, timeout 2): ",
print TimedOutFn(fn_1, 2, 4)
except TimedOutExc:
print "took too long"
try:
print "fn_3 (sleep 4, timeout 2): ",
print fn_3(4)
except TimedOutExc:
print "took too long"
try:
print "fn_4 (sleep 4, timeout 2): ",
print fn_4(4)
except TimedOutExc:
print "took too long"
|
I needed to call a list of callables, and not have any one of them going on forever. It's pretty hard to tell by inspection whether a function is gong to take a long time to execute :), so the above recipe solved the problem for me. Each callable either runs to completion, or is stopped by the raising of an exception. You don't have any control over what part of the function will be running when the exception is raised, so it's important to "clean up after yourself", and fn_4 shows how to do that.
It's been developed and tested on OS X.
ack!!! print strings and code not in sync! This version 1) fixes the inconsistency between the printed strings and the code and 2) shows how the TimedOutException can be caught to do any cleaning up required:
I'd be grateful if the editors could perhaps replace the code I submitted with this (perhaps more useful and less confusing) version!
You can edit the original. Go to My Recipes and click the recipe's edit link.
Ignore the above comment! I've been hit by a clue stick, so have edited (and slightly improved) the recipe (by adding in **kwargs). My above comment is superfluous.
reset alarm? You have this code:
Does the typo'd signal.arlam() serve any purpose? I don't see how it could ever get executed.
Alarm remains active in case of exception. I would modify the try block in the wrapper function as below adding also signal.alarm(0) in the finally clause to reset the timer. Else the signal could trigger TimedOutExc in some outside block if 'f' raises some other exception.
how can i do this under WINDOWS platform i want to timeout execution of an exe program outside my python script