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.
Related
I am currently working on a project where I am looking for information updates and then posting status messages to a slack channel. This is my first python project and I am a little out of the loop with what is going wrong. However, it appears that the RuntimeError: There is no current event loop in thread 'MainThread' error that I am getting is caused by having two async requests in my main function.
I was wondering if anyone would be able to tell me what best practice would be and how i could avoid any more issues?
def main():
configure()
print("the project has started")
asyncio.run(post_message("the project has started"))
event_filter = [my api call to another service goes here]
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(
asyncio.gather(
log_loop(event_filter, 2)))
finally:
# close loop to free up system resources
loop.close()
async def post_message(message):
try:
client = AsyncWebClient(token=os.getenv('SLACK_BOT_TOKEN'))
response = await client.chat_postMessage(channel='#notifications', text=message)
assert response["message"]["text"] == message
except SlackApiError as e:
assert e.response["ok"] is False
assert e.response["error"] # str like 'invalid_auth', 'channel_not_found'
print(f"Got an error: {e.response['error']}")
It seems to me that the call asyncio.run(post_message("the project has started")) is not playing well with my loop = asyncio.get_event_loop() but again I am unsure why.
Any help would be much appreciated!
EDIT
Here is the full traceback as requested:
Traceback (most recent call last):
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "myprojectpath/__main__.py", line 4, in <module>
app.main()
File "myprojectpath/app.py", line 54, in main
loop = asyncio.get_event_loop()
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'MainThread'.
This recreates your error:
import asyncio
async def test():
pass
def main():
asyncio.run(test())
loop = asyncio.get_event_loop()
loop.run_until_complete(test())
The issue is that asyncio.run creates an event loop, runs your coroutine, and then closes the event loop. Therefore you can't use that loop later on. Instead, change main into an async function and just use await OR use asyncio.run for your gather call. Try avoiding using the loop api unless you have to.
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.
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.
Making a Discord bot using discord.py, this is the first time I work with asyncio, and probably the first time I encountered something this frustrating in Python.
The point of this question isn't to teach me how to use asyncio, but instead to teach me how to avoid using it, even if it's not the right way to do things.
So I needed to run the discord client coroutines from regular def functions. After hours of searching I found this: asyncio.get_event_loop().run_until_complete(...). I set up a small script to test it out:
import asyncio
async def test():
print('Success')
asyncio.get_event_loop().run_until_complete(test())
And it worked perfectly. So I went ahead and tried to use it in a discord bot:
import discord
import asyncio
client = discord.Client()
#client.event
async def on_ready():
test()
def test():
asyncio.get_event_loop().run_until_complete(run())
async def run():
print('Success')
client.run('TOKEN_HERE')
And I got an error... Stacktrace/Output:
Success
Ignoring exception in on_ready
Traceback (most recent call last):
File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
test()
File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 11, in test
asyncio.get_event_loop().run_until_complete(run())
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
self.run_forever()
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 408, in run_forever
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
What's weird is that "Success" part at the end... I tried some other tests to see if I could return data from the coroutine or execute more stuff, but it couldn't.
I even tried replacing asyncio.get_event_loop() with client.loop, which didn't work either.
I looked for like 2 days, still no solution. Any ideas?
EDIT: Replacing get_event_loop() with new_event_loop() as suggested in the comments raised this:
Ignoring exception in on_ready
Traceback (most recent call last):
File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
test()
File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 11, in test
asyncio.new_event_loop().run_until_complete(run())
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
self.run_forever()
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 411, in run_forever
'Cannot run the event loop while another loop is running')
RuntimeError: Cannot run the event loop while another loop is running
Your problem seems to essentially be about mixing synchronous and asynchronous code. There are two possibilities:
1) If your non-async routines don't need to block, just to schedule some async task (e.g. send_message) to be run later, then they can simply call get_event_loop().create_task(). You can even use add_done_callback on the returned task if you want some other (non-async) routine to be called when the asynchronous operation is complete. (If the routine to be run is also non-async, then use get_event_loop().call_soon().)
2) If your non-async routines absolutely must block (which includes possibly awaiting an asynchronous routine), and cannot schedule the blocking operation for later, then you should not run them from the same thread as the main event loop. You can create a thread pool with concurrent.futures.ThreadPoolExecutor, and use asyncio.run_in_executor() to schedule your non-async routines, then await the result. And if they in turn need to call async routines, then run_until_complete() should work because now you're not running in a thread that already has an event loop. (But beware of threadsafety issues. You may need something like run_coroutine_threadsafe if you need to wait for something to run in the main event loop.)
If it helps, the asgiref package contains routines that can simplify this for you. They're designed for a slightly different purpose (web servers), but may also work for you. You can use await asgiref.sync.sync_to_async(func)(args) when you want to call a non-async routine from an async routine, which will run the routine in a thread pool, then use asgiref.sync.async_to_sync(func)(args) when you want to call an async routine from a non-async routine that's running inside that thread pool.
I am trying to make an API call from within my service and I am running into event loop issues. Can someone help me understand what I am doing wrong?
Basically I want to make a service that does some calculations based on data pulled from a different service.
I can call this code below from a cli, but not when I start up a web app (i.e.) hitting http://127.0.0.1:8080/add
loop = asyncio.get_event_loop()
data = loop.run_until_complete(run_fetch(loop, 'http://google.com'))
Sample code:
from aiohttp import web
import aiohttp
import asyncio
async def add(request):
loop = asyncio.get_event_loop()
data = loop.run_until_complete(run_fetch(loop, 'http://google.com'))
return web.json_response(data)
async def fetch(client, url):
async with client.get(url) as resp:
assert resp.status == 200
return await resp.text()
async def run_fetch(loop, url):
async with aiohttp.ClientSession(loop=loop) as client:
html = await fetch(client, url)
return html
app = web.Application()
app.router.add_get('/add', add)
web.run_app(app, host='127.0.0.1', port=8080)
Exception:
Error handling request
Traceback (most recent call last):
File ".../aiohttp/web_protocol.py", line 417, in start
resp = yield from self._request_handler(request)
File ".../aiohttp/web.py", line 289, in _handle
resp = yield from handler(request)
File ".../sample.py", line 11, in add data = loop.run_until_complete(run_fetch(loop, 'http://google.com'))
File ".../python3.6/asyncio/base_events.py", line 454, in run_until_complete
self.run_forever()
File ".../python3.6/asyncio/base_events.py", line 408, in run_forever
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
The run_until_complete is the way to run some async code from the sync context. Under the hood it adds provided future to the given ioloop and calls run_forever then returns result or throws exception (resolved future).
Actually you need to await run_fetch(loop, 'http://google.com'), since the caller function is asynchronous.