Reputation: 9465
I think I'm getting this error because my code calls asyncio.get_event_loop().run_until_complete(foo())
twice. Once from foo()
and second time from function called by foo()
. My question is then: why should this be a problem? Why should I even care that this loop is running?
There was an edit made to this question which, I think, obscured it (some people prefer to follow rules without understanding them, thus an "illegal" word was removed from the title). Unfortunately, this creates confusion.
I'm not surprised by the fact that the error is raised. I can trace it back to the asyncio
source and see that the authors of this library wanted to do it this way, there's no mystery there. The puzzling part is in the reason the authors of the library decided it's illegal to ask from event loop to run some function to completion when the loop is already running.
We can reduce the problem to just two such calls, and through case analysis we will see that these are the three possibilities:
Now, is there any sane behavior which would address all three cases? To me, it is obvious that there is, or, perhaps are multiple sane behaviors possible here. For example:
run_until_complete()
until second function completes (thus no code after run_until_complete()
will be executed.run_until_complete
ignoring all other invocation sites.Now, I can understand that this behavior may not be something that everyone would want. But, since this library decided to give programmers control over starting / stopping the event loop, it should also meet the consequences of such decisions. Making it an error to start the same loop multiple times precludes library code from ever doing this, which reduces the quality and usefulness of libraries utilizing asyncio
(which is indeed the case with, for example, aiohttp
).
Upvotes: 183
Views: 315776
Reputation: 6726
Using nest_asyncio
didn't work for me, because then aiohttp
started complaining with
RuntimeError: Timeout context manager should be used inside a task
Instead I decided to replace all calls to asyncio.run
with calls to this asyncio_run
:
def asyncio_run(future, as_task=True):
"""
A better implementation of `asyncio.run`.
:param future: A future or task or call of an async method.
:param as_task: Forces the future to be scheduled as task (needed for e.g. aiohttp).
"""
try:
loop = asyncio.get_running_loop()
except RuntimeError: # no event loop running:
loop = asyncio.new_event_loop()
return loop.run_until_complete(_to_task(future, as_task, loop))
else:
nest_asyncio.apply(loop)
return asyncio.run(_to_task(future, as_task, loop))
def _to_task(future, as_task, loop):
if not as_task or isinstance(future, asyncio.Task):
return future
return loop.create_task(future)
A secondary goal was to be able to think of asyncio.run
as promise.resolve
from the JS world, or Task.Wait
from the .NET world.
Edit: In our product we removed this code in favor of this other answer in a related question.
Upvotes: 13
Reputation: 2266
This might not solve the same problem but a similar one.
If the error is not produced by any of your custom files but by packages you install using pip
, proceed to read below because that was how I fixed mine.
I realized this error was due to package versions and compatibility.
So you might want to look at the stack trace and determine which libraries are involved and try upgrading or downgrading them, keeping in mind their compatibility.
If you are lucky enough pip install
might inform you of any incompatibilities.
Once again, in my case, I had to upgrade to
PyMySQL==1.1.0
aiomysql==0.2.0
Upvotes: 0
Reputation: 1649
Here is a general async-to-sync context manager I came up with based on this answer:
await def async_func():
...
def sync_func():
with sync_await() as await_:
x = await_(async_func())
It can actually be useful even in async context, e.g. for converting an async iterable to a regular (sync) iterable:
await def async_func(coroutines):
with sync_await() as await_:
# TypeError: 'async_generator' object is not iterable
# x = any(await c for c in coroutines)
x = any(map(await_, coroutines))
Upvotes: 1
Reputation: 1
I found that changing this:
filtered_cap = pyshark.FileCapture( <FILE_NAME>.pcap)
To this:
with pyshark.FileCapture( <FILE_NAME>.pcap) as filtered_cap:
Resolves the Cannot run the event loop while another loop is running
Error
Upvotes: -2
Reputation: 1816
I got the issue resolved by using the nest_async
pip install nest-asyncio
and adding below lines in my file.
import nest_asyncio
nest_asyncio.apply()
# EDIT: the __import__ line is not needed, leaving it just
# so that the comments make sense
# __import__('IPython').embed()
Upvotes: 167
Reputation: 1276
Sometimes the event loop is there and is running. In that case we don't need to run it again, we just need to create a task using create_task
try this
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
task = loop.create_task(user_insert_events(target))
if not loop.is_running():
loop.run_until_complete(task)
Upvotes: -1
Reputation: 11736
I'm writing this down not to patronize, but to explain how we can handle the situation where simply queueing async functions and awaiting their results synchronously while the event loop is running, doesn't work.
run_until_complete
is not for running any number of arbitrary async functions synchronously, it is for running the main entry point of your entire async program. This constraint is not immediately apparent from the docs.
Since libraries like aiohttp will queue it's own entry point to run as a server and block the loop's synchronous operations using run_until_complete
or run_forever
, the event loop will already be running and you won't be able to run independent synchronous operations on that event loop and wait for it's result within that thread.
That being said, if you have to queue an async operation into a running event loop from within a sync context and get it's result like a regular function, that may not be possible. One way is to pass in a synchronous callback to be called once the async operation finishes. That will of course slow down your event loop. Another way is to put the result in a queue and wait for it in a synchronous loop, emulating the event loop.
Another way of handling the situation is to execute your code within startup and cleanup callbacks of the async http library you're using. Here's a sample of how you may accomplish this.
Upvotes: 25
Reputation: 1816
I had the same problem and I fixed thsi issue using nest_async or asyncio
Solution #1
Simply install the package :
pip install nest-asyncio
Then add these lines:
import nest_asyncio
nest_asyncio.apply()
Solution #2
If it's not working with nest_asyncio, then try asyncio :
import asyncio
asyncio.set_event_loop(asyncio.new_event_loop())
Upvotes: 11
Reputation: 39546
Event loop running - is an entry point of your async program. It manages running of all coroutines, tasks, callbacks. Running loop while it's running makes no sense: in some sort it's like trying to run job executor from same already running job executor.
Since you have this question, I guess you may misunderstand a way how asyncio works. Please, read this article - it's not big and gives a good introduction.
Upd:
There's absolutely no problem in adding multiple things to be ran by event loop while this loop is already running. You can do it just by awaiting for it:
await coro() # add coro() to be run by event loop blocking flow here until coro() is finished
or creating a task:
# python 3.7+
asyncio.create_task(coro()) # add coro() to be run by event loop without blocking flow here
# This works in all Python versions but is less readable
asyncio.ensure_future(coro())
As you can see you don't need call event loop's methods to make something being ran by it.
Event loop's method such as run_forever
or run_until_complete
— are just a ways to start event loop in general.
run_until_complete(foo())
means: "add foo()
to be ran by event loop and run event loop itself until foo()
isn't done".
Upvotes: 69
Reputation: 628
Just add this bunch of code in the beginning
!pip install nest_asyncio
import nest_asyncio
nest_asyncio.apply()
Upvotes: 40