notebook: Can't invoke asyncio event_loop after tornado 5.0 update

On fresh python3.6 venv, after pip install jupyter && jupyter notebook and starting a new python3.6 notebook:

import asyncio

async def foo():
    return 42

asyncio.get_event_loop().run_until_complete(foo())

throws:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-5-3ad9bf216544> in <module>()
----> 1 asyncio.get_event_loop().run_until_complete(foo())

/usr/local/lib/python3.6/asyncio/base_events.py in run_until_complete(self, future)
    452         future.add_done_callback(_run_until_complete_cb)
    453         try:
--> 454             self.run_forever()
    455         except:
    456             if new_task and future.done() and not future.cancelled():

/usr/local/lib/python3.6/asyncio/base_events.py in run_forever(self)
    406         self._check_closed()
    407         if self.is_running():
--> 408             raise RuntimeError('This event loop is already running')
    409         if events._get_running_loop() is not None:
    410             raise RuntimeError(

RuntimeError: This event loop is already running

If I specify tornado==4.5.3 before pip install jupyter, it works fine

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 41
  • Comments: 73 (32 by maintainers)

Commits related to this issue

Most upvoted comments

@parmentelat pip3 install tornado==4.5.3 solved the issue

but wait, as far as I am concerned this means I can’t run anything tainted with asyncio in a notebook

which will asymptotically amount to saying, I can’t run anything in a notebook 😉

it that right, or am I missing something obvious ?

For those interested, I’ve just a created package called nest_asyncio that solves the problem by patching asyncio to allow nested event loops. To use it in a notebook is a matter of putting

import nest_asyncio
nest_asyncio.apply()

somewhere near the top.

I have the same issue…the only way I was able to make it work was:

import asyncio
loop = asyncio.get_event_loop()
loop.create_task(some_async_func())

heart = broken

to make min’s suggestion more concrete, I could work around this issue by just issuing

pip3 install tornado==4.5.3

edit: I expect the jupyter server needs to be restarted as well

It is working correctly with python3.7, Tornado 5.1 and ipykernel 4.8.2 on ArchLinux by manually updating python-ipykernel to the last version (not working with python-ipykernel-4.6.1)

i see this issue as closed. but seems the only solution to this is to pin versions of notebook and tornado. is that really the only solution?

We should have prereleases of IPython 7 and ipykernel 5 next week that will enable top-level async/await in IPython or a notebook.

Because ipykernel itself relies on asyncio, if we want to achieve the ability for users to call get_event_loop().run_until_complete(), we have just a few options:

  1. instruct users to use something like nest_asyncio (neat!)
  2. use nest_asyncio by default in ipykernel (probably simplest, but I can’t speak to how robust it is yet, since it seems to have only existed for a few hours)
  3. get off the mainloop, and run all of ipykernel in a background thread. We already do this for IOPub, and we could do it for the rest.

Personally, I view the fact that asyncio and tornado are running to be a feature, as user code can launch long-running coroutines on the main eventloop and they will keep running in the background. This would be challenging if the loop were in a thread.

So my inclination is for now, recommend nest_asyncio at the user-level and finish shipping ipykernel 5 / ipython 7 with top-level await / mainloop. We could even add a special exception handler for this RuntimeError to point folks at nest_asyncio or autoawait:

def recommend_nest_asyncio(shell, etype, value, tb, tb_offset=0):
    shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
    if "already running" in str(value):
        print("""
    You might want to check out `nest_asyncio`, which enables asyncio.run_until_complete() even though asyncio is already running.
    Or with IPython 7, you can `await future` at the top-level without calling `run_until_complete`.
""")
get_ipython().set_custom_exc((RuntimeError,), handle_runtime)

And then revisit the possibility of putting the main eventloop into a background thread at a future date.

At the risk of stating the obvious, I’d just like to outline the following discrepency between what I get in a terminal - be it python or ipython - and in a notebook

I am not sure that I quite understand the other discussion there ipython/ipython#10390 but regardless, it is my feeling that something is wrong at this very early point already.

screen shot 2018-03-28 at 09 42 28

image

Running notebook=6.0.0 does not solve the problem in our case (gremlinpython).

Downgrading notebook does:

pip install jupyter notebook==5.7.8 tornado==4.5.3

Yep, that’s expected. The kernel itself runs on an event loop, and as of Tornado 5.0, it’s using the asyncio event loop. So the asyncio event loop is always running in the kernel. As far as I know, we haven’t figured out a way to deal with this yet.

Thank you @satra. I understand and completely agree. It’s a very complex stack happening here, coupled with extremely varied technical levels. I have no intention of expecting folks to change how issues are reported. I just believe that redirection should happen as early in the analysis as possible and, over time, users will understand. That said, users are always welcome to start that process here (and rightly so).

@azag0 Exactly. Having the asyncio loop on the main thread always running basically breaks most asyncio-based apps/libraries out there. Any new comer to the asyncio world that tries to run the code of some asyncio tutorial on Jupyter will be incredibly frustrated as most sample code they find on the web (which usually uses loop.run_until_complete or loop.run_forever) just simply won’t run.

@Carreau I am not familiar with the internals of ipykernel, but is it possible to have the ipykernel/tornado asyncio loop running in a background thread instead of the main thread?

The new Jupyterhub has:

jupyterhub 0.9.2 has requirement tornado>=5.0, but you'll have tornado 4.5.3 which is incompatible.

after which I am unable to install the older version of tornado in the main environment of the Jupyterhub.

Does this mean that until this issue is solved, the root/main environment of any Jupyterhub>=0.9.2 (at least) is broken?

We are aware of the issue and working on making it easier to run async code within a notebook, without having to manipulate the event loop yourself.

Getting it to work was already challenging, having it function on multiple python version is far from being easy, and we lack people willing to test our in-progress pull request to give feedback. In particular on the IPython side:

And ipykernel side:

There a a lot of extremely subtle behavior, autoasync

Any help to do other tasks unrelated to this bug might give us some band with to focus on this, but it’s relatively complex code that requires multiple hours of focused time, which is becoming rare.

I agree with @satra, this issue seems like it should be open, not closed. The Python REPL and the IPython REPL work fine, jupyter notebook does not.

The problem still occurs with notebook 6.1.3.

@kevin-bates - we were able to update our notebooks with the nest_asyncio solution and that works now on binder

You can also use await foo(), if you go back to software releases from within the last two years!

On Thu, Nov 14, 2019, 01:41 PointCloudNiphon notifications@github.com wrote:

pip install jupyter notebook==5.7.8 tornado==4.5.3

thanks,your solution fit me well

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/jupyter/notebook/issues/3397?email_source=notifications&email_token=AAALCRHEZSAET5IVSAS75ETQTTXIVA5CNFSM4ETTCZS2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEEAYSIQ#issuecomment-553748770, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALCRDQBJJACKDH2D23U3LQTTXIVANCNFSM4ETTCZSQ .

@lmeyerov Have you tried:

import asyncio
import nest_asyncio
nest_asyncio.apply(loop=asyncio.get_event_loop())

I’m tempted to also say that asyncio is cooperative scheduling so these applications should give option to run on already existing loops. I understand the use case, it is just hard to figure out how to do the right thing automatically.

I agree on that point. For instance, with Aiohttp, rather then the blocking web.run_app(app), one can do

runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner)  # web server launched as a background task
await site.start()
# user code
await runner.cleanup()

Ideally, all asyncio Python packages should allow such use. Unfortunately, one cannot turn a package that doesn’t provide such functionality into a package that does easily. Even better, packages would provide an async context manager:

async with web.AppRunner(app) as runner:
    async with web.TCPSite(runner) as site:
        # user code

If this was common, the current situation with the Ipython kernel wouldn’t really be such an issue.

Quick update on conda:

While conda won’t allow downgrading to tornado==4.5.3 on Python 3.7:

$ conda create -y -n py37_3 python=3.7 ipykernel tornado=4.5.3
# UnsatisfiableError: The following specifications were found to be in conflict [...]

I have confirmed overriding tornado through pip does work:

$ conda create -y -n py37 python=3.7 ipykernel  # installs tornado 5.1
$ conda activate py37
$ pip install "tornado==4.5.3"  # Successfully installed tornado-4.5.3

I am not sure if this will cause some minor bugs elsewhere or if the conda dependency check is just being overly strict though.

Most asyncio packages have an entry point that deals with the loop. Asyncio itself has asyncio.run() which won’t work in the Ipython kernel at the moment. Aiohttp has web.run_app(). Other packages have other functions. These functions set up the loop, run the coroutines, and do application-specific nontrivial teardowns of the loop. Currently, one would have to go one step lower, and reimplement these methods in the Notebook. This is not feasible.

@Carreau Those integrations look really nice, but will they be strictly optional? In other words, will there be a way to have the asyncio loop NOT running at all on Jupyter Notebook going forward?

I use Jupyter as a scratchpad for development and as a developer of asyncio-based apps, not being able to run loop.run_forever or loop.run_until_complete (or the new asyncio.run in 3.7) from Jupyter pretty much makes it useless as a scratchpad for me. I am sticking to Python 3.6.6 with tornado 4.5.3 for now, but that clearly isn’t sustainable… (conda won’t let me install Python 3.7 with tornado 4.5.3)

@getzze

It is working correctly with python3.7, Tornado 5.1 and ipykernel 4.8.2 on ArchLinux by manually updating python-ipykernel to the last version (not working with python-ipykernel-4.6.1)

Running the code snippet in the first post of this thread with Python 3.7.0, Tornado 5.1 and ipykernel 4.8.2 still gives me the same “RuntimeError: This event loop is already running”.

Using the new asyncio.run method instead of run_until_complete gives a slightly different “RuntimeError: asyncio.run() cannot be called from a running event loop”.

The real problem is imho not with jupyter or tornado, but with this decision for asyncio.

I’d like to second this request for an update

As far as I am concerned, this is a major hindrance, as anything remotely useful tends to have some dosage of asyncio these days

Hey all, just wondering if there is there any update on this issue?

I’d be inclined to figure out a way to run the user’s coroutines on the existing event loop rather than starting a new thread. Threads cause all sorts of problems.

Probably related to the fact that

“On Python 3, IOLoop is always a wrapper around the asyncio event loop.”

as listed in “Backwards-compatibility notes” of tornado 5.0: http://www.tornadoweb.org/en/stable/releases/v5.0.0.html#backwards-compatibility-notes