Reputation: 967
I have an flask app that closely follows fast.ai's render app: https://github.com/render-examples/fastai-v3. It uses asyncio to download the model while the page renders. The following code sets it up:
loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(setup_learner())] #setup_learner downloads the model
learn = loop.run_until_complete(asyncio.gather(*tasks))[0]
loop.close()
I noticed that when FLASK_DEBUG=1, the first line throws the error:
RuntimeError: There is no current event loop in thread 'Thread-1'.
But when FLASK_DEBUG=0, it doesn't. But the app is harder to debug. Has anyone come across this issue and what's causing it?
Upvotes: 4
Views: 731
Reputation: 3504
The reason this works with FLASK_DEBUG=0
but not FLASK_DEBUG=1
is because the app runs in the main thread while not in debug mode, but doesn't run in the main thread while in debug mode.
The relevant code is in asyncio.events.BaseDefaultEventLoopPolicy.get_event_loop:
def get_event_loop():
if (self._local._loop is None and
not self._local._set_called and
isinstance(threading.current_thread(), threading._MainThread)):
self.set_event_loop(self.new_event_loop())
if self._local._loop is None:
raise RuntimeError('There is no current event loop in thread %r.'
% threading.current_thread().name)
return self._local._loop
An event loop may be created only in the main thread with the current event loop policy: _UnixDefaultEventLoopPolicy
in my case. Note that _UnixDefaultEventLoopPolicy
subclasses BaseDefaultEventLoopPolicy
but does not override BaseDefaultEventLoopPolicy
.
Possible solutions:
loop = asyncio.new_event_loop()
asyncio.get_event_loop()
to create a new event loop in a thread other than the main thread by overriding the events.AbstractEventLoopPolicy.get_event_loop
method. The safest way to do this is probably to subclass whichever event loop policy your environment is using and override its get_event_loop
method, removing the main thread check. This way you retain all other policy behaviors, though the change may break some of these behaviors.Either way, you're creating an event loop for the thread. My standard inclination is to go for simplicity, so #1.
So why doesn't the app run in the main thread while in debug mode? First, there are two processes while running in debug mode: the Flask server and the debugger, flask run
and python pydevd.py
, respectively. The main thread of each process has an event loop that facilitates communication between the app server and the debugger - among other things, and spawns another thread in which the app actually runs. You'll also see this behavior without debug mode enabled if the application is being served by a multi-threaded app server, e.g. gunicorn or uwsgi.
Flask doesn't really support asyncio
. Sure one can use it with Flask, but no guarantees are made about their compatibility. See this issue, and more specifically: the issue referenced in its comments
Upvotes: 3