Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Coroutine as background job in Jupyter notebook

I have a coroutine which I'd like to run as a "background job" in a Jupyter notebook. I've seen ways to accomplish this using threading, but I'm wondering whether it's also possible to hook into the notebook's event loop.

For example, say I have the following class:

import asyncio
class Counter:
    def __init__(self):
        self.counter = 0

    async def run(self):
        while True:
            self.counter += 1
            await asyncio.sleep(1.0)

t = Counter()

and I'd like to execute the run method (which loops indefinitely), while still being able to check the t.counter variable at any point. Any ideas?

like image 555
Mark Avatar asked Apr 27 '16 16:04

Mark


People also ask

How do you make a Jupyter notebook run in the background?

You can put the process into the background by using jupyter notebook --no-browser & disown . You can close the terminal afterwards and the process will still be running. If you're using zsh you can also use a shorter version that does the same: jupyter notebook --no-browser &! .

What does %% capture do?

Capturing Output With %%capture IPython has a cell magic, %%capture , which captures the stdout/stderr of a cell. With this magic you can discard these streams or store them in a variable. By default, %%capture discards these streams. This is a simple way to suppress unwanted output.

How do I schedule a job in Jupyter notebook?

Open a Jupyter notebook from the left sidebar. Click on the Scheduler icon either from the left sidebar tab or from the top toolbar of the Jupyter notebook. The left sidebar displays the Schedule(s) and Run History tabs as shown below. To view the active schedules, click Schedule(s) tab.

How do you keep a running Jupyter notebook?

This can be done by typing jupyter notebook in the terminal, which will open a browser. Then, navigate to the respective jupyter notebook file in the browser and open it. Click Cell > Run All on the toolbar. All done!


3 Answers

The following basically does what I want I think, but it does use a separate thread. However, I can still use the async primitives.

def run_loop():
    loop = asyncio.new_event_loop()
    run_loop.loop = loop
    asyncio.set_event_loop(loop)
    task = loop.create_task(t.run())
    loop.run_until_complete(task)

from IPython.lib import backgroundjobs as bg
jobs = bg.BackgroundJobManager()
jobs.new('run_loop()')
loop = run_loop.loop # to access the loop outside
like image 75
Mark Avatar answered Sep 21 '22 19:09

Mark


I think your code works perfectly, you just need to create a task to wrap the coroutine, i.e. :

import asyncio
class Counter:
    def __init__(self):
        self.counter = 0

    async def run(self):
        while True:
            self.counter += 1
            await asyncio.sleep(1.0)

t = Counter()
asyncio.create_task(t.run())

wait 10 seconds, and check

t.counter

should get

> 10
like image 22
Maverick Meerkat Avatar answered Sep 20 '22 19:09

Maverick Meerkat


There's a simplified version of what Mark proposed:

from IPython.lib import backgroundjobs as bg
jobs = bg.BackgroundJobManager()
jobs.new(asyncio.get_event_loop().run_forever)

If you need, you can access the loop with asyncio.get_event_loop()

like image 24
Vinicius Fortuna Avatar answered Sep 22 '22 19:09

Vinicius Fortuna