Open lbustelo opened 8 years ago
I'm leaning towards Option 2 because that would put serialization concerns closer to where the data is sent through the wire.
Also, I'm sure we don't want other, future libraries that build atop ipywidgets to have to fix the same problem and duplicate everything done in declarative widgets. This becomes more of a concern now that ipywidgets is becoming less strictly coupled with the notebook and can be used in other contexts.
I think the problem here is the API. As a Python user, the Python json.dumps API is what I would want. The function will serialize nested objects just fine, but when it encounters an object it can't handle, it raises. In such cases, the user can provide either a default function or a class to handle any object that dumps
doesn't know how to handle. Gimme that.
Unfortunately, the serialization of objects going over the comm channel is triggered deep in the bowels of Jupyter. Not in ipywidgets, not even in ipykernel, but in jupyter_client/session.py. This is far, far away from the widgets user, who ultimately has to provide the alternate serialization mechanism, because it's not feasible for the Jupyter internals to handle every case without throwing up its hands and invoking repr
on the object.
I wonder if the fix for this would be to create a custom traitlet that is used to hold a value and the traitlet implements the repr
and delegated down to contained object.
For example in kernel-python/urth/widgets/widget_function.py
we would have the return value of the function be held as a traitlet instance like
result = ResultTraitlet(None, sync=True)
When the function is invoke, we just update the result
traitlet and that will trigger the update
message back to the client. When the ResultTraitlet
reaches the point of serialization, it will have repr
and would delegate to underlying object. At which point, we are returning json string so we can call things like Pandas.to_json
and return directly. ResultTraitlet
will then incorporate the serialization support.
The same traitlet should be reusable for channel().set()
api.
@jtyberg Said in #138
I can see 2 options
Option 1
Stay the course and have serialization helpers in our widgets and fix the issue of serializing nested structures. I think that there has to be a Python package that would help with this similar to how in Scala we are relying on Play.Json to do the work. This option would also benefit from an enhancement to ipywidgets to accept pre-serialized data, because ultimately certain cases would return a stringyfied JSON blob and there is no way to send that to ipywidgets without turning it back into a Object.
Right now we are ultimately relying on ipywidgets to turn our message into a string.
The
Widget._send()
method will try to serialize themsg
object. If it encounters anything that it cannot serialize, it fails. In declarativewidgets, we try to turn objects into Serializable ones, but ultimately the serialization is done by ipywidgets.It we do the Pandas.DataFrame.to_json(), which gives us a string, there is not way to tell
_send()
to unwrap that string and place it in the message. We are force to load that json string back into a Dict to give back to_send()
.Option 2
Remove serialization concerns from our widgets and rely on comprehensive/extensible support for serialization to be provided by ipywidgets. ipywidgets ultimately takes the message and sends it through the wire. This would mean that widgets like
kernel-python/urth/widgets/widget_function.py
would not call any serialization helper, but rather send the returned object from the function straight into the messages to git toWidget._send()
.At the end, the key behavior to retain is that we do not want the client side to have to differentiate between an actual
string
value and one that is really anObject
but is stringyfied.I'm leaning towards Option 2 because that would put serialization concerns closer to where the data is sent through the wire.
/cc @jdfreder @SylvainCorlay