Let's have a class that has function that fails from time to time but after some actions it just works perfectly.
Real life example would be Mysql Query that raises _mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')
but after client reconnection it works fine.
I've tried to write decorator for this:
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)
And if client loses connection it just silently reconnect and everybody is happy.
But what if I want to transform get_data()
to generator (and use yield
statement):
@_auto_reconnect_wrapper
def get_data(self):
sql = '...'
cursor = self.connection.execute(sql)
for row in cursor:
yield row
cursor.close()
Well, previous example won't work because inner function already returned generator and it will break after calling first next()
.
As I understand it if python sees yield
inside method it just yields control immediately (without executing one single statement) and waits for first next()
.
I've managed to make it work by replacing:
return func(self, *args, **kwargs)
With:
for row in func(self, *args, **kwargs):
yield row
But I'm curious whether there is more elegant (more pythonic) way to do this. Is there a way to make python run all the code up to first yield
and then wait?
I'm aware of possibility of just calling return tuple(func(self, *args, **kwargs))
but I want to avoid loading all records at once.