Issue
I am making a Discord bot using discord.py and sometimes the script I use to run it needs to close out the bot. When I close it out without using signal handlers there is a lot of errors about a loop not closing, so I added a signal handler (using the code below) and inside I need to call client.close()
and client.logout()
, but the problem is those are async functions and thus require me to await them, but I can't await the functions since the signal handler can't be an async function.
Here is the code:
def handler():
print("Logging out of Discord Bot")
client.logout()
client.close()
sys.exit()
@client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
for signame in ('SIGINT', 'SIGTERM'):
client.loop.add_signal_handler(getattr(signal, signame),
lambda: asyncio.ensure_future(handler()))
Is there a way to either logout properly using the signal handler, or at least just silence the warnings and errors from inside the code so no errors are printed in the console.
Solution
Your approach is on the right track - since add_signal_handler
expects an ordinary function and not an async function, you do need to call ensure_future
(or its cousin create_task
) to submit an async function to run in the event loop. The next step is to actually make handler
async, and await
the coroutines it invokes:
async def handler():
print("Logging out of Discord Bot")
await client.logout()
await client.close()
asyncio.get_event_loop().stop()
Note that I changed sys.exit()
to explicit stopping of the event loop, because asyncio doesn't react well to sys.exit()
being invoked from the middle of a callback (it catches the SystemExit
exception and complains of an unretrieved exception).
Since I don't have discord to test, I tested it by changing the logout and close with a sleep:
import asyncio, signal
async def handler():
print("sleeping a bit...")
await asyncio.sleep(0.2)
print('exiting')
asyncio.get_event_loop().stop()
def setup():
loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
loop.add_signal_handler(getattr(signal, signame),
lambda: asyncio.create_task(handler()))
setup()
asyncio.get_event_loop().run_forever()
If you are starting the event loop using something other than run_forever
, such as asyncio.run(some_function())
, then you will need to replace loop.stop()
with the code that sets whatever event the main coroutine awaits. For example, if it awaits server.serve_forever()
on a server, then you'd pass the server object to handler
and call server.close()
, and so on.
Answered By - user4815162342
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.