Skip to main content

A JupyterLab extension.

Project description

JupyterLab Plugin Playground

Github Actions StatusBinder

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jupyterlab_plugin_playground-0.2.0.tar.gz (188.6 kB view details)

Uploaded Source

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

Hashes for jupyterlab_plugin_playground-0.2.0.tar.gz
Algorithm Hash digest
SHA256 98047f1b24555e20e63dd53f84daafbe4e4e879eca0a1fbfcee7ee9a211208d3
MD5 3a8125a2b4803fd4561a2ebc5820d6a1
BLAKE2b-256 dcaef0c5c65a27b1b7b8d0a9d0dafb8b889147cfa991ec79d3cc339d3c96373a

See more details on using hashes here.

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

Hashes for jupyterlab_plugin_playground-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 08ffd444c6de7c3d53d6eda605a70b76158ca871de679796e9fa6b59387a1fd2
MD5 2ba794c4a85a7ef407a439c7a39a800a
BLAKE2b-256 52dc593295b3fe9cabd1704baac8a3c08b0f914d28e95d0f45b2f2b8b78e597e

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page