##// END OF EJS Templates
Change custom serialization to use custom models, rather than transmitting the serializer name across the wire...
Jason Grout -
Show More
@@ -14,18 +14,33 b' define(['
14 14 "widgets/js/widget_selection",
15 15 "widgets/js/widget_selectioncontainer",
16 16 "widgets/js/widget_string",
17 ], function(widgetmanager, linkModels) {
18 for (var target_name in linkModels) {
19 if (linkModels.hasOwnProperty(target_name)) {
20 widgetmanager.WidgetManager.register_widget_model(target_name, linkModels[target_name]);
21 }
17 ], function(widgetmanager) {
18
19
20 /**
21 * From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
22 * Can be removed with the string endsWith function is implemented in major browsers
23 */
24 var endsWith = function(target, searchString, position) {
25 var subjectString = target.toString();
26 if (position === undefined || position > subjectString.length) {
27 position = subjectString.length;
22 28 }
29 position -= searchString.length;
30 var lastIndex = subjectString.indexOf(searchString, position);
31 return lastIndex !== -1 && lastIndex === position;
32 };
23 33
24 // Register all of the loaded views with the widget manager.
25 for (var i = 2; i < arguments.length; i++) {
26 for (var target_name in arguments[i]) {
27 if (arguments[i].hasOwnProperty(target_name)) {
28 widgetmanager.WidgetManager.register_widget_view(target_name, arguments[i][target_name]);
34 // Register all of the loaded models and views with the widget manager.
35 for (var i = 1; i < arguments.length; i++) {
36 var module = arguments[i];
37 for (var target_name in module) {
38 if (module.hasOwnProperty(target_name)) {
39 if (endsWith(target_name, "View")) {
40 widgetmanager.WidgetManager.register_widget_view(target_name, module[target_name]);
41 } else if (endsWith(target_name, "Model")) {
42 widgetmanager.WidgetManager.register_widget_model(target_name, module[target_name]);
43 }
29 44 }
30 45 }
31 46 }
@@ -32,7 +32,6 b' define(["widgets/js/manager",'
32 32 this.state_lock = null;
33 33 this.id = model_id;
34 34 this.views = {};
35 this.serializers = {};
36 35 this._resolve_received_state = {};
37 36
38 37 if (comm !== undefined) {
@@ -146,23 +145,17 b' define(["widgets/js/manager",'
146 145 var state = msg.content.data.state || {};
147 146 var buffer_keys = msg.content.data.buffers || [];
148 147 var buffers = msg.buffers || [];
149 var metadata = msg.content.data.metadata || {};
150 var i,k;
151 148 for (var i=0; i<buffer_keys.length; i++) {
152 k = buffer_keys[i];
153 state[k] = buffers[i];
149 state[buffer_keys[i]] = buffers[i];
154 150 }
155 151
156 // for any metadata specifying a deserializer, set the
157 // state to a promise that resolves to the deserialized version
158 // also, store the serialization function for the attribute
159 var keys = Object.keys(metadata);
160 for (var i=0; i<keys.length; i++) {
161 k = keys[i];
162 if (metadata[k] && metadata[k].serialization) {
163 that.serializers[k] = utils.load_class.apply(that,
164 metadata[k].serialization);
165 state[k] = that.deserialize(that.serializers[k], state[k]);
152 // deserialize fields that have custom deserializers
153 var serializers = that.constructor.serializers;
154 if (serializers) {
155 for (var k in state) {
156 if (serializers[k] && serializers[k].deserialize) {
157 state[k] = (serializers[k].deserialize)(state[k], that);
158 }
166 159 }
167 160 }
168 161 return utils.resolve_promises_dict(state);
@@ -188,19 +181,6 b' define(["widgets/js/manager",'
188 181 }
189 182 },
190 183
191 deserialize: function(serializer, value) {
192 // given a serializer dict and a value,
193 // return a promise for the deserialized value
194 var that = this;
195 return serializer.then(function(s) {
196 if (s.deserialize) {
197 return s.deserialize(value, that);
198 } else {
199 return value;
200 }
201 });
202 },
203
204 184 set_state: function (state) {
205 185 var that = this;
206 186 // Handle when a widget is updated via the python side.
@@ -362,25 +342,15 b' define(["widgets/js/manager",'
362 342 var that = this;
363 343 // first, build a state dictionary with key=the attribute and the value
364 344 // being the value or the promise of the serialized value
365 var state_promise_dict = {};
366 var keys = Object.keys(attrs);
367 for (var i=0; i<keys.length; i++) {
368 // bind k and v locally; needed since we have an inner async function using v
369 (function(k,v) {
370 if (that.serializers[k]) {
371 state_promise_dict[k] = that.serializers[k].then(function(f) {
372 if (f.serialize) {
373 return f.serialize(v, that);
374 } else {
375 return v;
345 var serializers = this.constructor.serializers;
346 if (serializers) {
347 for (k in attrs) {
348 if (serializers[k] && serializers[k].serialize) {
349 attrs[k] = (serializers[k].serialize)(attrs[k], this);
376 350 }
377 })
378 } else {
379 state_promise_dict[k] = v;
380 351 }
381 })(keys[i], attrs[keys[i]])
382 352 }
383 utils.resolve_promises_dict(state_promise_dict).then(function(state) {
353 utils.resolve_promises_dict(attrs).then(function(state) {
384 354 // get binary values, then send
385 355 var keys = Object.keys(state);
386 356 var buffers = [];
@@ -402,20 +372,6 b' define(["widgets/js/manager",'
402 372 });
403 373 },
404 374
405 serialize: function(model, attrs) {
406 // Serialize the attributes into a sync message
407 var keys = Object.keys(attrs);
408 var key, value;
409 var buffers, metadata, buffer_keys, serialize;
410 for (var i=0; i<keys.length; i++) {
411 key = keys[i];
412 serialize = model.serializers[key];
413 if (serialize && serialize.serialize) {
414 attrs[key] = serialize.serialize(attrs[key]);
415 }
416 }
417 },
418
419 375 save_changes: function(callbacks) {
420 376 /**
421 377 * Push this model's state to the back-end
@@ -4,10 +4,41 b''
4 4 define([
5 5 "widgets/js/widget",
6 6 "jqueryui",
7 "underscore",
7 8 "base/js/utils",
8 9 "bootstrap",
9 ], function(widget, $, utils){
10 ], function(widget, $, _, utils){
10 11 "use strict";
12 var unpack_models = function unpack_models(value, model) {
13 /**
14 * Replace model ids with models recursively.
15 */
16 var unpacked;
17 if ($.isArray(value)) {
18 unpacked = [];
19 _.each(value, function(sub_value, key) {
20 unpacked.push(unpack_models(sub_value, model));
21 });
22 return Promise.all(unpacked);
23 } else if (value instanceof Object) {
24 unpacked = {};
25 _.each(value, function(sub_value, key) {
26 unpacked[key] = unpack_models(sub_value, model);
27 });
28 return utils.resolve_promises_dict(unpacked);
29 } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") {
30 // get_model returns a promise already
31 return model.widget_manager.get_model(value.slice(10, value.length));
32 } else {
33 return Promise.resolve(value);
34 }
35 };
36
37 var BoxModel = widget.WidgetModel.extend({}, {
38 serializers: _.extend({
39 children: {deserialize: unpack_models}
40 }, widget.WidgetModel.serializers)
41 });
11 42
12 43 var BoxView = widget.DOMWidgetView.extend({
13 44 initialize: function(){
@@ -148,6 +179,7 b' define(['
148 179 });
149 180
150 181 return {
182 'BoxModel': BoxModel,
151 183 'BoxView': BoxView,
152 184 'FlexBoxView': FlexBoxView,
153 185 };
@@ -89,31 +89,6 b' def register(key=None):'
89 89 return wrap
90 90
91 91
92 def _widget_to_json(x):
93 if isinstance(x, dict):
94 return {k: _widget_to_json(v) for k, v in x.items()}
95 elif isinstance(x, (list, tuple)):
96 return [_widget_to_json(v) for v in x]
97 elif isinstance(x, Widget):
98 return "IPY_MODEL_" + x.model_id
99 else:
100 return x
101
102 def _json_to_widget(x):
103 if isinstance(x, dict):
104 return {k: _json_to_widget(v) for k, v in x.items()}
105 elif isinstance(x, (list, tuple)):
106 return [_json_to_widget(v) for v in x]
107 elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets:
108 return Widget.widgets[x[10:]]
109 else:
110 return x
111
112 widget_serialization = {
113 'from_json': _json_to_widget,
114 'to_json': lambda x: (_widget_to_json(x), {'serialization': ('models', 'widgets/js/types')})
115 }
116
117 92 class Widget(LoggingConfigurable):
118 93 #-------------------------------------------------------------------------
119 94 # Class attributes
@@ -241,12 +216,10 b' class Widget(LoggingConfigurable):'
241 216 key : unicode, or iterable (optional)
242 217 A single property's name or iterable of property names to sync with the front-end.
243 218 """
244 state, buffer_keys, buffers, metadata = self.get_state(key=key)
219 state, buffer_keys, buffers = self.get_state(key=key)
245 220 msg = {"method": "update", "state": state}
246 221 if buffer_keys:
247 222 msg['buffers'] = buffer_keys
248 if metadata:
249 msg['metadata'] = metadata
250 223 self._send(msg, buffers=buffers)
251 224
252 225 def get_state(self, key=None):
@@ -278,19 +251,16 b' class Widget(LoggingConfigurable):'
278 251 state = {}
279 252 buffers = []
280 253 buffer_keys = []
281 metadata = {}
282 254 for k in keys:
283 255 f = self.trait_metadata(k, 'to_json', self._trait_to_json)
284 256 value = getattr(self, k)
285 serialized, md = f(value)
257 serialized = f(value)
286 258 if isinstance(serialized, memoryview):
287 259 buffers.append(serialized)
288 260 buffer_keys.append(k)
289 261 else:
290 262 state[k] = serialized
291 if md is not None:
292 metadata[k] = md
293 return state, buffer_keys, buffers, metadata
263 return state, buffer_keys, buffers
294 264
295 265 def set_state(self, sync_data):
296 266 """Called when a state is received from the front-end."""
@@ -439,11 +409,8 b' class Widget(LoggingConfigurable):'
439 409 self._display_callbacks(self, **kwargs)
440 410
441 411 def _trait_to_json(self, x):
442 """Convert a trait value to json.
443
444 Metadata (the second return value) is not sent
445 """
446 return x, None
412 """Convert a trait value to json."""
413 return x
447 414
448 415 def _trait_from_json(self, x):
449 416 """Convert json values to objects."""
@@ -6,13 +6,40 b' Represents a container that can be used to group other widgets.'
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 from .widget import DOMWidget, register, widget_serialization
9 from .widget import DOMWidget, Widget, register
10 10 from IPython.utils.traitlets import Unicode, Tuple, TraitError, Int, CaselessStrEnum
11 11 from IPython.utils.warn import DeprecatedClass
12 12
13 def _widget_to_json(x):
14 if isinstance(x, dict):
15 return {k: _widget_to_json(v) for k, v in x.items()}
16 elif isinstance(x, (list, tuple)):
17 return [_widget_to_json(v) for v in x]
18 elif isinstance(x, Widget):
19 return "IPY_MODEL_" + x.model_id
20 else:
21 return x
22
23 def _json_to_widget(x):
24 if isinstance(x, dict):
25 return {k: _json_to_widget(v) for k, v in x.items()}
26 elif isinstance(x, (list, tuple)):
27 return [_json_to_widget(v) for v in x]
28 elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets:
29 return Widget.widgets[x[10:]]
30 else:
31 return x
32
33 widget_serialization = {
34 'from_json': _json_to_widget,
35 'to_json': _widget_to_json
36 }
37
38
13 39 @register('IPython.Box')
14 40 class Box(DOMWidget):
15 41 """Displays multiple widgets in a group."""
42 _model_name = Unicode('BoxModel', sync=True)
16 43 _view_name = Unicode('BoxView', sync=True)
17 44
18 45 # Child widgets in the container.
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now