From 6d152e27f02de96f8a97f99e584e6846380352fc 2014-07-13 04:32:34 From: Jason Grout Date: 2014-07-13 04:32:34 Subject: [PATCH] Update widget serializer/unserializer to allow custom serialization on a trait-by-trait basis. Serializers/unserializers are given as the trait metadata keys `to_json` and `from_json`. --- diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py index 4131826..72c9c23 100644 --- a/IPython/html/widgets/widget.py +++ b/IPython/html/widgets/widget.py @@ -195,8 +195,16 @@ class Widget(LoggingConfigurable): A single property's name to get. """ keys = self.keys if key is None else [key] - return {k: self._pack_widgets(getattr(self, k)) for k in keys} - + state = {} + for k in keys: + f = self.trait_metadata(k, 'to_json') + value = getattr(self, k) + if f is not None: + state[k] = f(value) + else: + state[k] = self._serialize_trait(value) + return state + def send(self, content): """Sends a custom msg to the widget model in the front-end. @@ -280,7 +288,11 @@ class Widget(LoggingConfigurable): """Called when a state is received from the front-end.""" for name in self.keys: if name in sync_data: - value = self._unpack_widgets(sync_data[name]) + f = self.trait_metadata(name, 'from_json') + if f is not None: + value = f(sync_data[name]) + else: + value = self._unserialize_trait(sync_data[name]) with self._lock_property(name, value): setattr(self, name, value) @@ -299,31 +311,34 @@ class Widget(LoggingConfigurable): """Called when a view has been displayed for this widget instance""" self._display_callbacks(self, **kwargs) - def _pack_widgets(self, x): - """Recursively converts all widget instances to model id strings. + def _serialize_trait(self, x): + """Serialize a trait value to json - Children widgets will be stored and transmitted to the front-end by - their model ids. Return value must be JSON-able.""" + Traverse lists/tuples and dicts and serialize their values as well. + Replace any widgets with their model_id + """ if isinstance(x, dict): - return {k: self._pack_widgets(v) for k, v in x.items()} + return {k: self._serialize_trait(v) for k, v in x.items()} elif isinstance(x, (list, tuple)): - return [self._pack_widgets(v) for v in x] + return [self._serialize_trait(v) for v in x] elif isinstance(x, Widget): return x.model_id else: return x # Value must be JSON-able - def _unpack_widgets(self, x): - """Recursively converts all model id strings to widget instances. + def _unserialize_trait(self, x): + """Convert json values to objects - Children widgets will be stored and transmitted to the front-end by - their model ids.""" + We explicitly support converting valid string widget UUIDs to Widget references. + """ if isinstance(x, dict): - return {k: self._unpack_widgets(v) for k, v in x.items()} + return {k: self._unserialize_trait(v) for k, v in x.items()} elif isinstance(x, (list, tuple)): - return [self._unpack_widgets(v) for v in x] - elif isinstance(x, string_types): - return x if x not in Widget.widgets else Widget.widgets[x] + return [self._unserialize_trait(v) for v in x] + elif isinstance(x, string_types) and x in Widget.widgets: + # we want to support having child widgets at any level in a hierarchy + # trusting that a widget UUID will not appear out in the wild + return Widget.widgets[x] else: return x