En Python essayez jusqu'à ce qu'aucune erreur

J'ai un morceau de code en Python qui semble provoquer une erreur probabiliste car il accède à un serveur et parfois ce serveur a une erreur de serveur interne 500. Je veux continuer à essayer jusqu'à ce que je n'ai pas l'erreur. Ma solution était:

while True:
    try:
        #code with possible error
    except:
         continue
    else:
         #the rest of the code
         break

Cela me semble être un hack. Y a-t-il un moyen plus pythonique de le faire?

42
demandé sur murgatroid99 2011-01-05 20:08:34

9 réponses

Il ne sera pas beaucoup plus propre. Ce n'est pas une chose très propre à faire. Au mieux (ce qui serait de toute façon plus lisible, puisque la condition pour le break est là-haut avec le while), Vous pouvez créer une variable result = None et la boucle pendant qu'elle is None. Vous devez également ajuster les variables et vous pouvez remplacer continue par le pass sémantiquement peut-être correct (vous ne vous souciez pas si une erreur se produit, vous voulez juste l'ignorer) et supprimer le break - cela obtient également le reste du code, qui ne s'exécute qu'une fois, de la boucle. Notez également que les clauses bare except: sont mauvaises pour des raisons données dans la documentation .

Exemple incorporant tout ce qui précède:

result = None
while result is None:
    try:
        # connect
        result = get_data(...)
    except:
         pass
# other code that uses result but is not involved in getting it
39
répondu User that is not a user 2018-02-17 18:41:27

Peut-être quelque chose comme ceci:

connected = False

while not connected:
    try:
        try_connect()
        connected = True
    except ...:
        pass
18
répondu mouad 2011-01-05 17:12:19

En voici un qui échoue après 4 tentatives, et attend 2 secondes entre les tentatives. Changez comme vous souhaitez obtenir ce que vous voulez former celui-ci:

from time import sleep

for x in range(0, 4):  # try 4 times
    try:
        # msg.send()
        # put your logic here
        str_error = None
    except Exception as str_error:
        pass

    if str_error:
        sleep(2)  # wait for 2 seconds before trying to fetch the data again
    else:
        break

Voici un exemple avec l'interruption:

from time import sleep

sleep_time = 2
num_retries = 4
for x in range(0, num_retries):  
    try:
        # put your logic here
        str_error = None
    except Exception as str_error:
        pass

    if str_error:
        sleep(sleep_time)  # wait before trying to fetch the data again
        sleep_time *= 2  # Implement your backoff algorithm here i.e. exponential backoff
    else:
        break
13
répondu radtek 2017-09-26 17:37:51

Voici une fonction utilitaire que j'ai écrite pour envelopper la nouvelle tentative jusqu'au succès dans un paquet plus propre. Il utilise la même structure de base, mais empêche la répétition. Il pourrait être modifié pour attraper et repousser l'exception sur l'essai final relativement facilement.

def try_until(func, max_tries, sleep_time):
    for _ in range(0,max_tries):
        try:
            return func()
        except:
            sleep(sleep_time)
    raise WellNamedException()
    #could be 'return sensibleDefaultValue'

Peut alors être appelé comme ceci

result = try_until(my_function, 100, 1000)

Si vous avez besoin de passer des arguments à my_function, vous pouvez soit le faire en envoyant try_until les arguments, soit en les enveloppant dans un lambda sans argument:

result = try_until(lambda : my_function(x,y,z), 100, 1000)
2
répondu Harry Harrison 2015-11-18 23:58:49

Le itertools.iter_except recettes encapsule cette idée de "l'appel d'une fonction à plusieurs reprises jusqu'à ce qu'une exception est levée". C'est similaire à la réponse acceptée, mais la recette donne un itérateur à la place.

Des recettes:

def iter_except(func, exception, first=None):
    """ Call a function repeatedly until an exception is raised."""
    try:
        if first is not None:
            yield first()            # For database APIs needing an initial cast to db.first()
        while True:
            yield func()
    except exception:
        pass

Vous pouvez certainement implémenter ce dernier code directement. Pour plus de commodité, j'utilise une bibliothèque séparée, more_itertools, cela implémente cette recette pour nous (facultatif).

Code

import more_itertools as mit

list(mit.iter_except([0, 1, 2].pop, IndexError))
# [2, 1, 0]

Détails

Ici, la méthode pop (ou fonction donnée) est appelée pour chaque itération de l'objet list jusqu'à ce qu'un IndexError soit déclenché.

Pour votre cas, compte tenu de l'erreur connect_function et attendue, vous pouvez créer un itérateur qui appelle la fonction à plusieurs reprises jusqu'à ce qu'une exception soit déclenchée, par exemple

mit.iter_except(connect_function, ConnectionError)

À ce stade, traitez-le comme n'importe quel autre itérateur en le bouclant ou en appelant next().

2
répondu pylang 2018-07-05 00:10:20

Peut-être décorateur basé? Vous pouvez passer comme arguments de décorateur liste des exceptions sur lesquelles nous voulons réessayer et / ou nombre de tentatives.

def retry(exceptions=None, tries=None):
    if exceptions:
        exceptions = tuple(exceptions)
    def wrapper(fun):
        def retry_calls(*args, **kwargs):
            if tries:
                for _ in xrange(tries):
                    try:
                        fun(*args, **kwargs)
                    except exceptions:
                        pass
                    else:
                        break
            else:
                while True:
                    try:
                        fun(*args, **kwargs)
                    except exceptions:
                        pass
                    else:
                        break
        return retry_calls
    return wrapper


from random import randint

@retry([NameError, ValueError])
def foo():
    if randint(0, 1):
        raise NameError('FAIL!')
    print 'Success'

@retry([ValueError], 2)
def bar():
    if randint(0, 1):
        raise ValueError('FAIL!')
    print 'Success'

@retry([ValueError], 2)
def baz():
    while True:
        raise ValueError('FAIL!')

foo()
bar()
baz()

Bien sûr, la partie 'try' devrait être déplacée vers une autre fonction car nous l'utilisons dans les deux boucles mais c'est juste un exemple;)

1
répondu virhilo 2011-01-05 20:38:37

Comme la plupart des autres, je recommande d'essayer un nombre fini de fois et de dormir entre les tentatives. De cette façon, vous ne vous trouvez pas dans une boucle infinie au cas où quelque chose arriverait réellement au serveur distant.

Je recommande également de continuer uniquement lorsque vous obtenez l'exception spécifique que vous attendez. De cette façon, vous pouvez toujours gérer les exceptions auxquelles vous ne vous attendez peut-être pas.

from urllib.error import HTTPError
import traceback
from time import sleep


attempts = 10
while attempts > 0:
    try:
        #code with possible error
    except HTTPError:
        attempts -= 1
        sleep(1)
        continue
    except:
        print(traceback.format_exc())

    #the rest of the code
    break

En outre, vous n'avez pas besoin d'un bloc else. En raison de la suite dans le bloc except, vous sautez le reste de la boucle jusqu'à ce que le bloc try fonctionne, la condition while est satisfaite ou une exception autre que HTTPError apparaît.

1
répondu noname 2018-03-30 20:29:16
e = ''
while e == '':
    try:
        response = ur.urlopen('https://https://raw.githubusercontent.com/MrMe42/Joe-Bot-Home-Assistant/mac/Joe.py')
        e = ' '
    except:
        print('Connection refused. Retrying...')
        time.sleep(1)

Cela devrait fonctionner. Il définit e sur "et la boucle while vérifie si elle est toujours". S'il y a une erreur détectée par l'instruction try, elle imprime que la connexion a été refusée, attend 1 seconde et recommence. Il continuera jusqu'à ce qu'il n'y ait pas d'erreur dans try, qui définit ensuite e sur ' ', ce qui tue la boucle while.

1
répondu user9311010 2018-04-02 14:50:39

Voici un petit morceau de code que j'utilise pour capturer l'erreur sous forme de chaîne. Va réessayer jusqu'à ce qu'il réussisse. Cela attrape toutes les exceptions mais vous pouvez changer cela comme vous le souhaitez.

start = 0
str_error = "Not executed yet."
while str_error:
    try:
        # replace line below with your logic , i.e. time out, max attempts
        start = raw_input("enter a number, 0 for fail, last was {0}: ".format(start))
        new_val = 5/int(start)
        str_error=None
    except Exception as str_error:
         pass

Avertissement: ce code sera bloqué dans une boucle forever jusqu'à ce qu'aucune exception ne se produise. Ceci est juste un exemple simple et peut vous obliger à sortir de la boucle plus tôt ou dormir entre les tentatives.

0
répondu radtek 2014-05-30 18:37:23