115 votes

Existe-t-il un moyen pythonique d’essayer quelque chose jusqu’à un maximum de fois?

J'ai un script python qui est de l'interrogation d'un serveur MySQL sur un serveur partagé hôte linux. Pour une raison quelconque, les requêtes pour MySQL reviennent souvent un "server has gone away" erreur:

_mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')

Si vous essayez de la requête à nouveau immédiatement après, il réussit. Donc, j'aimerais savoir si il y a une manière sensible en python à essayer d'exécuter une requête, et si cela échoue, essayez à nouveau, jusqu'à un nombre fixe de la tente. Probablement que je veux pour essayer 5 fois avant d'abandonner complètement.

Voici le genre de code que j'ai:

conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

try:
    cursor.execute(query)
    rows = cursor.fetchall()
    for row in rows:
        # do something with the data
except MySQLdb.Error, e:
    print "MySQL Error %d: %s" % (e.args[0], e.args[1])

Clairement, je pouvais le faire en ayant une autre tentative à l'exception de la clause, mais c'est incroyablement laid, et j'ai le sentiment il doit y avoir une manière décente pour atteindre cet objectif.

136voto

Dana Points 9876

Que diriez-vous:

 conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()
attempts = 0

while attempts < 3:
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        break
    except MySQLdb.Error, e:
        attempts += 1
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])
 

84voto

dwc Points 12676

En vous appuyant sur la réponse de Dana, vous voudrez peut-être faire cela en tant que décoratrice:

 def retry(howmany):
    def tryIt(func):
        def f():
            attempts = 0
            while attempts < howmany:
                try:
                    return func()
                except:
                    attempts += 1
        return f
    return tryIt
 

Ensuite...

 @retry(5)
def the_db_func():
    # [...]
 

Version améliorée utilisant le module decorator

 import decorator, time

def retry(howmany, *exception_types, **kwargs):
    timeout = kwargs.get('timeout', 0.0) # seconds
    @decorator.decorator
    def tryIt(func, *fargs, **fkwargs):
        for _ in xrange(howmany):
            try: return func(*fargs, **fkwargs)
            except exception_types or Exception:
                if timeout is not None: time.sleep(timeout)
    return tryIt
 

Ensuite...

 @retry(5, MySQLdb.Error, timeout=0.5)
def the_db_func():
    # [...]
 

Pour installer le module decorator :

 $ easy_install decorator
 

8voto

webjunkie Points 2101
conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

for i in range(3):
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        break
    except MySQLdb.Error, e:
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])

7voto

Kiv Points 9116

Comme S.Lott, j'aime bien un drapeau pour vérifier si nous avons terminé:

 conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

success = False
attempts = 0

while attempts < 3 and not success:
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        success = True 
    except MySQLdb.Error, e:
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])
        attempts += 1
 

6voto

cdleary Points 18869

Je refacturerais ça comme ça:

 def callee(cursor):
    cursor.execute(query)
    rows = cursor.fetchall()
    for row in rows:
        # do something with the data

def caller(attempt_count=3, wait_interval=20):
    """:param wait_interval: In seconds."""
    conn = MySQLdb.connect(host, user, password, database)
    cursor = conn.cursor()
    for attempt_number in range(attempt_count):
        try:
            callee(cursor)
        except MySQLdb.Error, e:
            logging.warn("MySQL Error %d: %s", e.args[0], e.args[1])
            time.sleep(wait_interval)
        else:
            break
 

L'extinction de la fonction callee semble rompre la fonctionnalité, ce qui permet de voir facilement la logique métier sans s'embourber dans le code de nouvelle tentative.

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