171 votes

Fonction d'expiration, si elle prend trop de temps pour finir

J'ai un script shell qui parcourt un fichier texte contenant des URL:s que je veux visiter et faire des captures d'écran.

Tout cela est fait et simple. Le script initialise une classe que lorsqu'il est exécuté crée une capture d'écran de chaque site dans la liste. Certains sites mettent très, très long à charger, et certains pourraient ne pas être chargé. Je tiens donc à envelopper le screengrabber fonction dans un délai de script, rendant le retour de la fonction False si elle n'avait pas pu terminer dans les 10 secondes.

Je suis content avec la solution la plus simple possible, peut-être mettre un asynchrone à rebours qui retourne la valeur False au bout de 10 secondes, peu importe ce qui se passe réellement à l'intérieur de la fonction?

Des idées?

Merci!

282voto

David Narayan Points 1550

Le processus pour le calendrier de sortie des opérations est décrit dans la documentation de signal.

L'idée de base est d'utiliser les gestionnaires de signaux pour définir une alarme pour un intervalle de temps et de soulever une exception une fois que la minuterie expire.

Notez que cela ne fonctionne que sur UNIX.

Voici une application qui crée un décorateur (enregistrez le code suivant en tant que timeout.py).

from functools import wraps
import errno
import os
import signal

class TimeoutError(Exception):
    pass

def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
    def decorator(func):
        def _handle_timeout(signum, frame):
            raise TimeoutError(error_message)

        def wrapper(*args, **kwargs):
            signal.signal(signal.SIGALRM, _handle_timeout)
            signal.alarm(seconds)
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result

        return wraps(func)(wrapper)

    return decorator

Cela crée un décorateur appelés @timeout qui peut être appliquée à toute la longue fonctions d'exécution.

Ainsi, dans le code de votre application, vous pouvez utiliser le décorateur de la sorte:

from timeout import timeout

# Timeout a long running function with the default expiry of 10 seconds.
@timeout
def long_running_function1():
    ...

# Timeout after 5 seconds
@timeout(5)
def long_running_function2():
    ...

# Timeout after 30 seconds, with the error "Connection timed out"
@timeout(30, os.strerror(errno.ETIMEDOUT))
def long_running_function3():
    ...

200voto

Thomas Ahle Points 10403

J'ai réécrit David réponse à l'aide de l' with déclaration, il vous permet de faire ceci:

with timeout(seconds=3):
    sleep(4)

Qui va élever un TimeoutError.

Le code est toujours à l'aide d' signals , et donc uniquement pour UNIX:

class timeout:
    def __init__(self, seconds=1, error_message='Timeout'):
        self.seconds = seconds
        self.error_message = error_message
    def handle_timeout(self, signum, frame):
        raise TimeoutError(self.error_message)
    def __enter__(self):
        signal.signal(signal.SIGALRM, self.handle_timeout)
        signal.alarm(self.seconds)
    def __exit__(self, type, value, traceback):
        signal.alarm(0)

5voto

theDmi Points 1307

La solution à cela est le multithreading: l'Exécution de votre site web chargeur de code dans un thread. Démarrer un deuxième thread horloge, qui envoie une interruption au thread de travail après 10s.

Jetez un oeil à l' Python Filetage API.

0voto

Jack Points 123

EDIT 1: Il ne fait pas travailler. Il n'a pas été supprimé parce que la logique peut être récupérable.

Version Thread-safe à l'aide d'enfilage minuteries:

import threading

class TimeoutError(Exception):
    pass

def die():
     raise TimeoutError()

death_counter = threading.Timer(60, die)
try:
    death_counter.start()
    some_operation()
    death_counter.cancel()
except TimeoutError:
    log_error('Timeout')

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X