Skip to content
This repository was archived by the owner on Sep 1, 2022. It is now read-only.

Hybrid Elements Message Spec

Gino Bustelo edited this page May 10, 2016 · 2 revisions

!!! NEEDS UPDATING TO LATEST MESSAGES

Backend Message Spec for Declarative Widgets

The comm protocol is used for communicating between the front-end and backend.

The Declarative Widgets contain several 'core' widgets that require backend implementations. The following spec defines the comm message formats that backend implementations will receive from the front-end as input and must send as output.

By adhering to the spec, the backend widgets can be implemented in a new language.

Widget Instantiation

  • The frontend sends a comm_open message on the ipython.widget channel containing a widget_class field:

      {
      	header: {
      		msg_type: "comm_open",
      		...
      	},
      	...
      	content: {
      		comm_id: "<COMM_ID>",
      		target_name: "ipython.widget",
      		data: {
      			widget_class: "<WIDGET_CLASS_NAME>"
      		}
      	}
      }
    

Function Widget

Initialization

  • The widget_class in the initial comm_open will be urth.widgets.widget_function.Function

  • A sync_data comm_msg will be sent from the frontend following the comm_open with a function_name field whose value is the name of the function to use. Extra options such as limit 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 the Status 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 the signature.

Invocation

  • A custom comm_msg will be sent from the frontend with an event: "invoke" field, and an args: {...} field. The args 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 the function_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 with method: update. The message contains a state object with a result 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 the Status 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.

DataFrame Widget

Initialization

  • The widget_class in the initial comm_open will be urth.widgets.widget_dataframe.DataFrame

  • A sync_data comm_msg will be sent from the frontend following the comm_open with a variable_name field whose value is the name of the data variable to use. Extra options such as limit 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 the Status 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.
  • The frontend will then send a sync message (see below)

Sync

  • A custom comm_msg will be sent from the frontend with an event: "sync" field. This signals the backend to send a representation of the current value of variable_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 a custom comm_msg with method: update. The message contains a state object with a value field that holds variable_name's value representation. By convention, the DataFrame's value is represented by columns, index, and data 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]]
      				}
      			}
      		},
      		...
      	}
      }
    

Status Messages

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
					}
				}
			},
			...
		}
	}

Implementation Notes

Below are some key operations that will inevitably need to be supported by a backend implementation of Declarative Widgets.

Function Widget

  • 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)]

DataFrame Widget

  • 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)
Clone this wiki locally