https://github.com/nteract/enchannel
:currency_exchange: standardizing how a frontend communicates with a kernel
https://github.com/nteract/enchannel
Last synced: about 2 months ago
JSON representation
:currency_exchange: standardizing how a frontend communicates with a kernel
- Host: GitHub
- URL: https://github.com/nteract/enchannel
- Owner: nteract
- License: bsd-3-clause
- Created: 2016-01-12T06:54:21.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2017-08-13T20:33:10.000Z (almost 8 years ago)
- Last Synced: 2025-03-30T21:02:21.255Z (2 months ago)
- Language: JavaScript
- Homepage:
- Size: 35.2 KB
- Stars: 10
- Watchers: 2
- Forks: 8
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# enchannel
Enchannel is a lightweight spec for flexible communications between a
frontend, like the [notebook](https://github.com/jupyter/notebook) or
[jupyter-sidecar](https://github.com/nteract/jupyter-sidecar), and a backend
kernel (the runtime, like Python, Julia, or R). Enchannel does not specify
the implementation or how the communications are constructed or destructed.[](https://cloud.githubusercontent.com/assets/836375/12282043/b19bb16e-b960-11e5-8661-ce2111ec0417.png)
## Motivation
### Background
The core functionality of the notebook is to send messages from a frontend to
a backend, and from a backend to a frontend ([or many
frontends](https://github.com/nteract/jupyter-sidecar)). In the case of the
Jupyter/IPython notebook, it communicates over websockets (which in turn reach
out to ØMQ on the backend).### What if...?
What if you want to serve the same HTML and Javascript for the notebook
application itself while being able to work in a native ØMQ environment? What
if websockets are fairly restricted in your working \*ahem\* *corporate*
environment and you need to send data via `POST` and receive streaming updates
using server-sent events?### Solutions
Well, we'd need a nice, clean way to abstract the transport layer. As [Jupyter
is messages all the way
down](http://jupyter-client.readthedocs.org/en/latest/messaging.html), hooking
up a series of event emitters, all with the same interface, is one
abstraction. That's [definitely
do-able](https://github.com/nteract/jupyter-transport-wrapper).Instead, let's rely on **Observables**: asynchronous data streams, [*from the
future*](https://zenparsing.github.io/es-observable/). Observables, as
flexible transport, are the multi-valued promise we've all been waiting for:
| | Single return value | Mutiple return values |
| ---------------------------- | ------------------- | -------------------------------------- |
| Pull/Synchronous/Interactive | Object | Iterables (Array, Set, Map, Object) |
| Push/Asynchronous/Reactive | Promise | Observable |Note: The enchannel spec uses RxJS's observables implementation.
---
## **enchannel** your data
### The spec
Kernel communications are described by a single object containing
[subjects](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md),
each corresponding to a communication channel of the kernel instance. There will
be between four and five channels:```js
const {
shell,
stdin,
control,
iopub,
heartbeat, // (optional)
} = channelsObject;
```*[For more information see the Jupyter client docs.](http://jupyter-client.readthedocs.org/en/latest/messaging.html)*
Relying on RxJS's implementation of subjects means the streams can be handled
like so:```javascript
iopub.filter(msg => msg.header.msg_type === 'execute_result')
.map(msg => msg.content.data)
.subscribe(x => { console.log(`DATA: ${util.inspect(x)}`)})
```As a benefit of subjects, we can go ahead and submit messages to the
underlying transport:```javascript
var message = {
header: {
msg_id: `execute_${uuid.v4()}`,
username: '',
session: '00000000-0000-0000-0000-000000000000',
msg_type: 'execute_request',
version: '5.0',
},
content: {
code: 'print("woo")',
silent: false,
store_history: true,
user_expressions: {},
allow_stdin: false,
},
};shell.next(message); // send the message
```Messages observed from these Subjects are all immutable, not by convention but
through a recursive `Object.freeze`.Note that
[heartbeat](http://jupyter-client.readthedocs.org/en/latest/messaging.html#heartbeat-for-kernels)
is not included in the spec above primarily because it's an implementation
by-product and may end up being deprecated based on the chosen development
approach.## What's in this repo? Convenience.
In addition to the spec doc itself (the text above) this repo contains
**convenience functions** for enchannel implementations and consumers. To use
these functions install this package with npm:npm install enchannel
The utility functions included are described below.
#### isChildMessage
Checks to see if one message is child to another. Accepts two arguments,
parent and child, both of which are [Jupyter message
objects](https://ipython.org/ipython-doc/3/development/messaging.html#general-message-format).
To use as a conditional:```js
const enchannel = require('enchannel');
if (enchannel.isChildMessage(parent, child)) {
console.log('is child');
}
```It will probably make more sense to use it as an observable filter. In the
example below, `parent` is a [Jupyter message
object](https://ipython.org/ipython-doc/3/development/messaging.html#general-message-format)
and `channels.iopub` is an RxJS observable:```js
const enchannel = require('enchannel');
const isChildMessage = enchannel.isChildMessage.bind(null, parent);
const childMessages = channels.iopub.filter(isChildMessage);
```#### createMessage
Creates a [Jupyter message object](https://ipython.org/ipython-doc/3/development/messaging.html#general-message-format) that accepts three arguments:- username: string
- session: string, `guid` unique to the current session
- msg_type: string, type of message being sentThis full example shows how you'd setup the session and
username, and then create and send a shutdown request:```js
channels = ...connected using an enchannel backend...// Created once with the channels
const uuid = require('uuid');
const session = uuid.v4();
const username = process.env.LOGNAME || process.env.USER ||
process.env.LNAME || process.env.USERNAME;// Create the shutdown request method
const enchannel = require('enchannel');
const shutdownRequest = enchannel.createMessage(username, session, 'shutdown_request');
shutdownRequest.content = { restart: false };// Send it
// Before sending, don't forget to subscribe to the channel you are sending on! In practice
// there is more code involved here, because you'd want to filter the messages your subscribing
// to for messages that are child to the one that you send.
channels.shell.subscribe(content => { /* ... */ });
channels.shell.next(shutdownRequest);
```#### shutdownRequest
Sends a [shutdown request Jupyter message](https://ipython.org/ipython-doc/3/development/messaging.html#kernel-shutdown) to the kernel and completes the observables. Accepts three arguments:- channels: object, enchannel channels object
- username, string
- session, string, `guid` unique to the current session
- restart: optional boolean, whether the shutdown request is actually a restart requestThe following example shows how this method would be used:
```js
const enchannel = require('enchannel');
console.log('begin shutdown');
enchannel.shutdownRequest(channels, username, session, restart).then(() => {
console.log('finished shutting down');
});
```## Develop with us
To contribute to the spec or convenience functions, clone this repo and
install it by running the following from the repo root:```
npm install
```Before contributing changes to the utility functions, be kind to your peers
and check if the unit tests pass locally by running:```
npm test
```## Implementations
* [enchannel-zmq-backend](https://github.com/nteract/enchannel-zmq-backend)
* [enchannel-socketio-backend](https://github.com/nteract/enchannel-socketio-backend)## References
* [The introduction to Reactive Programming you've been missing](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754)
* [RxJS (5.0)](https://github.com/ReactiveX/RxJS)
* [RxJS (4.0)](https://github.com/Reactive-Extensions/RxJS)