简体   繁体   中英

Cannot close a running event loop

What is the way to close the discord.py bot loop once tasks are done?

Added:

nest_asyncio.apply()

Tried:

bot.logout(), bot.close(), bot.cancel()

Code:

async def helper(bot, discord_user_feed):
    nest_asyncio.apply()
    await bot.wait_until_ready()
    # await asyncio.sleep(10)
    for each_id, events_list in discord_user_feed.items():
        discord_user = await bot.fetch_user(int(each_id))
        for each_one in events_list:
            msg_sent = True
            while msg_sent:
                await discord_user.send(each_one)
                msg_sent = False
    await bot.close()
    return 'discord messages sent'


async def discord_headlines(request):
    nest_asyncio.apply()
    discord_user_feed =  await get_details()
    bot = commands.Bot(command_prefix='!')
    bot.loop.create_task(helper(bot, discord_user_feed))
    message = bot.run('my_token')
    return HttpResponse(message)

I can be able to send messages to discord users using id and discord bot. But, even after, django view api is continuously running and getting the error. It needs to return a response message - discord messages sent.

Error:

Exception in callback <TaskWakeupMethWrapper object at 0x000001C852D2DA38>(<Future finis...SWdZVtXT7E']}>)
handle: <Handle <TaskWakeupMethWrapper object at 0x000001C852D2DA38>(<Future finis...SWdZVtXT7E']}>)>
Traceback (most recent call last):
  File "E:\sd\envs\port\lib\asyncio\events.py", line 145, in _run
    self._callback(*self._args)
KeyError: <_WindowsSelectorEventLoop running=True closed=False debug=False>
Internal Server Error: /api/v0/discord_headlines/
Traceback (most recent call last):
  File "E:\sd\envs\port\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "E:\sd\envs\port\lib\site-packages\django\core\handlers\base.py", line 179, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "E:\sd\envs\port\lib\site-packages\asgiref\sync.py", line 139, in __call__
    return call_result.result()
  File "E:\sd\envs\port\lib\concurrent\futures\_base.py", line 425, in result
    return self.__get_result()
  File "E:\sd\envs\port\lib\concurrent\futures\_base.py", line 384, in __get_result
    raise self._exception
  File "E:\sd\envs\port\lib\site-packages\asgiref\sync.py", line 204, in main_wrap
    result = await self.awaitable(*args, **kwargs)
  File "E:\back-end\port_api\views\discord_integration.py", line 110, in discord_headlines
    message = bot.run('my_token')
  File "E:\sd\envs\port\lib\site-packages\discord\client.py", line 719, in run
    _cleanup_loop(loop)
  File "E:\sd\envs\port\lib\site-packages\discord\client.py", line 95, in _cleanup_loop
    loop.close()
  File "E:\sd\envs\port\lib\asyncio\selector_events.py", line 107, in close
    raise RuntimeError("Cannot close a running event loop")
RuntimeError: Cannot close a running event loop
[13/Jun/2021 01:07:34] "GET /api/v0/discord_headlines/ HTTP/1.1" 500 109010

These are the versions:

Python 3.6.4, Django 3.1, discord.py 1.7.3, Asyncio, Anaconda-4.9.2, Windows-10

I've not used older versions so I can't attest to the accuracy of this answer for pre-2021 releases (v1.6.0+) Discord.py.

When starting a discord Client or Bot via run(*args, **kwargs) there is actually no class method to stop it.

Per the Discord.py Documentations Discord.py discord.Client.run :

A blocking call that abstracts away the event loop initialisation from you.

If you want more control over the event loop then this function should not be used. Use start() coroutine or connect() + login()

And it's effectively equal to:

# run(*args, **kwargs) is basically
try:
    # Start the bot on asyncio event loop with using .start() coroutine
    loop.run_until_complete(start(*args, **kwargs))
except KeyboardInterrupt:
    # Invoke close() coroutine on the same loop if interrupt
    loop.run_until_complete(close())
    # Cancel any other lingering tasks and such
finally:
    loop.close()

So the unrewarding but accurate answer with your current attempted implementation is simply... you can't.

If you wanna drop some more info on the webserver library your using in response or via a message, I can help craft you something functional.

If your webserver is fully asyncio integrated, your best bet is seeing if the library has a way to add tasks to its existing event loop, or if you pass one to it. Sanic allows for this in its decorator startup/shutdown methods (ex. after_server_start, etc). Store the reference to the bot instance somewhere and pass it around as needed.

I'll try and keep an eye on here for a response or DM and try to help get you functional and we can post updated code answer here, there's a big lack of solid answers for using discord.py in more complex cases within other applications/webservices and I just ran into a similiar issue with writing a discord.py bot that accepted webhook data and posted embeds to appropriate channels based on some filters.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM