Asyncio "RuntimeError: Event loop is closed" - python

I'm trying to make a Discord Bot and it has to check an API every minute or so and then send a message to a channel.
But for some reason, the loop breaks and ends the task.
It's my first time using asyncio and discord.py
Traceback (most recent call last):
File "bot.py", line 207, in <module>
client.loop.run_forever()
File "/usr/local/lib/python3.6/asyncio/base_events.py", line 407, in run_forever
self._check_closed()
File "/usr/local/lib/python3.6/asyncio/base_events.py", line 358, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Task was destroyed but it is pending!
task: <Task pending coro=<my_background_task() running at bot.py:193> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0xb52dbd10>()]>>
The source code for the script is this one:
bot.py
Just want to have my_background_task() to run forever, or at least restart if it breaks.

Make the bot run the async task
async def bg():
for i in range(3):
print("Background tast")
await asyncio.sleep(1)
#bot.event
async def on_ready():
print("Ready!")
bot.loop.create_task(bg())

Related

discord.py heartbeat blocked on attempt to run with asyncio.run

The main idea is to make a discord integration to the existing telegram bot written with python-telegram-bot library. So here I was thinking about running multiple coroutines with asyncio.gather and asyncio.run (I'm using python 3.10).
Right now I'm not doing anything with my existing code. Instead to avoid any possible blocking code in my main app for now I decided to write this simple program which creates a bot and just push a message to logs if message is received:
import logging as log
import asyncio
import os
import discord
from discord.ext import commands
log.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=log.DEBUG)
client = commands.Bot(command_prefix="/")
#client.event
async def on_ready():
log.info('Connected to Discord')
#client.event
async def on_message(message):
log.info("Got message")
async def start_discord():
await client.start(os.environ.get('DISCORD_TOKEN'))
async def main():
await asyncio.gather(start_discord())
asyncio.run(main())
When I start it everything looks ok, but after a while I started to get exceptions that heartbeat is blocked and WebSocket connection timed out. In addition to that none of both events are triggered no matter how many messages are sent:
2022-07-04 17:50:52,378 - discord.gateway - WARNING - Shard ID None heartbeat blocked for more than 10 seconds.
Loop thread traceback (most recent call last):
File "/mnt/d/Documents/Projects/test.py", line 28, in <module>
asyncio.run(main())
File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.10/asyncio/base_events.py", line 633, in run_until_complete
self.run_forever()
File "/usr/lib/python3.10/asyncio/base_events.py", line 600, in run_forever
self._run_once()
File "/usr/lib/python3.10/asyncio/base_events.py", line 1860, in _run_once
event_list = self._selector.select(timeout)
File "/usr/lib/python3.10/selectors.py", line 469, in select
fd_event_list = self._selector.poll(timeout, max_ev)
But if I replace asyncio.run(main) with asyncio.get_event_loop().run_until_complete(main()) everything starts to work correctly. I see that both events are triggered and message is received. But as I see get_event_loop is deprecated and it's suggested to use asyncio.run() from 3.10:
/mnt/d/Documents/Projects/test.py:29: DeprecationWarning: There is no current event loop
asyncio.get_event_loop().run_until_complete(main())

RuntimeError: Cannot enter into task while anothe task is being executed

So i'm on Spyder trying to connect my discord bot and i'm running this code :
import discord
import nest_asyncio
nest_asyncio.apply()
client = discord.Client()
#client.event
async def on_ready():
print('The bot is ready')
#client.event
async def on_message(message):
if message.content.lower()=='ping':
await message.channel.send('pong')
client.run(token)
And i'm facing this error :
Exception in callback <TaskStepMethWrapper object at 0x000002A55BF8DF40>() handle: <Handle <TaskStepMethWrapper object at 0x000002A55BF8DF40>()>
Traceback (most recent call last):
File "C:\Users\33767\anaconda3\lib\asyncio\events.py", line 80, in _run
self._context.run(self._callback, *self._args)
RuntimeError: Cannot enter into task <ClientEventTask state=pending event=on_ready coro <function on_ready at 0x000002A55BEECE50>> while another task <Task pending name='Task-2' coro=<Kernel.dispatch_queue() running at C:\Users\33767\anaconda3\lib\site-packages\ipykernel\kernelbase.py:457> cb=[IOLoop.add_future.<locals>.<lambda>() at C:\Users\33767\anaconda3\lib\site-packages\tornado\ioloop.py:688]> is being executed.
Seems like there's a conflits between some sort of tasks, do you guys know how to solve this ?

Python, async, strange error after code is finished: Event loop is closed Process finished with exit code 0 [duplicate]

I am using asyncio and aiohttp to make concurrent requests. I've recently upgraded Python to version 3.8.0 and I'm getting a RuntimeError: Event loop is closed after the program has run.
import asyncio
import aiohttp
async def do_call(name, session):
async with session.get('https://www.google.be') as response:
await response.text()
return 'ok - {}'.format(name)
async def main():
async with aiohttp.ClientSession() as session:
tasks = [do_call(str(i), session) for i in range(0,4)]
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main())
I do get a valid result from asyncio.gather(), but when exiting the exception is raised.
I'd like to change the code so it doesn't run into exceptions.
The traceback is as follows:
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x000001E9A92079D0>
Traceback (most recent call last):
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\asyncio\proactor_events.py", line 116, in __del__
self.close()
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\asyncio\proactor_events.py", line 108, in close
self._loop.call_soon(self._call_connection_lost, None)
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 711, in call_soon
self._check_closed()
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 504, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed`
I think this is most likely an aiohttp bug. Specifically, I found this issue on their github: https://github.com/aio-libs/aiohttp/issues/4324
I realise this doesn't necessarily help you, but maybe you can switch back and stop banging your head against a wall. Your code is fine!
I solved this by not calling my_loop.close() after I was done using it. Closing the event loop this way caused the error to be thrown even after I had gotten all the responses I was expecting.

Python asyncio.Event class gives error with different tasks

I am trying setup a flag for my async loop. I tried using asyncio.Event but I get a RuntimeError
Here's my code:
import sys
import asyncio
import logging
root = logging.getLogger()
root.setLevel(logging.DEBUG)
ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(
logging.Formatter('%(asctime)s - %(name)s - %(levelname)s %(message)s')
)
root.addHandler(ch)
global event
event = asyncio.Event()
class AsyncTest():
async def bg(self):
root.debug('Waiting for 10 secs')
await asyncio.sleep(10)
event.set()
root.debug('Setted Event')
async def waiter(self):
event.clear()
root.debug('Created task')
asyncio.create_task(self.bg())
await asyncio.sleep(0)
root.debug('waiting')
await event.wait()
root.debug('Finished Waiting.')
return
async def main():
obj = AsyncTest()
await obj.waiter()
asyncio.run(main())
The bg courotine does a periodic task. Therefore it has to be run in asyncio.create_task() .
The waiter coroutine is called at multiple points to wait until the event is set.
So while running this i get this error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\kevin\AppData\Local\Programs\Python\Python37\lib\asyncio\runners.py", line 43, in run
return loop.run_until_complete(main)
File "C:\Users\kevin\AppData\Local\Programs\Python\Python37\lib\asyncio\base_events.py", line 587, in run_until_complete
return future.result()
File "C:\Python Programs\Python\KLibs\NeuralConnect\test\test.py", line 39, in main
await obj.waiter()
File "C:\Python Programs\Python\KLibs\NeuralConnect\test\test.py", line 32, in waiter
await event.wait()
File "C:\Users\kevin\AppData\Local\Programs\Python\Python37\lib\asyncio\locks.py", line 293, in wait
await fut
RuntimeError: Task <Task pending coro=<main() running at C:\Python Programs\Python\KLibs\NeuralConnect\test\test.py:39> cb=[_run_until_complete_cb() at C:\Users\kevin\AppData\Local\Programs\Python\Python37\lib\asyncio\base_events.py:157]> got Future <Future pending> attached to a different loop
can anyone point out what is wrong in my above implementation? Also how should I correct? Should abandon the asyncio.Event() part and then use a bool variable?
The problem using the bool flag variable is that I have to do:
cond = False
while not cond:
await asyncio.sleep(0.3)
I find this un-pythonic. Is there any better implementation of this, incase asyncio.Event isnt suitable?
Finally I recieved an answer to this problem from alex_normane who answered a question linked to this. It seems that the asyncio.Event object has to passed as parameter to the coroutine in create_task(). Plus the asyncio.Event object must be created within a coroutine as also stated by user4815162342.
The answer is here: https://stackoverflow.com/a/65353254/13574717

Having a Telepot Bot run in its own Thread

I wrote a telegram bot in python using the telepot library. The bot itself is working. Since I also have some other stuff I need to do periodically while running the bot (namely writing stuff in logs), I want the bot to run in its own thread.
The telegram bot's code is a file called telebot.py while the threading code is in one called plantbot.py
I first tried simply creating a new thread that calls my run_bot() function, which didn't work and gave me the following RuntimeError:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
self.run()
File "/usr/lib/python3.5/threading.py", line 862, in run
self._target(*self._args, **self._kwargs)
File "plantbot.py", line 12, in start_bot
telebot.run_bot()
File "/srv/plantbot/telebot.py", line 108, in run_bot
loop = asyncio.get_event_loop()
File "/usr/lib/python3.5/asyncio/events.py", line 671, in get_event_loop
return get_event_loop_policy().get_event_loop()
File "/usr/lib/python3.5/asyncio/events.py", line 583, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-1'.
Here is the code I used for that:
plantbot.py
def start_bot():
telebot.run_bot()
bot = threading.Thread(target=start_bot, daemon=True)
bot.start()
print("Bot started...")
bot.join()
telebot.py
[lots of unrelated code]
bot = telepot.aio.Bot(TOKEN)
def run_bot():
loop = asyncio.get_event_loop()
loop.create_task(MessageLoop(bot, {'chat': on_chat_message, 'callback_query': on_callback_query}).run_forever())
print('Listening ...')
loop.run_forever()
I then tried around a little with the event loops until I ended up creating the event loop in my main file (plantbot.py) which looked like this:
plantbot.py
def start_bot(loop):
telebot.run_bot(loop)
loop = asyncio.new_event_loop()
bot = threading.Thread(target=start_bot, args=(loop,), daemon=True)
bot.start()
print("Bot started...")
bot.join()
telebot.py
[...]
bot = telepot.aio.Bot(TOKEN)
def run_bot(loop):
loop.create_task(MessageLoop(bot, {'chat': on_chat_message, 'callback_query': on_callback_query}).run_forever())
print('Listening ...')
loop.run_forever()
I also tried another variation:
plantbot.py
def start_bot(loop):
asyncio.set_event_loop(loop)
loop.create_task(telebot.task.run_forever())
loop.run_forever()
loop = asyncio.new_event_loop()
bot = threading.Thread(target=start_bot, args=(loop,), daemon=True)
bot.start()
print("Bot started...")
bot.join()
telebot.py
[...]
bot = telepot.aio.Bot(TOKEN)
task = MessageLoop(bot, {'chat': on_chat_message, 'callback_query': on_callback_query})
Unfortunately, none of these worked.
My question is thus, how do I properly run an event loop in its own designated thread? Specifically: How do I run a telegram bot in its own thread?
I did end up finding a solution, though it's not a satisfying one:
I am now running the bot in the main thread and put everything else into other threads. It does work that way around.

Categories