A JupyterLab extension.
Project description
JupyterLab Plugin Playground
A JupyterLab extension to load JupyterLab extensions (dynamically).
One of the big impediments when developing JupyterLab (JLab) extensions is that it's currently required to rebuild JupyterLab – and that takes time. JavaScript used to be so easy!
This extension is trying to bring some of that old magic back, by dynamically loading and injecting JupyterLab extensions into JupyterLab. Just refresh JupyterLab to re-load the extension, no recompilation necessary!
Note that for real deployments, it might still be a lot better to actually compile the extension into JLab, to benefit from proper minimization and, more importantly, deduplication.
jupyterlab-plugin-playground uses require.js to dynamically load the extensions, and require.js supports the AMD module syntax. Using an AMD module, you can also dynamically load widget extensions to jupyterlab -- however, there is one caveat -- the widget needs to be loaded before opening the first notebook.
As a dynamic extension cannot just use the JupyterLab modules, we dynamically look up the modules in the required section, and inject them from JLab. Here is an example for a dynamic extension:
{
id: 'mydynamicplugin',
autoStart: true,
requires: ["@jupyterlab/apputils:ICommandPalette"],
activate: function(app, palette) {
console.log("Hello from a dynamically loaded plugin!");
// We can use `.app` here to do something with the JupyterLab
// app instance, such as adding a new command
let commandID = "MySuperCoolDynamicCommand";
let toggle = true;
app.commands.addCommand(commandID, {
label: 'My Super Cool Dynamic Command',
isToggled: function() {
return toggle;
},
execute: function() {
console.log("Executed " + commandID);
toggle = !toggle;
}
});
palette.addItem({
command: commandID,
// make it appear right at the top!
category: 'AAA',
args: {}
});
}
}
As you can see, the dynamic extension returns exactly the same dictionary as one would from a regular JupyterLab extension.
Part of the magic is in the requires
-- these strings (TokenIDs) are inspected, and the live instance of the mentioned plugins is used to call the activate
function (app
always comes as default). In this case we are reqiring the CommandPalette
in order to attach a new command to it.
If you want to register a new widget library, for example, you would require the "jupyter.extensions.jupyterWidgetRegistry"
instead.
These tokens can be a little arbitrary, so at this point it might require some digging into the JupyterLab source code to figure out
Here is one example of dynamically loading the bqplot
widget library from unpkg.com:
{
id: 'mydynamicwidget',
autoStart: true,
requires: ["jupyter.extensions.jupyterWidgetRegistry"],
activate: function(app, widgets) {
require.config({
// take the widget from jsdelivr
baseUrl: "https://cdn.jsdelivr.net/npm"
});
let widget = 'bqplot';
// note that we are using require js here to load the AMD module
// requirejs is automatically loaded with @jupyterlab/plugin-playground.
// * (star) selects the latest version from jsdelivr, and then loads the `/dist/index.js` file
// the final URL will be something like https://cdn.jsdelivr.net/npm/bqplot@*/dist/index.js
require([widget + "@*/dist/index"], function(plugin) {
widgets.registerWidget({
name: widget,
version: plugin.version,
exports: plugin
});
});
}
}
If you want to test some dynamic scripts locally, then there is also a small test server available under /scripts
which serves the directory content.
Loading from URL or current file
In the JupyterLab settings you can configure some URL's to load scripts automatically from (e.g. GitHub gists).
And last but not least, you can also edit a JavaScript file inside JupyterLab, and then run the command "Load current file as extension" to load the current file as a JupyterLab extension. Note: currently it's only possible to run this command once for each id
. To support loading the same widget multiple times, we would need to clear the previous extension - but this might not be side-effect free. We currently don't have a solution for this, but we could either add a deactivate
function to call on clear, or we could just have the author of the extension make sure that reloading the extension is possible without breaking everything.
Requirements
- JupyterLab >= 3.0
Install
To install the extension, execute:
pip install jupyterlab-plugin-playground
Uninstall
To remove the extension, execute:
pip uninstall jupyterlab-plugin-playground
Contributing
Development install
Note: You will need NodeJS to build the extension package.
The jlpm
command is JupyterLab's pinned version of
yarn that is installed with JupyterLab. You may use
yarn
or npm
in lieu of jlpm
below.
# Clone the repo to your local environment
# Change directory to the jupyterlab-plugin-playground directory
# Install package in development mode
pip install -e .
# Link your development version of the extension with JupyterLab
jupyter labextension develop . --overwrite
# Rebuild extension Typescript source after making changes
jlpm run build
You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension.
# Watch the source directory in one terminal, automatically rebuilding when needed
jlpm run watch
# Run JupyterLab in another terminal
jupyter lab
With the watch command running, every saved change will immediately be built locally and available in your running JupyterLab. Refresh JupyterLab to load the change in your browser (you may need to wait several seconds for the extension to be rebuilt).
By default, the jlpm run build
command generates the source maps for this extension to make it easier to debug using the browser dev tools. To also generate source maps for the JupyterLab core extensions, you can run the following command:
jupyter lab build --minimize=False
Development uninstall
pip uninstall jupyterlab-plugin-playground
In development mode, you will also need to remove the symlink created by jupyter labextension develop
command. To find its location, you can run jupyter labextension list
to figure out where the labextensions
folder is located. Then you can remove the symlink named @jupyterlab/plugin-playground
within that folder.
Packaging the extension
See RELEASE
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file jupyterlab_plugin_playground-0.2.0.tar.gz
.
File metadata
- Download URL: jupyterlab_plugin_playground-0.2.0.tar.gz
- Upload date:
- Size: 188.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/0.0.0 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 98047f1b24555e20e63dd53f84daafbe4e4e879eca0a1fbfcee7ee9a211208d3 |
|
MD5 | 3a8125a2b4803fd4561a2ebc5820d6a1 |
|
BLAKE2b-256 | dcaef0c5c65a27b1b7b8d0a9d0dafb8b889147cfa991ec79d3cc339d3c96373a |
File details
Details for the file jupyterlab_plugin_playground-0.2.0-py3-none-any.whl
.
File metadata
- Download URL: jupyterlab_plugin_playground-0.2.0-py3-none-any.whl
- Upload date:
- Size: 152.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/0.0.0 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 08ffd444c6de7c3d53d6eda605a70b76158ca871de679796e9fa6b59387a1fd2 |
|
MD5 | 2ba794c4a85a7ef407a439c7a39a800a |
|
BLAKE2b-256 | 52dc593295b3fe9cabd1704baac8a3c08b0f914d28e95d0f45b2f2b8b78e597e |