I am writing a class and want to use an async function in the __init__ method to set up some variables needed for the class. The problem is, I can't do that, because __init__ has to be synchronous.
Here is the relevant part of my code (edited for simplicity, the logic remains the same):
# This has to be called outside of the class
asyncDatabaseConnection = startDBConnection()
class discordBot(discord.Client):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Init is only run once, but we cant use async stuff here
self.firstRun = True
async def on_ready(self):
# Other stuff happens here but it doesen't involve this question
# on_ready is called when bot is ready, but can be called multiple times when running
# (if bot has to reconnect to API), so we have to check
if self.firstRun:
await asyncDatabaseConnection.setValue("key", "value")
self.firstRun = False
if __name__ == "__main__":
# Instance class and start async stuff
bot = discordBot()
bot.run()
As you can see, it is for a Discord bot, but that doesn't really matter, it's more about the logic.
The function I want to call is asyncDatabaseConnection.setValue("key", "value").
Like I said, I cannot call it from __init__ because __init__ has to be synchronous, so instead I set firstRun to True during the init call, and then I can use that later on to tell if the code has been run before or not
on_ready is a function that is called when the bot is ready to start sending/receiving data, so I can use it as sort of a second __init__. The problem comes from the fact that on_ready can be called multiple times throughout the running of the program, meaning I have to have the firstRun check I described before.
This seems like a lot of code just to do 1 thing at startup (as well as adding overhead, however small, when on_ready is called). Is there a cleaner way of doing this?
It's a little awkward, but you can create a Task, then run it and get its result. If you do this often, it may help to write a helper function:
def run_and_get(coro):
task = asyncio.create_task(coro)
asyncio.get_running_loop().run_until_complete(task)
return task.result()
class discordBot(discord.Client):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
run_and_get(asyncDatabaseConnection.setValue("key", "value"))
This depends on there being a running event loop, which I believe Client.__init__ sets up
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With