Skip to content

Delete the version validation checks. #1219

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 6 commits into from
Mar 22, 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
3 changes: 1 addition & 2 deletions ipywidgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import os

from IPython import get_ipython
from ._version import version_info, __version__, __frontend_version__
from ._version import version_info, __version__, __protocol_version__, __jupyter_widget_version__
from .widgets import *


Expand All @@ -37,7 +37,6 @@ def register_comm_target(kernel=None):
if kernel is None:
kernel = get_ipython().kernel
kernel.comm_manager.register_target('jupyter.widget', Widget.handle_comm_opened)
kernel.comm_manager.register_target('jupyter.widget.version', handle_version_comm_opened)

# deprecated alias
handle_kernel = register_comm_target
Expand Down
5 changes: 3 additions & 2 deletions ipywidgets/_version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

version_info = (6, 0, 0)
version_info = (7, 0, 0, 'dev0')
__version__ = '.'.join(map(str, version_info))
__frontend_version__ = '~2.2.0'
__protocol_version__ = '2.0'
__jupyter_widget_version__ = '3.0.0'
2 changes: 1 addition & 1 deletion ipywidgets/widgets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

from .widget import Widget, CallbackDispatcher, register, widget_serialization, handle_version_comm_opened
from .widget import Widget, CallbackDispatcher, register, widget_serialization
from .domwidget import DOMWidget
from .valuewidget import ValueWidget

Expand Down
32 changes: 1 addition & 31 deletions ipywidgets/widgets/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from ipython_genutils.py3compat import string_types, PY3
from IPython.display import display

from .._version import __frontend_version__
from .._version import __protocol_version__


def _widget_to_json(x, obj):
Expand Down Expand Up @@ -550,25 +550,7 @@ def _trait_from_json(x, self):

def _ipython_display_(self, **kwargs):
"""Called when `IPython.display.display` is called on the widget."""
def loud_error(message):
self.log.warn(message)
sys.stderr.write('%s\n' % message)

# Show view.
if self._view_name is not None:
validated = Widget._version_validated

# Before the user tries to display a widget, validate that the
# widget front-end is what is expected.
if validated is None:
loud_error('Widget Javascript not detected. It may not be '
'installed or enabled properly.')
elif not validated:
msg = ('The installed widget Javascript is the wrong version.'
' It must satisfy the semver range %s.'%__frontend_version__)
if (Widget._version_frontend):
msg += ' The widget Javascript is version %s.'%Widget._version_frontend
loud_error(msg)

# TODO: delete this sending of a comm message when the display statement
# below works. Then add a 'text/plain' mimetype to the dictionary below.
Expand All @@ -594,15 +576,3 @@ def _send(self, msg, buffers=None):
"""Sends a message to the model in the front-end."""
if self.comm is not None and self.comm.kernel is not None:
self.comm.send(data=msg, buffers=buffers)


Widget._version_validated = None
Widget._version_frontend = None
def handle_version_comm_opened(comm, msg):
"""Called when version comm is opened, because the front-end wants to
validate the version."""
def handle_version_message(msg):
Widget._version_validated = msg['content']['data']['validated']
Widget._version_frontend = msg['content']['data'].get('frontend_version', '')
comm.on_msg(handle_version_message)
comm.send({'version': __frontend_version__})
6 changes: 3 additions & 3 deletions ipywidgets/widgets/widget_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"""Base widget class for widgets provided in Core"""

from .widget import Widget
from .._version import __frontend_version__
from .._version import __jupyter_widget_version__

from traitlets import Unicode

class CoreWidget(Widget):

_model_module_version = Unicode(__frontend_version__).tag(sync=True)
_view_module_version = Unicode(__frontend_version__).tag(sync=True)
_model_module_version = Unicode(__jupyter_widget_version__).tag(sync=True)
_view_module_version = Unicode(__jupyter_widget_version__).tag(sync=True)

2 changes: 1 addition & 1 deletion jupyter-js-widgets/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jupyter-js-widgets",
"version": "2.2.0",
"version": "3.0.0",
"description": "Jupyter interactive widgets",
"author": "Project Jupyter",
"license": "BSD-3-Clause",
Expand Down
6 changes: 5 additions & 1 deletion jupyter-js-widgets/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ export * from "./widget_selection";
export * from "./widget_selectioncontainer";
export * from "./widget_string";

export var version = (require('../package.json') as any).version;
export
const version = (require('../package.json') as any).version;

export
const PROTOCOL_VERSION = '2.0';
34 changes: 0 additions & 34 deletions jupyter-js-widgets/src/manager-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,6 @@ abstract class ManagerBase<T> {
return 'jupyter.widget';
}

/**
* The version comm target name to register.
*/
get version_comm_target_name(): string {
return 'jupyter.widget.version';
}

/**
* Display a view for a particular model.
*/
Expand Down Expand Up @@ -238,33 +231,6 @@ abstract class ManagerBase<T> {
});
};

/**
* Validate the version of the Javascript against the version requested by
* the backend.
* @return Whether or not the versions are okay
*/
validateVersion(): Promise<boolean> {
return this._create_comm(this.version_comm_target_name, undefined, {}).then((function(comm) {
return new Promise((function(resolve, reject) {
comm.on_msg((function(msg) {
var version = (require('../package.json') as any).version;
var requirement = msg.content.data.version;
var validated = semver.satisfies(version, requirement);
comm.send({'validated': validated, 'frontend_version': version});
if (validated) {
console.info('Widget backend and frontend versions are compatible');
} else {
console.warn(`Widget backend and frontend versions are not compatible. The backend wants a version in the range ${requirement}, but the frontend is version ${version}.`)
}
resolve(validated);
}).bind(this));
setTimeout(function() {
reject(new Error('Timeout while trying to cross validate the widget frontend and backend versions.'));
}, 3000);
}).bind(this));
}).bind(this));
};

/**
* Create and return a promise for a new widget model
*
Expand Down
4 changes: 0 additions & 4 deletions jupyter-js-widgets/test/src/manager_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ describe("ManagerBase", function() {
expect(this.managerBase.comm_target_name).to.equal('jupyter.widget');
});

it('version_comm_target_name', function() {
expect(this.managerBase.version_comm_target_name).to.equal('jupyter.widget.version');
});

it('get_state', function() {
expect(this.managerBase.get_state).to.not.be(void 0);
});
Expand Down
182 changes: 182 additions & 0 deletions jupyter-widgets-schema/messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,185 @@ To display a widget in JupyterLab, the kernel sends a Jupyter [iopub `display_da
```

In order to display widgets in both the classic notebook and JupyterLab, ipywidgets sends both the `display` comm message and the iopub `display_data` message, and omits the `text/plain` mimetype from the `display_data` message (so the classic notebook will not show any output from the iopub message).
















# Widget messaging protocol, version 2

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:

- creating a companion Jupyter widget object through opening a `comm`
- synchronizing widget state between the frontend and the kernel companion objects
- sending custom messages between these objects
- displaying a widget

For more details on the `comm` infrastructure, see the [Custom messages section](http://jupyter-client.readthedocs.io/en/latest/messaging.html#custom-messages) of the Jupyter kernel message specification.

Throughout this document, relevant parts of messages to the discussion are quoted, and fields irrelevant to the discussion are not displayed.

## Highlights of changes from version 1

The `jupyter.widget.version` comm target and associated version validation messages are gone. Instead, it is up to the package maintainers to ensure that the versions of the packages speak the same widget message protocol. We encourage kernel and frontend package developers to clearly indicate which protocol version the package supports.

## Implementating the Jupyter widgets protocol in the kernel

In this section, we concentrate on implementing the Jupyter widget messaging protocol in the kernel.

### The `jupyter.widget` comm target

A kernel-side Jupyter widgets library also registers a `jupyter.widget` comm target for created creating widget comm channels (one per widget instance). State synchronization and custom messages for a particular widget instance are then sent over the created widget comm channel.

### Instantiating a widget object

When a widget is instantiated in either the kernel or the frontend, it creates a companion object on the other side by sending a `comm_open` message to the `jupyter.widget` comm target.

#### Reception of a `comm_open` message from the frontend

When a frontend creates a Jupyter widget, it sends a `comm_open` message to the kernel:

```
{
'comm_id' : 'u-u-i-d',
'target_name' : 'jupyter.widget',
'data' : {
'widget_class': 'some.string'
}
}
```

The type of widget to be instantiated is given in the `widget_class` string.

In the ipywidgets implementation, this string is actually the key in a registry of widget types. In the ipywidgets implementation, widget types are registered in the dictionary with the `register` decorator. For example the integral progress bar class is registered with `@register('Jupyter.IntProgress')`. When the `widget_class` is not in the registry, it is parsed as a `module` `+` `class` string.

#### Sending a `comm_open` message upon instantiation of a widget

Symmetrically, when instantiating a widget in the kernel, the kernel widgets library sends a `comm_open` message to the frontend:

```
{
'comm_id' : 'u-u-i-d',
'target_name' : 'jupyter.widget',
'data' : {
<dictionary of widget state>
}
}
```

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.

### State synchronization

#### Synchronizing from kernel to frontend: `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:

```
{
'comm_id' : 'u-u-i-d',
'data' : {
'method': 'update',
'state': { <dictionary of widget state> },
'buffers': [ <optional list of state keys corresponding to binary buffers in the message> ]
}
}
```

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

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 optional `data.buffers` value contains a list of keys corresponding to the binary buffers. For example, if `data.buffers` is `['x', 'y']`, then the first binary buffer is the value of the `'x'` state attribute and the second binary buffer is the value of the `'y'` state attribute.

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_keys': [ <optional list of state keys corresponding to binary buffers in the message> ]
}
}
```

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_keys` optional value contains a list of keys corresponding to the binary buffers. For example, if `data.buffer_keys` is `['x', 'y']`, then the first binary buffer is the value of the `'x'` state attribute and the second binary buffer is the value of the `'y'` state attribute.

#### State requests: `request_state`

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

```
{
'comm_id' : 'u-u-i-d',
'data' : {
'method': 'request_state'
}
}
```

The kernel side of the widget should immediately send an `update` message with the entire widget state.

### Custom messages: `custom`

Widgets may also send custom comm messages to their counterpart.

```
{
'comm_id': 'u-u-i-d',
'data': {
'method': 'custom',
'content': <the specified content>,
}
}
```

In the ipywidgets implementation, the `Widget.send(content, buffers=None)` method will produce these messages.

### Displaying widgets

To display a widget in the classic Jupyter notebook, the kernel sends a `display` comm message to the frontend on the widget's comm channel:

```
{
'comm_id': 'u-u-i-d',
'data': {
'method': 'display'
}
}
```

To display a widget in JupyterLab, the kernel sends a Jupyter [iopub `display_data` message](http://jupyter-client.readthedocs.io/en/latest/messaging.html#display-data) with a special mimetype (where the `model_id` is the widget's comm channel id):

```
{
'data': {
'application/vnd.jupyter.widget-view+json': {
'model_id': 'u-u-i-d'
}
}
}
```

In order to display widgets in both the classic notebook and JupyterLab, ipywidgets sends both the `display` comm message and the iopub `display_data` message, and omits the `text/plain` mimetype from the `display_data` message (so the classic notebook will not show any output from the iopub message).
2 changes: 1 addition & 1 deletion jupyterlab_widgets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@phosphor/coreutils": "^0.1.0",
"@phosphor/disposable": "^0.1.0",
"@phosphor/widgets": "^0.1.0",
"jupyter-js-widgets": "~2.2.0",
"jupyter-js-widgets": "~3.0.0",
"jupyterlab": "^0.17.2"
},
"devDependencies": {
Expand Down
4 changes: 0 additions & 4 deletions jupyterlab_widgets/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,10 @@ class WidgetManager extends ManagerBase<Widget> implements IDisposable {
this._rendermime = rendermime;

context.kernelChanged.connect((sender, kernel) => {
if (context.kernel) {
this.validateVersion();
}
this.newKernel(kernel);
});

if (context.kernel) {
this.validateVersion();
this.newKernel(context.kernel);
}
}
Expand Down
2 changes: 1 addition & 1 deletion widgetsnbextension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@phosphor/messaging": "^0.1.0",
"@phosphor/widgets": "^0.3.0",
"backbone": "^1.2.3",
"jupyter-js-widgets": "~2.1.4",
"jupyter-js-widgets": "~3.0.0",
"underscore": "^1.8.3"
},
"devDependencies": {
Expand Down
Loading