Trouble creating a widget in kernel from frontend

I’m attempting to send a widget created in Typescript back to Python, but I’m running into issues that I thought I had solved in the past and now seem to have un-solved. In Python, I have a class that subclasses ipywidgets.Widget, implements model_name, model_module, model_module_version, view_name, view_module, view_module_version, and a handful of other attributes. On the Typescript side, both the model_name and view_name classes are implemented and exported.

I’ve created a new model with const manager: ManagerBase<any> = this.model.widget_manager (the template argument doesn’t seem to matter for these purposes) and then called await manager.new_widget, supplying the config with the appropriate model_name etc and the full state of the model in the second argument.

The trouble seems to be when I attempt to send it back to Python. I’ve got a container class, also registered as @ipywidgets.register and subclassing ipywidgets.DOMWidget. Two of the attributes are

​nodes = traitlets.List(
		traitlets.Instance(NodeInstanceModel),
        default_value=[],
    ).tag(sync=True, **ipywidgets.widget_serialization)
selected_node = traitlets.Instance(NodeInstanceMode).tag(sync=True, **ipywidgets.widget_serialization)

In typescript, after I create the new widget with await manager.new_widget(), I set the nodes attribute to be a typescript array of the appropriate NodeInstanceModel counterparts in typescript (in this case, ReteNodeModel) and I save it.

What happens on the Python side is that an error is thrown, saying that the nodes trait expects a List of NodeInstanceModel, but it gets the string IPY_MODEL_u_u_i_d (where u_u_i_d really is a UUID.) I’ve checked and it is indeed the case that the newly created model always matches the UUID – so it seems the widget isn’t getting unpacked properly. To be sure, I’ve also checked and the selected_node shows the same issue.

(Here’s where I note that I have run into this before, and as near as I can tell, I’m not doing anything different this time!)

One strange thing that I did notice is that in the Python error output, the very first error, before the trait validation throws an error about the string instead of an instance, is that a comm does not exist for the same UUID that is in the IPY_MODEL name. I’m not entirely sure how to interpret that, since I thought that the new_widget call was supposed to create a new comm as well, so perhaps it’s a red herring?

Does anyone have any insight? It feels like I’ve overlooked something trivial, but I cannot seem to figure out what it is. I’ve tried to keep the code short, but I’m happy to provide more here or point to the original repo.

a container class

Do you have serialize on the typescript side for Container.nodes?

typescript array of the appropriate

Are they already-created, or are they new widgets?

Here’s the only one I recall doing, where I cheat and use Box.children to create a file.

I cribbed the pattern from bqplot, whereas only the controller uses it in core.

provide more here or point to the original repo.

A link to the repo (and/or a PR of it not working in binder, with jupyterlab-kernelspy installed) would be good, if only for posterity… provided it gets fixed, and doesn’t just remain mysterious.

Do you have serialize on the typescript side for Container.nodes ?

Ah! Maybe this is the root of the problem. I’ve set up the serializers:

  static serializers: ISerializers = {
    ...DOMWidgetModel.serializers,
    _components: { deserialize: unpack_models},
    nodes: { deserialize: unpack_models },
    selected_node: {deserialize: unpack_models}
  };

(_components is unrelated, and not expected to be created frontend-side.) Perhaps this is the root of the problem – I haven’t provided any information on how to serialize, as I expected that it would be able to recursively apply serialization of the objects, as they’re all just traitlets-ized objects. I think I was under the assumption that anything that resolved to either traits/types that could be round-tripped with standard JSON-serialization wouldn’t need serializer to be specified.

I’ll try with jupyterlab-kernelspy to see if I can get more information, and at least try to deduce where the issue is showing up, if it’s in serializing the nodes or what. Thank you for the suggestions. The repo is GitHub - cropsinsilico/jupyterlab_nodeeditor , but I’ve not go a binder link yet and will set that up.

Thank you for suggesting that I check out Kernelspy! I was able to get this out of it:

The 'comm' trait of a NodeInstanceModel instance expected a Comm or None, not the BaseComm at '0x7fbe7c8b9f90'

This looks like it prevents the new comm from being opened and the object not being sent. I’m going to keep digging into this, and make sure all my versions are correctly in sync.

Uggh, the transition to comm has been… not so fun.

Depending on what you dig up, this may be another bug. But yeah, kernelspy has some pointy bits, but usually a lot better than wading through the browser console… though they have gotten better of late.

I’m not prepared yet to say it is a bug, because I don’t yet fully understand the relationship between ipykernel.comm and comm.base_comm but by removing the validation of comm = Instance('ipykernel.comm.Comm', allow_none=True) in the base ipywidgets.Widget class, I was able to restore the behavior.

So I suspect it might be a bug, but I’m not really ready to point fingers. :smile: And I certainly don’t have a proposed solution. I’ll keep looking at how the various comm modules and the results of create_comm in comm.base_comm are related and possibly file an issue. As it stands, it looks like:

comm.base_comm.CommManager.comm_open imports create_comm from comm, which in my running kernel has been overridden to be ipykernel.ipkernel.create_comm which explicitly returns an instance of ipykernel.comm.comm.BaseComm.