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