-
Notifications
You must be signed in to change notification settings - Fork 35
Hybrid Elements Message Spec
As explained earlier, DeclarativeWidgets contains a set of elements that are referred to as Hybrid Elements and are made up of both a Browser and a Kernel component. This document goes into detail about the communication messages between the 2 sides. By adhering to the following spec, Kernel can add support for DeclarativeWidgets.
The main requirement for supporting DeclarativeWidgets is for the Kernel to implement the Comm Protocol. This serves as the backbone for 2-way communication between the Browser and the Kernel.
-
The frontend sends a
comm_openmessage on thejupyter.widgetchannel containing awidget_classfield:{ header: { msg_type: "comm_open", ... }, ... content: { comm_id: "<COMM_ID>", target_name: "ipython.widget", data: { widget_class: "<WIDGET_CLASS_NAME>" } } }
Upon receiving this message, the handler for target_name must create the appropriate instance based on the content of widget_class and provide that instance with access to the comm.
-
The
widget_classin the initialcomm_openwill beurth.widgets.widget_function.Function -
A
sync_datacomm_msgwill be sent from the frontend following thecomm_openwith afunction_namefield whose value is the name of the function to use. Extra options such aslimitare also included in this message:{ header: { msg_type: "comm_msg", ... }, ... content: { data: { method: "backbone", sync_data: { function_name: "<FUNCTION_NAME>", limit: <LIMIT> } }, ... } } -
The backend should then send the function's
signature, a mapping of argument name to properties (e.g. JavaScript type), in the following format:{ header: { msg_type: "comm_msg", ... }, content: { data: { method: "update" state: { argspec: { "<ARG_NAME_1>": { type: "<ARG_1_TYPE>", required: true }, ... "<ARG_NAME_N>": { type: "<ARG_N_TYPE>" } } } } }, ... }
If a type cannot be mapped to a JavaScript type, the language implementation's type should be sent.
The required field is set to true when the argument must be present for function invocation. For instance, the required field is not necessary for a parameter with a default argument.
-
Finally, the backend should send a
statusmessage. See theStatus Messagessection for a full description of the message format.- Send a
status: okmessage if the function name registration succeeded. - Send a
status: errormessage if the function name registration failed, e.g. if no function with the given name has been declared, or if an error occurred while determining thesignature.
- Send a
-
A
customcomm_msgwill be sent from the frontend with anevent: "invoke"field, and anargs: {...}field. Theargsfield contains an object with mappings from argument name to argument value that should be used for the function invocation. The function to be invoked is the function whose name was sent during initialization in thefunction_namefield.{ header: { msg_type: "comm_msg", ... }, ... content: { data: { method: "custom", content: { event: "invoke", args: { "<ARG_NAME_1>": "<ARG_VALUE_1>", ... "<ARG_NAME_N>": "<ARG_VALUE_N>" } } } } }
Note that all argument values are sent as strings. It is up to the language implementation to convert argument values to their proper types if necessary.
-
The function's return value should be sent from the backend as a
customcomm_msgwithmethod: update. The message contains astateobject with aresultfield that holds the function's return value.{ header: { msg_type: "comm_msg", ... }, ... content: { data: { method: "update", state: { "result": <FUNCTION_RETURN_VALUE> } }, ... } }
Note that the language implementation should send a representation of the function return value that can be serialized as JSON.
-
Finally, the backend should send a
statusmessage. See theStatus Messagessection for a full description of the message format.- Send a
status: okmessage if the function invocation succeeded. - Send a
status: errormessage if the function invocation failed.
- Send a
-
The
widget_classin the initialcomm_openwill beurth.widgets.widget_dataframe.DataFrame -
A
sync_datacomm_msgwill be sent from the frontend following thecomm_openwith avariable_namefield whose value is the name of the data variable to use. Extra options such aslimitare also included in this message:{ header: { msg_type: "comm_msg", ... }, ... content: { data: { method: "backbone", sync_data: { variable_name: "<FUNCTION_NAME>", limit: <LIMIT> } }, ... } } -
The backend should then send a
statusmessage. See theStatus Messagessection for a full description of the message format.- Send a
status: okmessage if the DataFrame name registration succeeded. - Send a
status: errormessage if the DataFrame name registration failed, e.g. if no DataFrame with the given name has been declared.
- Send a
-
The frontend will then send a
syncmessage (see below)
-
A
customcomm_msgwill be sent from the frontend with anevent: "sync"field. This signals the backend to send a representation of the current value ofvariable_name.{ header: { msg_type: "comm_msg", ... }, ... content: { data: { method: "custom", content: { event: "sync" } } } } -
A representation of
variable_name's current value should be sent from the backend as acustomcomm_msgwithmethod: update. The message contains astateobject with avaluefield that holdsvariable_name's value representation. By convention, the DataFrame's value is represented bycolumns,index, anddatafields:{ header: { msg_type: "comm_msg", ... }, ... content: { data: { method: "update", state: { "value": { "columns": ["<COLNAME_1>", ..., "<COLNAME_N>"], "index": ["<ROWNAME_1>", ..., "<ROWNAME_M>"], "data": [[<data_11>, ..., <data_1n], ... [<data_m1>, ..., <data_mn]] } } }, ... } }
Declarative widgets use status messages to communicate success or failure. For instance, a Function widget sends an status: ok message following a successful function invocation, and a DataFrame widget sends a status: error message given an invalid DataFrame variable name.
A status message is a custom comm_msg with method: update, and a state object with a status field representing the status:
{
header: {
msg_type: "comm_msg",
...
},
...
content: {
data: {
method: "update",
state: {
"status": {
"status": "ok" | "error",
"msg": success or error message string,
"timestamp": long
}
}
},
...
}
}
Below are some key operations that will inevitably need to be supported by a backend implementation of Declarative Widgets.
- Executing a function with a given name, using a mapping of argument names to argument values as strings. For instance, the Scala widgets contain the function:
def invokeFunction(funcName: String, args: Map[String, String]): Try[Any]- Obtaining the argument names and types of a function with a given name. For instance, the Scala widgets contain the function:
def argTypes(funcName: String): List[(Name, Type)]-
Retrieving the current value of a variable given the variable's name. For instance, the Scala widgets retrieve the value using a function provided by the Spark Kernel's
Interpreter:
kernelInterpreter.read(dataFrameVariableName)