J'appelle une fonction en Python qui je sais peut bloquer et me forcer à redémarrer le script.
Comment appeler la fonction ou comment l'envelopper pour que si cela prend plus de 5 secondes, le script l'annule et fasse autre chose?
J'appelle une fonction en Python qui je sais peut bloquer et me forcer à redémarrer le script.
Comment appeler la fonction ou comment l'envelopper pour que si cela prend plus de 5 secondes, le script l'annule et fasse autre chose?
Vous pouvez utiliser le package de signaux si vous exécutez sous UNIX:
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 secondes après l'appel alarm.alarm(10)
, le gestionnaire est appelé. Cela déclenche une exception que vous pouvez intercepter à partir du code Python standard.
Ce module ne fonctionne pas bien avec les threads (mais alors, qui le fait?)
Notez que puisque nous levons une exception lorsque le délai d'expiration se produit, elle peut finir par être interceptée et ignorée à l'intérieur de la fonction, par exemple d'une de ces fonctions:
def loop_forever():
while 1:
print('sec')
try:
time.sleep(10)
except:
continue
signal.alarm
et les éléments associésSIGALRM
ne sont pas disponibles sur les plates-formes Windows.signal.signal
--- fonctionnera-t-il tous correctement? Est-ce que chaquesignal.signal
appel n'en annulera pas un "simultané"?Vous pouvez utiliser
multiprocessing.Process
pour faire exactement cela.Code
la source
join()
. qui rend votre x nombre de sous - processus simultanés étant en cours d' exécution jusqu'à ce les terminer leur travail, ou un montant défini dansjoin(10)
. Si vous avez une E / S bloquante pour 10 processus, en utilisant join (10), vous les avez configurés pour les attendre tous au maximum 10 pour CHAQUE processus qui a démarré. Utilisez l'indicateur démon comme cet exemple stackoverflow.com/a/27420072/2480481 . Bien sûr, vous pouvez passer le drapeaudaemon=True
directement pourmultiprocessing.Process()
fonctionner.terminate() ... Note that exit handlers and finally clauses, etc., will not be executed. Note that descendant processes of the process will not be terminated – they will simply become orphaned.
J'ai posté un résumé qui résout cette question / problème avec un décorateur et un
threading.Timer
. Le voici avec une panne.Importations et configurations pour la compatibilité
Il a été testé avec Python 2 et 3. Il devrait également fonctionner sous Unix / Linux et Windows.
D'abord les importations. Ceux-ci tentent de garder le code cohérent quelle que soit la version de Python:
Utilisez un code indépendant de la version:
Maintenant, nous avons importé nos fonctionnalités de la bibliothèque standard.
exit_after
décorateurEnsuite, nous avons besoin d'une fonction pour terminer le à
main()
partir du thread enfant:Et voici le décorateur lui-même:
Usage
Et voici l'utilisation qui répond directement à votre question sur la sortie après 5 secondes!:
Démo:
Le deuxième appel de fonction ne se terminera pas, à la place, le processus devrait se terminer avec une trace!
KeyboardInterrupt
n'arrête pas toujours un fil endormiNotez que le sommeil ne sera pas toujours interrompu par une interruption clavier, sur Python 2 sous Windows, par exemple:
il n'est pas non plus susceptible d'interrompre le code en cours d'exécution dans les extensions à moins qu'il ne le vérifie explicitement
PyErr_CheckSignals()
, voir Cython, Python et KeyboardInterrupt ignorésJ'éviterais de dormir un thread plus d'une seconde, en tout cas - c'est une éon en temps de processeur.
Pour l'attraper et faire autre chose, vous pouvez attraper le KeyboardInterrupt.
la source
thread.interrupt_main()
, pourquoi ne puis-je pas lever directement une exception?multiprocessing.connection.Client
? - Essayer de résoudre: stackoverflow.com/questions/57817955/…J'ai une proposition différente qui est une fonction pure (avec la même API que la suggestion de filetage) et semble fonctionner correctement (basée sur les suggestions de ce fil)
la source
timeout
. Il est préférable de définir la valeur par défaut surNone
et, sur la première ligne de la fonction, d'ajouterkwargs = kwargs or {}
. Args est correct car les tuples ne sont pas modifiables.J'ai parcouru ce fil lors de la recherche d'un appel de délai d'attente sur les tests unitaires. Je n'ai rien trouvé de simple dans les réponses ou les packages tiers, j'ai donc écrit le décorateur ci-dessous, vous pouvez le déposer directement dans le code:
Ensuite, c'est aussi simple que cela pour expirer un test ou une fonction que vous aimez:
la source
Exception
intérieur de func_wrapper et fairepool.close()
après la capture pour vous assurer que le thread meurt toujours après quoi qu'il arrive . Ensuite, vous pouvez lancerTimeoutError
ou ce que vous voulez après. Semble fonctionner pour moi.RuntimeError: can't start new thread
. Cela fonctionnera-t-il toujours si je l'ignore ou est-ce que je peux faire autre chose pour contourner cela? Merci d'avance!Le
stopit
package, trouvé sur pypi, semble bien gérer les délais d'attente.J'aime le
@stopit.threading_timeoutable
décorateur, qui ajoute untimeout
paramètre à la fonction décorée, qui fait ce que vous attendez, il arrête la fonction.Découvrez-le sur pypi: https://pypi.python.org/pypi/stopit
la source
Il y a beaucoup de suggestions, mais aucune n'utilise simult.futures, ce qui, à mon avis, est la façon la plus lisible de gérer cela.
Super simple à lire et à entretenir.
Nous créons un pool, soumettons un seul processus, puis attendons jusqu'à 5 secondes avant de déclencher une TimeoutError que vous pouvez intercepter et gérer comme bon vous semble.
Natif de python 3.2+ et rétroporté à 2.7 (pip install futures).
Basculer entre les threads et les processus est aussi simple que de le remplacer
ProcessPoolExecutor
parThreadPoolExecutor
.Si vous souhaitez mettre fin au processus à l'expiration du délai, je vous suggère d'examiner Pebble .
la source
Générateur de délai d'expiration du projet PyPi , facile à utiliser et fiable ( https://pypi.org/project/timeout-decorator/ )
installation :
Utilisation :
la source
Je suis l'auteur de wrapt_timeout_decorator
La plupart des solutions présentées ici fonctionnent à merveille sous Linux à première vue - parce que nous avons fork () et signaux () - mais sur Windows, les choses semblent un peu différentes. Et quand il s'agit de sous-threads sur Linux, vous ne pouvez plus utiliser de signaux.
Pour générer un processus sous Windows, il doit être sélectionnable - et de nombreuses fonctions décorées ou méthodes de classe ne le sont pas.
Vous devez donc utiliser un meilleur pickler comme l'aneth et le multiprocess (pas le pickle et le multiprocessing) - c'est pourquoi vous ne pouvez pas utiliser ProcessPoolExecutor (ou seulement avec des fonctionnalités limitées).
Pour le délai lui-même - Vous devez définir ce que signifie le délai d'attente - car sous Windows, il faudra un temps considérable (et non déterminable) pour lancer le processus. Cela peut être délicat sur de courts délais. Supposons que la génération du processus prenne environ 0,5 seconde (facilement !!!). Si vous donnez un délai de 0,2 seconde, que devrait-il se passer? La fonction doit-elle expirer après 0,5 + 0,2 seconde (alors laissez la méthode s'exécuter pendant 0,2 seconde)? Ou le processus appelé devrait-il expirer après 0,2 seconde (dans ce cas, la fonction décorée expire TOUJOURS, car pendant ce temps, elle n'est même pas générée)?
Les décorateurs imbriqués peuvent également être méchants et vous ne pouvez pas utiliser de signaux dans un sous-fil. Si vous voulez créer un décorateur multiplateforme véritablement universel, tout cela doit être pris en considération (et testé).
D'autres problèmes transmettent des exceptions à l'appelant, ainsi que des problèmes de journalisation (s'ils sont utilisés dans la fonction décorée - la journalisation dans des fichiers dans un autre processus n'est PAS prise en charge)
J'ai essayé de couvrir tous les cas marginaux, vous pouvez examiner le package wrapt_timeout_decorator, ou au moins tester vos propres solutions en vous inspirant des tests non utilisés.
@Alexis Eggermont - malheureusement, je n'ai pas assez de points à commenter - peut-être que quelqu'un d'autre peut vous en informer - je pense avoir résolu votre problème d'importation.
la source
timeout-decorator
ne fonctionne pas sur le système Windows car Windows ne prend pas en chargesignal
bien.Si vous utilisez timeout-decorator dans le système Windows, vous obtiendrez les éléments suivants
Certains ont suggéré d'utiliser
use_signals=False
mais n'ont pas fonctionné pour moi.L'auteur @bitranox a créé le package suivant:
Échantillon de code:
Donne l'exception suivante:
la source
from wrapt_timeout_decorator import *
semble tuer certaines de mes autres importations. Par exempleModuleNotFoundError: No module named 'google.appengine'
, j'obtiens , mais je n'obtiens pas cette erreur si je n'importe pas wrapt_timeout_decoratorNous pouvons utiliser des signaux pour la même chose. Je pense que l'exemple ci-dessous vous sera utile. C'est très simple par rapport aux threads.
la source
try: ... except: ...
sont toujours une mauvaise idée.la source
J'avais besoin de interruptions chronométrées emboîtables (que SIGALARM ne peut pas faire) qui ne seront pas bloquées par time.sleep (ce que l'approche basée sur les threads ne peut pas faire). J'ai fini par copier et modifier légèrement le code d'ici: http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/
Le code lui-même:
et un exemple d'utilisation:
la source
Voici une légère amélioration de la solution basée sur les threads donnée.
Le code ci-dessous prend en charge les exceptions :
L'invoquer avec un délai de 5 secondes:
la source
runFunctionCatchExceptions()
certaines fonctions Python l'obtention de GIL était appelée. Par exemple , le suivant ne serait jamais, ou très longtemps, le retour si elle est appelée dans la fonction:eval(2**9999999999**9999999999)
. Voir stackoverflow.com/questions/22138190/…