Skip to content

Jupyter Protocol and Specification

The Jupyter group contains the SDK packages for working with Jupyter specifications. These include notebook formatting, messaging specifications, as well as server APIs. The packages in this group relate to core concepts within Jupyter.

Table of contents


/commutable

The @nteract/commutable package is for creating an in-memory immutable representation of a Jupyter notebook.

This package follows the principles below. Tom MacWright's outline for practical undo offers the fundamental ideas below.

  • A notebook document is immutable. The notebook document's representation is never mutated in-place.
  • Operations form the changes to a notebook document. They take a previous version and return a new version of the notebook without modifying the old version.
  • A list of states composes the history. The past is on one end and the present is on the other. The index backs up into "undo states."
  • Modifying a notebook document discards any future states.

The package builds on top of the ImmutableJS library.

Notebook format

Jupyter notebooks serialize into files with .ipynb extensions. These files are JSON-based and follow a schema. The definition of the schema is in the nbformat specification.

The top-level properties in the notebook have the following schema.

Example:

{
  "metadata" : {
    "kernel_info": {
        # if kernel_info is defined, its name field is required.
        "name" : "the name of the kernel"
    },
    "language_info": {
        # if language_info is defined, its name field is required.
        "name" : "the programming language of the kernel",
        "version": "the version of the language",
        "codemirror_mode": "The name of the codemirror mode to use [optional]"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0,
  "cells" : [
      # list of cell dictionaries, see below
  ],
}

The cells properties contain a list of cells in the notebook. The fundamental structure for a cell uses the format below.

{
  "cell_type" : "type",
  "metadata" : {},
  "source" : "single string or [list, of, strings]",
}

For examples of serialized Jupyter notebooks, view the raw JSON for an example notebook.

/commutable in-memory format

The @nteract/commutable package converts the serialized representation into an immutable in-memory representation. The structure of the in-memory representation is different as the design is for easier in-memory format use when developing interactive notebook UIs. The interface for a notebooks is in the example below.

Example:

export interface NotebookRecordParams {
  cellOrder: ImmutableList<CellId>;
  cellMap: ImmutableMap<CellId, ImmutableCell>;
  nbformat_minor: number;
  nbformat: number;
  metadata: ImmutableMap<string, any>;
}

NOTE: This package generates a unique CellId for each cell it encounters when parsing the notebook. These CellIds are uuid-v4 identifiers. These refer to the cell in the in-memory store.

  • cellOrder: A list of CellIds in order of appearance in the notebook.
  • cellMap: An map that associates a CellId with the ImmutableCell it represents.
  • nbformat, nbformat_minor: The version of the nbformat that this notebook follows.
  • metadata: Top-level metadata stored in the notebook.

This Jupyter specification outlines three different cell types: code cells, Markdown cells, and raw cells. The /commutable package provides type interfaces and functions. These help create data objects to match the specifications of cell types.

Transient nteract data

The in-memory format includes an nteract.transient metadata field in each cell. This enables UI-specific interactions for nteract-based interfaces. Metadata fields in the Jupyter nbformat have no strong types. If a UI doesn't understand how to interpret a particular metadata field, it is safe for the UI to ignore the metadata field.

Users opening a notebook in nteract, working with the notebook altering its metadata property, then using another Jupyter UI do not cause issues to the notebook.

/commutable API documentation

All support actions are in this package's API docs.

Examples of /commutable

Creating a notebook in-memory model

To create an in-memory model of a notebook, pass a string containing the serialized contents of the notebook to the fromJS function.

In the example below, notebookString loads from a Jupyter server via the Jupyter Contents API. A filesystem API retrieves it from disk and loads it from a cloud storage provider with their API or anywhere else. The notebookString converts to the in-memory model when following the nbformat.

Example:

import { fromJS } from "@nteract/commutable";

const notebookString = "{ cells: [...], metadata: {...} }";
const immutableNotebook = fromJS(notebookString);

Create a code cell

To create a code cell, use the makeCodeCell function in the commutable API.

Example:

import { makeCodeCell } from "@nteract/commutable";

const codeCell = makeCodeCell({
  source: "print(1)"
});

NOTE: Not all the properties for a cell in the parameter passed to the makeCodeCell method. The package uses sensible defaults where appropriate. There are analogous functions to create other types of cells, such as makeMarkdownCell and makeRawCell.

/messaging

The @nteract/messaging package contains type definitions and helper functions for interacting with the Jupyter Messaging Protocol. These functions create different types of request and response messages.

Example

The example below shows how to use the createMessage function in this package to create an inspect_request Jupyter message.

Example:

import { createMessage } from "@nteract/messaging";

const message = createMessage("inspect_request", {
  code: "string.for",
  cursor_pos: 10,
  detail_level: 1
});

/rx-jupyter

The @nteract/rx-jupyter package is a ReactiveX wrapper around the Jupyter Server API. rx-jupyter helps query local and remote Jupyter Server instances using Jupyter's Services APIs. Also, rx-jupyter integrates responses seamlessly with RxJS's functional tooling.

Example

The example below shows how to use this package to get the version of the Jupyter server API the endpoint is running.

Example:

import jupyter from "rx-jupyter";
import { of } from "rxjs";
import { mergeMap, catchError } from "rxjs/operators";

const apiVersion = jupyter.apiVersion({
  endpoint: "https://myjupyterendpoint.com",
  crossDomain: true
});
apiVersion.pipe(
  mergeMap(apiVersion => of(apiVersionFulfilled({ apiVersion }))),
  catchError(error => of(apiVersionFailed({ error })))
);