Skip to content

Allows lab manager to load state from kernel/notebook #2265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Apr 12, 2019

Conversation

vidartf
Copy link
Member

@vidartf vidartf commented Nov 15, 2018

When connecting to a new kernel in lab, this code now:

  • Loads all live widgets from the kernel.
  • Loads all stored widgets from the notebook (where the comm id does not overlap with a live one).

This attempts to mirror the logic in the widgetsnbextension code, and mostly the code is a hard copy (not DRY as such, and maintainers should note the possible need to keep these in sync in the future).

@vidartf
Copy link
Member Author

vidartf commented Nov 15, 2018

One issue for a TODO:

  • When restarting kernel, widgets are not set as disconnected.

@vidartf
Copy link
Member Author

vidartf commented Nov 16, 2018

Note, this resolves a TODO on get_model that might not be fully backwards compatible.

… reused, we use the same widget manager.

This makes widgets work across multiple views of a notebook, since they all share the same widget manager.
…endering widgets until the appropriate widget manager is set.

This allows a notebook to open and start ‘rendering’ widgets as soon as it opens, and the real widget rendering will actually happen asynchronously after the widget manager is created.
@jasongrout
Copy link
Member

Note, this resolves a TODO on get_model that might not be fully backwards compatible.

Unfortunately, this makes it backwards incompatible, which technically means we should bump the base package up a major version number. It feels like we should have a bigger carrot for a major version bump in the base package.

How about we separate these two changes, so we can merge the jlab manager changes in immediately with essentially no change in base, and we take up fixing the get_model function in the next base major release?

(I'm okay with the typings enhancements you did in base, as well as the function you added, so I imagine this would be a minor base version bump, just not a major one yet.)

@jasongrout
Copy link
Member

Unfortunately, this makes it backwards incompatible, which technically means we should bump the base package up a major version number.

The reason I think we should have a bigger reason to bump a major version is that a major bump of base requires everyone to release their widgets again (presuming they use the standard ^1 semver for base).

Maybe this conflation points us to having the widget manager base as a separate package from the widgets base package in some future version.

@jasongrout
Copy link
Member

@vidartf - FYI, I'm working on further enhancements to this PR...

@vidartf
Copy link
Member Author

vidartf commented Mar 5, 2019

@jasongrout OK, give me a ping when you are satisfied, and I'll have a look.

vidartf and others added 14 commits March 5, 2019 08:32
Move to base manager so it can be reused.
Remaining, know issues:
- When restarting kernel, widgets are not set as disconnected.
- Maybe a problem of deadlock for interdependent widgets? Maybe it was
like that from before?
Calling code should handle the case where get_model returns a rejected promise, so subclasses may still choose to return a rejected promise if the model is not found.
* Convert many functions to use async/await
* Rework the restoring promise and initial restore logic
@jasongrout
Copy link
Member

I rebased on master to pull in changes needed to work with the current jlab prerelease.

@jasongrout
Copy link
Member

If the widget save setting is 'false', should we clear the existing widget state in the notebook, or just leave it alone? Or perhaps we should clear it from the model after we load it?

Also, does it seem reasonable to only save live widgets? This may be a good first attempt. This may mean that opening a notebook with saved state, running just a few cells, and saving means that other cells with saved widgets then stop working. Perhaps it's better to somehow save live widgets plus any widgets referenced from a view (hard)? Or perhaps any widgets that have been displayed at least once (again, probably hard)? Or perhaps we just have an option to save all widget state, including unseen and unused widgets (easy, but confusing, I think)?

@jasongrout
Copy link
Member

And another possible TODO: right now, if we can't find a widget model, we just leave a "Loading widget..." message. Perhaps we should start a timeout when we do that, so that if the model doesn't load after a while (5 seconds?), we just explain there is an error, rather than having the user keep waiting for the widget to load.

@jasongrout
Copy link
Member

And another possible TODO: right now, if we can't find a widget model, we just leave a "Loading widget..." message. Perhaps we should start a timeout when we do that, so that if the model doesn't load after a while (5 seconds?), we just explain there is an error, rather than having the user keep waiting for the widget to load.

I did this in the latest commit.

@jasongrout
Copy link
Member

Wrapping this up until we can discuss with @vidartf (or whoever else wants to review it). This I think requires a minor version bump in base and a major version bump in jlab manager.

@jasongrout jasongrout added this to the Minor release milestone Mar 6, 2019
Copy link
Member Author

@vidartf vidartf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few points to discuss.

// For any widgets that have already been rendered with the placeholder, set
// the renderer. This iteration structure is closely tied to the structure of
// the notebook widget.
nb.widgets.forEach(cell => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop is tied to the DOM, and won't pick up on e.g. "New view for output" that can be restored on layout.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just pushed a commit that (a) refactors the awkward logic into a self-contained iterator, and (b) attempts to also iterate through linked views, using even more awkward logic that relies on specific jlab details. It's hard for me to test, though, since linked output views aren't restored in the latest jlab release.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(note that both of these awkward iterators are just an interim solution until there might be a more supported clean way of iterating through renderers in jlab itself)

* Set the dirty state of the notebook model if applicable.
*
* TODO: should also set dirty when auto-save state is turned on (easy), and when any
* model changes any data (likely hard right now).
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: Just before merging, if this TODO note is still here, it should be replaced by an issue.

@vidartf
Copy link
Member Author

vidartf commented Mar 15, 2019

This may mean that opening a notebook with saved state, running just a few cells, and saving means that other cells with saved widgets then stop working.

Not really, as widgets in the state will be restored (will become live). Your scenario will only happen if you are working super fast (and/or the restore is super slow). Still, it could leave to some weird behavior.

// error message to the user.
setTimeout(() => {
if (this._rerenderMimeModel) {
this.node.textContent = 'Error displaying widget: model not found';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should tweak this error message to indicate that, while no actual error has occurred, getting the widget state is taking a lot longer than expected.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a commit that tries to make the error messages more intelligent, based on whether the manager has restored its status or not.

@jasongrout
Copy link
Member

@vidartf - I think I addressed the issues you raised above. Thoughts now?

@jasongrout
Copy link
Member

This may mean that opening a notebook with saved state, running just a few cells, and saving means that other cells with saved widgets then stop working.

Not really, as widgets in the state will be restored (will become live). Your scenario will only happen if you are working super fast (and/or the restore is super slow). Still, it could leave to some weird behavior.

I was talking about dead widgets that only live in the notebook metadata, not restored widgets from the kernel.

@vidartf
Copy link
Member Author

vidartf commented Mar 28, 2019

Not really, as widgets in the state will be restored (will become live).

I was mistaken about this, widgets in the notebook without a kernel equivalent, will not become live (they won't get a comm).

Copy link
Member Author

@vidartf vidartf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add some minor typings, and also have a few minor points that should be checked before merging. @jasongrout Would you mind checking those?

_handleKernelStatusChange(args: Kernel.Status) {
switch (args) {
case 'connected':
// TODO: should we clear away any old widgets before restoring?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we answer this? If so, we should change this comment to explain what we chose (and why).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also reset this._restoredStatus here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the only thing left to resolve here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so. I.e. both the TODO, and reviewing if this._restoredStatus should ever be reset (and if so, when and where).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// TODO: should we clear away any old widgets before restoring?

YES

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// TODO: should we clear away any old widgets before restoring?

YES

😏 we were discussing this in person with @jasongrout.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and reset the restored status in disconnect. I think it makes most sense to reset it there, since a reconnect will then restore widgets again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this actually done? Since you force pushed, the commit log doesn't show any partial diffs for this, so its super hard to track what was changed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, and in general this PR was already hard to track being so long and involved. Are you asking if the TODO was fixed, or if the restored status was reset?

@@ -78,7 +98,7 @@ class BackboneViewWrapper extends Widget {
*/
export
class WidgetManager extends ManagerBase<Widget> implements IDisposable {
constructor(context: DocumentRegistry.IContext<DocumentRegistry.IModel>, rendermime: RenderMimeRegistry) {
constructor(context: DocumentRegistry.IContext<INotebookModel>, rendermime: RenderMimeRegistry, settings: WidgetManager.Settings) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for self: support the second argument in Voila's widget manager.

_handleKernelStatusChange(args: Kernel.Status) {
switch (args) {
case 'connected':
// TODO: should we clear away any old widgets before restoring?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// TODO: should we clear away any old widgets before restoring?

YES

_handleKernelStatusChange(args: Kernel.Status) {
switch (args) {
case 'connected':
// TODO: should we clear away any old widgets before restoring?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// TODO: should we clear away any old widgets before restoring?

YES

😏 we were discussing this in person with @jasongrout.

async clear_state(): Promise<void> {
await super.clear_state();
this._modelsSync = new Map();
this.setDirty();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • note: clear_state and register_model seem to be overriden just for the _modelsSync and setDirty() settings.

if (modelPromise === undefined) {
throw new Error('widget model not found');
}
return modelPromise;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for self: seem to be overriden to make the function trully async and not be able to return undefined.

*/
async restoreWidgets(notebook: INotebookModel): Promise<void> {
await this._loadFromKernel();
await this._loadFromNotebook(notebook);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • note for self: restoreWidgets should be overridden in voila.

await this._loadFromKernel();
await this._loadFromNotebook(notebook);
this._restoredStatus = true;
this._restored.emit();
Copy link
Member

@SylvainCorlay SylvainCorlay Apr 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be two signals for _restoredFromKernel and _restoredFromNotebookStatus. The former would be set to false in disconnect?

@SylvainCorlay SylvainCorlay merged commit 1df5b73 into jupyter-widgets:master Apr 12, 2019
@vidartf vidartf deleted the lab-state branch April 13, 2019 00:39
jasongrout added a commit to jasongrout/ipywidgets that referenced this pull request Apr 13, 2019
This didn’t get pushed to the PR to be included.
@jasongrout
Copy link
Member

To answer the conversation above: should we clear away old widgets before restoring? Actually, since we're only storing live widget models right, I think it doesn't hurt to leave old widget models in - we won't get file bloating from disconnected widgets.

@lock lock bot added the resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion. label May 21, 2020
@lock lock bot locked as resolved and limited conversation to collaborators May 21, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants