Skip to content

Make the widget update message the same no matter the direction of syncing #1221

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 2 commits into from
Mar 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions ipywidgets/widgets/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,14 +508,12 @@ def _handle_msg(self, msg):
data = msg['content']['data']
method = data['method']

# Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
if method == 'backbone':
if 'sync_data' in data:
# get binary buffers too
sync_data = data['sync_data']
if method == 'update':
if 'state' in data:
state = data['state']
if 'buffer_paths' in data:
_put_buffers(sync_data, data['buffer_paths'], msg['buffers'])
self.set_state(sync_data) # handles all methods
_put_buffers(state, data['buffer_paths'], msg['buffers'])
self.set_state(state)

# Handle a state request.
elif method == 'request_state':
Expand Down
18 changes: 7 additions & 11 deletions jupyter-js-widgets/src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,20 +234,16 @@ class WidgetModel extends Backbone.Model {
*/
_handle_status(msg, callbacks) {
if (this.comm !== undefined) {
if (msg.content.execution_state ==='idle') {
this.pending_msgs--;
if (msg.content.execution_state === 'idle') {
// Send buffer if this message caused another message to be
// throttled.
if (this.msg_buffer !== null &&
(this.get('msg_throttle') || 1) === this.pending_msgs) {
var data = {
method: 'backbone',
sync_method: 'update',
sync_data: this.msg_buffer
};
this.comm.send(data, callbacks);
this.send_sync_message(this.msg_buffer, this.msg_buffer_callbacks);
this.msg_buffer = null;
} else {
--this.pending_msgs;
this.msg_buffer_callbacks = null;
this.pending_msgs++;
}
}
}
Expand Down Expand Up @@ -398,8 +394,8 @@ class WidgetModel extends Backbone.Model {
// on the python side the inverse happens
var split = utils.remove_buffers(state);
this.comm.send({
method: 'backbone',
sync_data: split.state,
method: 'update',
state: split.state,
buffer_paths: split.buffer_paths
}, callbacks, {}, split.buffers);
}).catch((error) => {
Expand Down
34 changes: 7 additions & 27 deletions jupyter-widgets-schema/messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ In order to display widgets in both the classic notebook and JupyterLab, ipywidg



# Widget messaging protocol, version 2
# Widget messaging protocol, version 2 (not finalized)

A Jupyter widget has both a frontend and kernel object communicating with each other using the `comm` messages provided by the Jupyter kernel messaging protocol. The primary communication that happens is synchronizing widget state, represented in the messages by a dictionary of key-value pairs. The Jupyter widget messaging protocol covers `comm` messages for the following actions:

Expand All @@ -229,6 +229,8 @@ The `jupyter.widget.version` comm target and associated version validation messa

While in version 1, binary buffers could only be top level attributes of the `state` object, now any item in the state can be a binary buffer. All binary buffers that are a descendant of the state object (in a nested object or list) will be removed from an object or replaced by null in a list. The 'path' of each binary buffer and its data are sent separately, so the state object can be reconstructed on the other side of the wire. This change was necessary to allow sending the data for a binary array plus its metadata (shape, type, masks) in one attribute.

The sync update event from the frontend to the kernel was restructured to have the same field names as the event from the kernel to the frontend, namely the method field is `'update'` and the state data is in the `state` attribute.

## Implementating the Jupyter widgets protocol in the kernel

In this section, we concentrate on implementing the Jupyter widget messaging protocol in the kernel.
Expand Down Expand Up @@ -276,17 +278,17 @@ Symmetrically, when instantiating a widget in the kernel, the kernel widgets lib

The type of widget to be instantiated in the frontend is determined by the `_model_name`, `_model_module` and `_model_module_version` keys in the state, which respectively stand for the name of the class that must be instantiated in the frontend, the JavaScript module where this class is defined, and a semver range for that module. See the [Model State](modelstate.md) documentation for the serialized state for core Jupyter widgets.

The initial state is split between values that are serializable with JSON (in the `data.state` dictionary), and binary values (represented in `data.buffer_paths`).
The state is split between values that are serializable with JSON (in the `data.state` dictionary), and binary values (represented in `data.buffer_paths`).

The `data.state` value is a dictionary of widget state keys and values that can be serialized to JSON.

Comm messages for state synchronization may contain binary buffers. The `data.buffer_paths` value contains a list of 'paths' in the `data.state` object corresponding to the binary buffers. For example, if `data.buffer_paths` is `[['x'], ['y', 'z', 0]]`, then the first binary buffer is the value of the `data.state['x']` attribute and the second binary buffer is the value of the `data.state['y']['z'][0]` state attribute. A path representing a list value (i.e., last index of the path is an integer) will be `null` in `data.state`, and a path representing a dictionary key (i.e., last index of the path is a string) will not exist in `data.state`.

### State synchronization

#### Synchronizing from kernel to frontend: `update`
#### Synchronizing widget state: `update`

When a widget's state changes in the kernel, the changed state keys and values are sent to the frontend over the widget's comm channel using an `update` message:
When a widget's state changes in either the kernel or the frontend, the changed state keys and values are sent to the other side over the widget's comm channel using an `update` message:

```
{
Expand All @@ -299,32 +301,10 @@ When a widget's state changes in the kernel, the changed state keys and values a
}
```

The `state` and `buffer_paths` are the same as in the `comm_open` case.
The `data.state` and `data.buffer_paths` values are the same as in the `comm_open` case.

See the [Model state](modelstate.md) documentation for the attributes of core Jupyter widgets.

#### Synchronizing from frontend to kernel: `backbone`

When a widget's state changes in the frontend, the changed keys are sent to the kernel over the widget's comm channel using a `backbone` message:

```
{
'comm_id' : 'u-u-i-d',
'data' : {
'method': 'backbone',
'sync_data': { <dictionary of widget state> }
'buffer_paths': [ <list with paths corresponding to the binary buffers, that should be put in the state dict at the kernel> ]
}
}
```

The state update is split between values that are serializable with JSON (in the `data.sync_data` dictionary), and binary values (represented in `data.buffer_keys`).

The `data.sync_data` value is a dictionary of widget state keys and values that can be serialized to JSON.

Comm messages for state synchronization may contain binary buffers. The `data.buffer_paths` value contains a list of 'paths' in the `data.sync_data` object corresponding to the binary buffers. For example, if `data.buffer_paths` is `[['x'], ['y', 'z', 0]]`, then the first binary buffer is the value of the `data.sync_data['x']` attribute and the second binary buffer is the value of the `data.sync_data['y']['z'][0]` state attribute. A path representing a list value (i.e., last index of the path is an integer) will be `null` in `data.sync_data`, and a path representing a dictionary key (i.e., last index of the path is a string) will not exist in `data.sync_data`.


#### State requests: `request_state`

When a frontend wants to request the full state of a widget, the frontend sends a `request_state` message:
Expand Down