Mettre un Timeout sur une fonction en python
Je vais vous présenter deux manières de limiter le temps d’éxécution d’une fonction/méthode en python.
La première est très élégante et inspirée de ce site, mais les bugs sont corrigés. Malheureusement dans un cas particulier (l’utilisation d’un wrapper vers des fonctions en C (swiglpk)) cela ne fonctionnait pas et j’ai dû trouver une autre solution.
La solution élégante
En premier lieu vous devez importer le module signal puis créer une nouvelle exception:
import signal
class TimeoutException(Exception):
pass
Ensuite, il faut créer le décorateur. C’est un peu plus compliqué à comprendre. Le décorateur va créer une alarme puis il va lancer la fonction à laquelle vous l’avez accolé. Si la durée d’éxécution est plus grande que la limite que vous avez définie alors un signal va être lancé par l’alarme. Le handler va récupérer le signal et va lancer une exception qui termine l’éxécution courante jusqu’à la fonction où vous la récupérez. Sinon l’alarme va être réinitialisée et le résultat du calcul de votre fonction va être renvoyé.
Cela ce code ainsi:
def deadline(timeout, *args):
"""is a the decotator name with the timeout parameter in second"""
def decorate(f):
""" the decorator creation """
def handler(signum, frame):
""" the handler for the timeout """
raise TimeoutException() #when the signal have been handle raise the exception
def new_f(*args):
""" the initiation of the handler,
the lauch of the function and the end of it"""
signal.signal(signal.SIGALRM, handler) #link the SIGALRM signal to the handler
signal.alarm(timeout) #create an alarm of timeout second
res = f(*args) #lauch the decorate function with this parameter
signal.alarm(0) #reinitiate the alarm
return res #return the return value of the fonction
new_f.__name__ = f.__name__
return new_f
Ensuite, ça s’utilise simplement comme ceci :
@deadline(1) #time limit of execution of 1 second
def longfunction(x, y):
res = 0
while True:
res += x*y
def call_of_longfunction():
try:
longfunction(1, 3)
except TimeoutException:
print "too long"
La solution robuste
En premier lieu vous devez importer Queue et Process du module multiprocessing :
from multiprocessing import Process, Queue
Puis vous pouvez créer la fonction prenant du temps. Au lieu de faire “return resultat” vous devez utiliser queue.put(resultat) et queue doit être un paramètre de votre fonction.
def longfunction(x, y, queue):
res = 0
while True:
res += x*y
queue.put(res)
Vous pouvez enfin créer la fonction qui va limiter la durée d’éxécution :
def call_of_longfunction():
queue = Queue() #using to get the result
proc = Process(target=longfunction, args=(1, 3, queue,)) #creation of a process calling longfunction with the specified arguments
proc.start() #lauching the processus on another thread
try:
res = queue.get(timeout=1) #getting the resultat under 1 second or stop
proc.join() #proper delete if the computation has take less than timeout seconds
except: #catching every exception type
proc.terminate() #kill the process
print "too long"
Il est peut être possible de créer un décorateur pour cette solution robuste, mais cela sera difficile à cause de l’utilisation de Queue et du tuple d’arguments de Process devant finir par “,”.
Mettre un Timeout sur une fonction en python par La Réponse est 42 est sous Licence Creative Commons Internationale Attribution-Partage à l'identique 4.0.
Merci pour le partage !
Dans la solution élégante, il manque juste une ligne. Il faut ajouter :
return decorate
à la fin de la fonction deadline() pour que çà marche.
(Sinon, on obtient le message TypeError: ‘NoneType’ object is not callable)
Amicalement,
François
Merci énormément !
Moi qui avait cette erreur depuis un bon moment je commençait a vouloir abandonner.
Merci beaucoup ^^