I have a class named Client which tries to connect to a server via Websockets. I tried to make a decorator to retry if it couldn't connect to the server but I get the error
ValueError: a coroutine was expected, got <__main__.Client object at 0x000001BE86D71930>
The code is this:
def retry(fnc):
def f_retry(self):
tries = 2
delay = 2
backoff = 2
while tries > 1:
try:
return self.main()
except Exception as e:
msg = "%s, Retrying in %d seconds..." % (str(e), delay)
print(msg)
time.sleep(delay)
tries -= 1
delay *= backoff
return self.main()
return f_retry
class Client():
def __init__(self):
self.name= None
@retry
async def main(self):
try :
async with websockets.connect(
'ws://localhost:8000',
) as ws:
logging.info("Connected to the server")
except Exception as e:
print(e)
if __name__ == '__main__':
client = Client()
asyncio.run(client.main())
This algorithm is inspired from This other question
CodePudding user response:
Because you are decorating an async
function, you need to make your wrapper async
and await
the result of the function:
import asyncio
from functools import wrap
def retry(fnc):
@wraps(fnc)
async def f_retry(*args, **kwargs):
tries = 2
delay = 2
backoff = 2
while tries > 1:
try:
return await fnc(*args, **kwargs)
except Exception as e:
msg = "%s, Retrying in %d seconds..." % (str(e), delay)
print(msg)
await asyncio.sleep(delay)
tries -= 1
delay *= backoff
return await fnc(*args, **kwargs)
return f_retry
I've fixed a few more things:
- Use
functools.wraps
functools.wraps
helps decorators behave more like the functions they wrap. - Make it care about the argument
Instead of just usingself.main
, this is now more versatile, can be used for methods and functions with any arguments, and any name. - Fix
RecursionError
Previously, it would callself.main
, which was itself. Now it calls the passed function.