Generador de recuperación utilizando decorador
Tengamos una clase que tiene una función que falla de vez en cuando, pero después de algunas acciones simplemente funciona perfectamente.
El ejemplo de la vida real sería Mysql Query que plantea_mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')
pero después de la reconexión del cliente funciona bien.
He intentado escribir decorador para esto:
def _auto_reconnect_wrapper(func):
''' Tries to reconnects dead connection
'''
def inner(self, *args, _retry=True, **kwargs):
try:
return func(self, *args, **kwargs)
except Mysql.My.OperationalError as e:
# No retry? Rethrow
if not _retry:
raise
# Handle server connection errors only
# http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html
if (e.code < 2000) or (e.code > 2055):
raise
# Reconnect
self.connection.reconnect()
# Retry
return inner(self, *args, _retry=False, **kwargs)
return inner
class A(object):
...
@_auto_reconnect_wrapper
def get_data(self):
sql = '...'
return self.connection.fetch_rows(sql)
Y si el cliente pierde la conexión, se reconecta silenciosamente y todos están contentos.
Pero que pasa si quiero transformarget_data()
al generador (y usaryield
declaración):
@_auto_reconnect_wrapper
def get_data(self):
sql = '...'
cursor = self.connection.execute(sql)
for row in cursor:
yield row
cursor.close()
Bueno, el ejemplo anterior no funcionará porque la función interna ya devolvió el generador y se interrumpirá después de llamar primeronext()
.
Según tengo entendido si Python veyield
dentro del método solo cede el control de inmediato (sin ejecutar una sola declaración) y espera primeronext()
.
He logrado hacer que funcione reemplazando:
return func(self, *args, **kwargs)
Con:
for row in func(self, *args, **kwargs):
yield row
Pero tengo curiosidad por saber si hay una forma más elegante (más pitónica) de hacer esto.¿Hay alguna manera de hacer que Python ejecute todo el código hasta primeroyield
yentonces ¿Espere?
Soy consciente de la posibilidad de simplemente llamarreturn tuple(func(self, *args, **kwargs))
pero quiero evitar cargar todos los registros a la vez.