diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js
index 944effd..6a0200f 100644
--- a/IPython/html/static/widgets/js/manager.js
+++ b/IPython/html/static/widgets/js/manager.js
@@ -187,12 +187,47 @@ define([
WidgetManager.prototype._handle_comm_open = function (comm, msg) {
// Handle when a comm is opened.
+ return this.create_model({model_name: msg.content.data.target_name, comm: comm});
+ };
+
+ WidgetManager.prototype.create_model = function (model_name, target_name) {
+ // Create and return a new widget model.
+ //
+ // Parameters
+ // ----------
+ // model_name: string
+ // Target name of the widget model to create.
+ // target_name: string
+ // Target name of the widget in the back-end.
+ return this._create_model({model_name: model_name, target_name: target_name});
+ };
+
+ WidgetManager.prototype._create_model = function (options) {
+ // Create and return a new widget model.
+ //
+ // Parameters
+ // ----------
+ // options: dictionary
+ // Dictionary of options with the following contents:
+ // model_name: string
+ // Target name of the widget model to create.
+ // target_name: (optional) string
+ // Target name of the widget in the back-end.
+ // comm: (optional) Comm
+
+ // Create a comm if it wasn't provided.
+ var comm = options.comm;
+ if (!comm) {
+ comm = this.comm_manager.new_comm('ipython.widget', {'target_name': options.target_name});
+ }
+
+ // Create and return a new model that is connected to the comm.
var that = this;
var instantiate_model = function(ModelType) {
var model_id = comm.comm_id;
var widget_model = new ModelType(that, model_id, comm);
- widget_model.on('comm:close', function () {
+ widget_model.on('comm:close', function () {sss
delete that._models[model_id];
});
that._models[model_id] = widget_model;
diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py
index f1571ea..65c2a33 100644
--- a/IPython/html/widgets/widget.py
+++ b/IPython/html/widgets/widget.py
@@ -18,6 +18,7 @@ import collections
from IPython.core.getipython import get_ipython
from IPython.kernel.comm import Comm
from IPython.config import LoggingConfigurable
+from IPython.utils.importstring import import_item
from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, \
CaselessStrEnum, Tuple, CUnicode, Int, Set
from IPython.utils.py3compat import string_types
@@ -95,6 +96,15 @@ class Widget(LoggingConfigurable):
if Widget._widget_construction_callback is not None and callable(Widget._widget_construction_callback):
Widget._widget_construction_callback(widget)
+ @staticmethod
+ def handle_comm_opened(comm, msg):
+ """Static method, called when a widget is constructed."""
+ target_name = msg['content']['data']['target_name']
+ widget_class = import_item(target_name)
+ widget = widget_class(open_comm=False)
+ widget.set_comm(comm)
+
+
#-------------------------------------------------------------------------
# Traits
#-------------------------------------------------------------------------
@@ -125,13 +135,14 @@ class Widget(LoggingConfigurable):
#-------------------------------------------------------------------------
# (Con/de)structor
#-------------------------------------------------------------------------
- def __init__(self, **kwargs):
+ def __init__(self, open_comm=True, **kwargs):
"""Public constructor"""
self._model_id = kwargs.pop('model_id', None)
super(Widget, self).__init__(**kwargs)
Widget._call_widget_constructed(self)
- self.open()
+ if open_comm:
+ self.open()
def __del__(self):
"""Object disposal"""
@@ -149,15 +160,19 @@ class Widget(LoggingConfigurable):
'model_module': self._model_module})
if self._model_id is not None:
args['comm_id'] = self._model_id
- self.comm = Comm(**args)
- self._model_id = self.model_id
+ self.set_comm(Comm(**args))
- self.comm.on_msg(self._handle_msg)
- Widget.widgets[self.model_id] = self
-
# first update
self.send_state()
+ def set_comm(self, comm):
+ """Set's the comm of the widget."""
+ self.comm = comm
+ self._model_id = self.model_id
+
+ self.comm.on_msg(self._handle_msg)
+ Widget.widgets[self.model_id] = self
+
@property
def model_id(self):
"""Gets the model id of this widget.
@@ -330,7 +345,7 @@ class Widget(LoggingConfigurable):
def _handle_custom_msg(self, content):
"""Called when a custom msg is received."""
self._msg_callbacks(self, content)
-
+
def _notify_trait(self, name, old_value, new_value):
"""Called when a property has been changed."""
# Trigger default traitlet callback machinery. This allows any user
@@ -341,10 +356,10 @@ class Widget(LoggingConfigurable):
# Send the state after the user registered callbacks for trait changes
# have all fired (allows for user to validate values).
if self.comm is not None and name in self.keys:
- # Make sure this isn't information that the front-end just sent us.
+ # Make sure this isn't information that the front-end just sent us.
if self._should_send_property(name, new_value):
- # Send new state to front-end
- self.send_state(key=name)
+ # Send new state to front-end
+ self.send_state(key=name)
def _handle_displayed(self, **kwargs):
"""Called when a view has been displayed for this widget instance"""
diff --git a/IPython/kernel/comm/manager.py b/IPython/kernel/comm/manager.py
index fef3699..ca5acee 100644
--- a/IPython/kernel/comm/manager.py
+++ b/IPython/kernel/comm/manager.py
@@ -107,15 +107,21 @@ class CommManager(LoggingConfigurable):
iopub_socket=self.iopub_socket,
primary=False,
)
+ self.register_comm(comm)
if f is None:
self.log.error("No such comm target registered: %s", target_name)
- comm.close()
- return
+ else:
+ try:
+ f(comm, msg)
+ return
+ except Exception:
+ self.log.error("Exception opening comm with target: %s", target_name, exc_info=True)
+
+ # Failure.
try:
- f(comm, msg)
- except Exception:
- self.log.error("Exception opening comm with target: %s", target_name, exc_info=True)
comm.close()
+ except:
+ pass # Eat errors, nomnomnom
def comm_msg(self, stream, ident, msg):
"""Handler for comm_msg messages"""