Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/SOF-7235: Add extension to bridge data between host and JL #2

Merged
merged 25 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Setup extension
run: |
bash ./setup.sh
- name: Install the dependencies
run: |
python -m pip install -r requirements.txt
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,6 @@ dmypy.json
# jupyterlite
*.doit.db
_output
.idea
.venv
.yarn
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,12 @@ For more info, keep an eye on the JupyterLite documentation:

- How-to Guides: https://jupyterlite.readthedocs.io/en/latest/howto/index.html
- Reference: https://jupyterlite.readthedocs.io/en/latest/reference/index.html

## Additional Notes

From Team Mat3ra:

- `data_bridge` extensions is built using the `setup.sh`
- pass `INSTALL=1 BUILD=1` to also build and install the jupyter lite with extension
- `requirements.txt` is updated as part of the above to include the extension
- requires `pyenv` and `npm` installed
71 changes: 71 additions & 0 deletions extensions/src/data_bridge/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {
JupyterFrontEnd,
JupyterFrontEndPlugin,
} from "@jupyterlab/application";

import { NotebookPanel } from "@jupyterlab/notebook";

/**
* Initialization data for the data-bridge extension.
* Similar to https://jupyterlite.readthedocs.io/en/latest/howto/configure/advanced/iframe.html
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a link to Jupyter Lite docs that we followed here

const plugin: JupyterFrontEndPlugin<void> = {
id: "data-bridge:plugin",
description:
"Extension to pass JSON data between host page and Jupyter Lite instance",
autoStart: true,
activate: async (app: JupyterFrontEnd) => {
console.log("JupyterLab extension data-bridge is activated!");

// @ts-ignore
window.sendDataToHost = (data: any) => {
window.parent.postMessage(
{
type: "from-iframe-to-host",
data: data,
},
"*"
);
};

// @ts-ignore
window.requestDataFromHost = () => {
window.parent.postMessage(
{
type: "from-iframe-to-host",
requestData: true,
},
"*"
);
};

// TODO: set type for data
window.addEventListener("message", async (event) => {
if (event.data.type === "from-host-to-iframe") {
let data = event.data.data;
const dataJson = JSON.stringify(data);
const code = `
import json
data = json.loads('${dataJson}')
`;
// Similar to https://jupyterlab.readthedocs.io/en/stable/api/classes/application.LabShell.html#currentWidget
// https://jupyterlite.readthedocs.io/en/latest/reference/api/ts/interfaces/jupyterlite_application.ISingleWidgetShell.html#currentwidget
const currentWidget = app.shell.currentWidget;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a link to JupyterLab docs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


if (currentWidget instanceof NotebookPanel) {
const notebookPanel = currentWidget;
const kernel = notebookPanel.sessionContext.session?.kernel;
if (kernel) {
kernel.requestExecute({ code: code });
} else {
console.error("No active kernel found");
}
} else {
console.error("Current active widget is not a notebook");
}
}
});
},
};

export default plugin;
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Core modules (mandatory)
jupyterlite-core==0.1.3
jupyterlab~=3.5.1
jupyterlab~=4.0.6

# Python kernel (optional)
jupyterlite-pyodide-kernel==0.1.3
Expand Down
105 changes: 105 additions & 0 deletions setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/bin/bash
# This script creates a JupyterLab extension using the cookiecutter template
# and updates the requirements.txt file to make it installable in the current
# JupyterLab environment.
# It assumes that pyenv and nvm are installed and configured correctly.

PYTHON_VERSION="3.10"
NODE_VERSION="18"
EXTENSION_NAME="data_bridge"
COOKIECUTTER_TEMPLATE_PATH="$HOME/.cookiecutters/extension-cookiecutter-ts"
GITHUB_TEMPLATE_URL="https://github.com/jupyterlab/extension-cookiecutter-ts"

kind="frontend"
author_name="Mat3ra"
author_email="[email protected]"
labextension_name=$EXTENSION_NAME
python_name=$EXTENSION_NAME
project_short_description="A JupyterLab extension that allows you to send data between notebook and host page"
has_settings=n
has_binder=n
test=n
repository="https://github.com/exabyte-io/jupyter-lite"

COOKIECUTTER_OPTIONS=(
"$GITHUB_TEMPLATE_URL"
"--no-input"
"kind=$kind"
"author_name=$author_name"
"author_email=$author_email"
"labextension_name=$labextension_name"
"python_name=$python_name"
"project_short_description=$project_short_description"
"has_settings=$has_settings"
"has_binder=$has_binder"
"test=$test"
"repository=$repository"
)

# Ensure Python and Node.js are installed and switch to the correct versions
if [ ! -d "$HOME/.pyenv/versions/$PYTHON_VERSION" ]; then
pyenv install $PYTHON_VERSION
fi
pyenv local $PYTHON_VERSION

python -m venv .venv
source .venv/bin/activate

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

nvm install $NODE_VERSION
nvm use $NODE_VERSION

pip install cookiecutter jupyterlab==4 jupyterlite-core

# Create directory if it doesn't exist
if [ ! -d "extensions/dist" ]; then
mkdir -p extensions/dist
fi
cd extensions/dist

# Use cookiecutter with the template path if it exists, otherwise use the URL
if [ ! -d "$COOKIECUTTER_TEMPLATE_PATH" ]; then
cookiecutter "${COOKIECUTTER_OPTIONS[@]}"
echo "Created extension using cookiecutter template."
else
# COOKIECUTTER_OPTIONS[0]="$COOKIECUTTER_TEMPLATE_PATH"
cookiecutter "${COOKIECUTTER_OPTIONS[@]}"
echo "Created extension using cached cookiecutter template."
fi

# Copy the index.ts file if both source and destination directories exist
SRC_FILE="../src/$EXTENSION_NAME/index.ts"
DEST_DIR="./$EXTENSION_NAME/src"
if [ -f "$SRC_FILE" ] && [ -d "$DEST_DIR" ]; then
cp "$SRC_FILE" "$DEST_DIR/index.ts"
else
echo "Source file or destination directory not found. Skipping copy."
fi

cd $EXTENSION_NAME
pip install -ve .
jupyter labextension develop --overwrite .

# Install dependencies
jlpm add @jupyterlab/application
jlpm add @jupyterlab/notebook
jlpm add @exabyte-io/code.js

# Build the extension
jlpm run build

cd ../../../

# add to requirements.txt
LINE="./extensions/dist/$EXTENSION_NAME"
FILE='requirements.txt'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"

# Install extension
[[ ! -z $INSTALL ]] && python -m pip install -r requirements.txt

# Build JupyterLite
[[ ! -z $BUILD ]] && jupyter lite build --contents content --output-dir dist

Loading