Output() Widget Behavior and Javascript Source

I am working to debug Thebe, a Javascript package which essentially allows you to insert executable Jupyter Notebook cells into a static HTML page. I was following the ipywidgets documentation and noticed that Thebe produces some incorrect output with the Output() widget;

Thebe

We suspect that Thebe’s implementation of output.js or manager.js is not working properly. In terms of Jupyter “jargon”, I believe that it has something to do with the WidgetModel and WidgetView. I believe Thebe may currently be creating a new WidgetModel for each WidgetView that is created, rather than properly attaching the new WidgetViews to the previously created WidgetModel. Furthermore, clear_output() does not work at all.

The expected behavior is shown in JupyterLab (without clear_output());

JupyterLab

I would like to know where the relevant source code Javascript files are in JupyterLab which handle this sort of behavior. If we can take a look at that source code, then maybe we can modify Thebe’s implementation and get it to function properly with the Output() widget. I believe @SylvainCorlay may have some idea on how to fix this?

See ipywidgets/output.ts at e0d41f6f02324596a282bc9e4650fd7ba63c0004 · jupyter-widgets/ipywidgets · GitHub

Also, here is the implementation for the html widget manager: ipywidgets/output.ts at e0d41f6f02324596a282bc9e4650fd7ba63c0004 · jupyter-widgets/ipywidgets · GitHub

1 Like

Thanks for the pointer, this was helpful. I’m curious about what exactly is the difference between these two output.ts files? What is the purpose of output.ts in the html-manager vs output.ts in jupyterlab_widgets?

Each frontend needs to provide its own output widget, since the output widget will be rendering arbitrary outputs from Jupyter, and each frontend may have its own way for rendering output.

I see, that does make sense. Our one remaining problem with Thebe is that the clear_output() function is only called on the first user modification to a widget, and afterwards it is no longer called and there is no updating of the widget output when using interact(). I’ve tried to crudely represent this behavior in the following image (I added console.log to output.js and manager.js in order to display which methods are being called);

Based on comparing Thebe’s output.js and ipywidget’s output.ts, I’m curious if the setOutputs() function and its related code is what is making Thebe not work because it is not present in Thebe. Would you or anyone else happen to know if a lack of this section of the code could cause the issues that we are seeing in Thebe? I see a call to this.clear_output() within this segment of code, which is what leads me to believe that Thebe may need it as well.

@jasongrout To add on to what @sandertyu explained, we investigated more and put more console logs in the output.js and got the results as displayed in the image below:

As is shown in the image, the first time when a change happens and it works, onMsgIdChange() function is called. This points out that there was a change in the msgId and the msgType becomes ‘clear_output’ which is when the clear_output() function is called. There are several steps which happen after that like the msgType changing to display_data, comm_msg, and so on. But none of this happens in any subsequent changes we make to the slider. Only the callbacks() function runs. It never goes to onMsgIdChange() and the msgId never gets changed.

Is there any documentation that explains the typescript files? There is a javascript implementation in thebe, typescript implementation in ipywidgets, and python implementations in viola and nbclient. We’re struggling to make sense of the difference in the implementations across the three languages. Are there docstrings and such that explain all the classes and their methods in the typescript files?

Additionally, the first change to the slider which actually works goes through another file services-shim.js which is the comm API and handles the communication between the front-end and the kernel. As can be seen from the image, it first goes through the send() function which sends a message to the sibling comm in the backend. Then, it goes to the _hookupCallbacks() function in the same file which hooks the callback object up with the kernel. But again none of this happens for any subsequent changes to the slider. So maybe the change is not even getting registered with the kernel since the data is not getting sent.