React to ipywidget events while the cell is blocket in an `await` (using `%autoawait` magic)

Viewed 144

I would like to wait to asynchronous jupyter widget events while the cell is blocked executing async code (thus, usually idle, waiting for I/O, utilising the %autowait magic). Unfortunately, the jupyter notebook does not seem to send any traitlet events while the cell is blocking. The documentation above circumvents this, by sending the callback into the "background", so it can react after the cell has finished executing. I would like to avoid that, because it make reasoning about background tasks harder than it needs to be; it's precisely against the principles of my favourite async event loop in python: trio. Is there a way to make that work?

Here is the example from the jupyter docs adapted to trio (though it fails the same way with asyncio):

import trio

from IPython.display import display
from ipywidgets import IntSlider, Output

%autoawait trio

async def wait_for_change(widget, attribute):
    result = None
    evt = trio.Event()
    def value_changed(change):
        nonlocal result
        result = change
        evt.set()
    widget.observe(value_changed, attribute)
    try:
        await evt.wait()
    finally:
        widget.unobserve(value_changed, attribute)
    return result

slider = IntSlider()
out = Output()

display(slider)
display(out)

# at this point, the widgets are visible

for i in range(10):
    out.append_stdout('did work ' + str(i) + '\n')
    # here, the cell blocks thanks to the `%autoawait` above -
    # but jupyter fails to forward any events
    x = await wait_for_change(slider, 'value')
    out.append_stdout('async function continued with value ' + str(x) + '\n')
0 Answers
Related