-
Notifications
You must be signed in to change notification settings - Fork 38
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_open
message on thejupyter.widget
channel containing awidget_class
field:{ 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_class
in the initialcomm_open
will beurth.widgets.widget_function.Function
-
A
sync_data
comm_msg
will be sent from the frontend following thecomm_open
with afunction_name
field whose value is the name of the function to use. Extra options such aslimit
are 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
status
message. See theStatus Messages
section for a full description of the message format.- Send a
status: ok
message if the function name registration succeeded. - Send a
status: error
message 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
custom
comm_msg
will be sent from the frontend with anevent: "invoke"
field, and anargs: {...}
field. Theargs
field 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_name
field.{ 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
custom
comm_msg
withmethod: update
. The message contains astate
object with aresult
field 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
status
message. See theStatus Messages
section for a full description of the message format.- Send a
status: ok
message if the function invocation succeeded. - Send a
status: error
message if the function invocation failed.
- Send a
-
The
widget_class
in the initialcomm_open
will beurth.widgets.widget_dataframe.DataFrame
-
A
sync_data
comm_msg
will be sent from the frontend following thecomm_open
with avariable_name
field whose value is the name of the data variable to use. Extra options such aslimit
are 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
status
message. See theStatus Messages
section for a full description of the message format.- Send a
status: ok
message if the DataFrame name registration succeeded. - Send a
status: error
message 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
sync
message (see below)
-
A
custom
comm_msg
will 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 acustom
comm_msg
withmethod: update
. The message contains astate
object with avalue
field that holdsvariable_name
's value representation. By convention, the DataFrame's value is represented bycolumns
,index
, anddata
fields:{ 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)