Comment ajouter un délai d'attente pour une fonction en Python
De nombreuses tentatives ont été faites dans le passé pour ajouter un délai d'attente de la fonctionnalité de Python telle que lorsqu'un certain délai expiré, l'attente de code pourrait passer. Malheureusement, les précédentes recettes soit permis à l'exécution de la fonction pour poursuivre l'exécution et la consommation de ressources ou d'autre tué la fonction à l'aide d'une plate-forme spécifique méthode de la fin des threads. Le but de ce wiki est de développer une croix-plate-forme de réponse à ce problème que de nombreux programmeurs ont dû s'attaquer à divers projets de programmation.
#! /usr/bin/env python
"""Provide way to add timeout specifications to arbitrary functions.
There are many ways to add a timeout to a function, but no solution
is both cross-platform and capable of terminating the procedure. This
module use the multiprocessing module to solve both of those problems."""
################################################################################
__author__ = 'Stephen "Zero" Chappell <[email protected]>'
__date__ = '11 February 2010'
__version__ = '$Revision: 3 $'
################################################################################
import inspect
import sys
import time
import multiprocessing
################################################################################
def add_timeout(function, limit=60):
"""Add a timeout parameter to a function and return it.
It is illegal to pass anything other than a function as the first
parameter. If the limit is not given, it gets a default value equal
to one minute. The function is wrapped and returned to the caller."""
assert inspect.isfunction(function)
if limit <= 0:
raise ValueError()
return _Timeout(function, limit)
class NotReadyError(Exception): pass
################################################################################
def _target(queue, function, *args, **kwargs):
"""Run a function with arguments and return output via a queue.
This is a helper function for the Process created in _Timeout. It runs
the function with positional arguments and keyword arguments and then
returns the function's output by way of a queue. If an exception gets
raised, it is returned to _Timeout to be raised by the value property."""
try:
queue.put((True, function(*args, **kwargs)))
except:
queue.put((False, sys.exc_info()[1]))
class _Timeout:
"""Wrap a function and add a timeout (limit) attribute to it.
Instances of this class are automatically generated by the add_timeout
function defined above. Wrapping a function allows asynchronous calls
to be made and termination of execution after a timeout has passed."""
def __init__(self, function, limit):
"""Initialize instance in preparation for being called."""
self.__limit = limit
self.__function = function
self.__timeout = time.clock()
self.__process = multiprocessing.Process()
self.__queue = multiprocessing.Queue()
def __call__(self, *args, **kwargs):
"""Execute the embedded function object asynchronously.
The function given to the constructor is transparently called and
requires that "ready" be intermittently polled. If and when it is
True, the "value" property may then be checked for returned data."""
self.cancel()
self.__queue = multiprocessing.Queue(1)
args = (self.__queue, self.__function) + args
self.__process = multiprocessing.Process(target=_target,
args=args,
kwargs=kwargs)
self.__process.daemon = True
self.__process.start()
self.__timeout = self.__limit + time.clock()
def cancel(self):
"""Terminate any possible execution of the embedded function."""
if self.__process.is_alive():
self.__process.terminate()
@property
def ready(self):
"""Read-only property indicating status of "value" property."""
if self.__queue.full():
return True
elif not self.__queue.empty():
return True
elif self.__timeout < time.clock():
self.cancel()
else:
return False
@property
def value(self):
"""Read-only property containing data returned from function."""
if self.ready is True:
flag, load = self.__queue.get()
if flag:
return load
raise load
raise NotReadyError()
def __get_limit(self):
return self.__limit
def __set_limit(self, value):
if value <= 0:
raise ValueError()
self.__limit = value
limit = property(__get_limit, __set_limit,
doc="Property for controlling the value of the timeout.")
Edit: Ce code a été écrit pour Python 3.x et n'a pas été conçu pour les méthodes de la classe comme une décoration. Le multiprocessing
module n'a pas été conçu pour modifier des instances de classe à travers le processus de limites.
- Que la gestion des exceptions ne fonctionne qu'en Python 3. En 2.x, il va jeter l'origine de la trace de pile, montrer l'exception, comme provenant de la "relance", et l'assertion de ne pas s'afficher dans la trace de la pile à tous.
Vous devez vous connecter pour publier un commentaire.
Le principal problème avec votre code est l'utilisation excessive de la double trait de soulignement de l'espace de noms de la prévention des conflits dans une classe qui n'est pas destiné à être sous-classée à tous.
En général,
self.__foo
est une odeur de code qui doit être accompagné par un commentaire sur les lignes de# This is a mixin and we don't want arbitrary subclasses to have a namespace conflict
.Plus de l'API cliente de cette méthode pourrait ressembler à ceci:
Ce n'est pas très pythonic à tous et un meilleur client api serait:
Vous utilisez @biens dans votre classe pour quelque chose qui est un état de la mutation de l'accesseur, ce n'est pas une bonne idée. Par exemple, qu'arriverait-il quand .la valeur est accessible à deux fois? On dirait qu'il échouerait à cause de la file d'attente.get() serait de retour corbeille, car la file d'attente est déjà vide.
Supprimer @propriété entièrement. Ne pas l'utiliser dans ce contexte, il n'est pas adapté à votre cas d'utilisation. Faire appel bloc lorsqu'il est appelé et le retour de la valeur ou de lever l'exception de lui-même. Si vous devez vraiment avoir de la valeur accéder ultérieurement à, en faire une méthode comme .get() ou .valeur().
Ce code pour le _target doit être réécrit un peu:
De cette façon, la trace de la pile sera correctement conservés et visibles par le programmeur.
Je pense que vous avez fait raisonnable première fissure à la rédaction d'un utile bibliothèque, j'aime l'utilisation du module de traitement pour atteindre les objectifs.
C'est de savoir comment obtenir le décorateur syntaxe Jerub mentionné
add_timeout
fonction, finit par créer.La Galets de la bibliothèque a été conçue pour offrir de la croix-plate-forme de mise en œuvre capable de traiter avec des problèmes de logique qui pourrait accident, erreur ou d'exécuter indéfiniment.
Le décorateur travaille aussi bien avec de la statique et les méthodes de la classe. Je recommande de ne pas décorer les méthodes néanmoins, comme il est très sujette aux erreurs pratique.
Cette question a été posée sur il y a 9 ans, et Python a changé une quantité décente depuis que a mon répertoire de l'expérience. Après avoir examiné d'autres Api de la bibliothèque standard, et de vouloir répliquer en partie un en particulier, le module a été écrit pour servir le même but que celui affiché dans la question.
asynchronous.py