Skip to main content

Jupyter Extensions for the Rubin Science Platform

Project description

rsp_jupyter_extensions

Github Actions Status Jupyter Extensions for the Rubin Science Platform

This extension is composed of a Python package named rsp_jupyter_extensions for the server extension and a NPM package named rsp-jupyter-extensions for the frontend extension.

RSP-Specific instructions

This is a very Rubin (and RSP, particularly)-specific package. Rubin Observatory's development practices don't entirely track what JupyterLab expects you to do. Some notes for use by Rubin developers follow:

If you need to change the rsp-jupyter-extensions package, you will need to know that it is a prebuilt extension created from the JupyterLab extension copier template.

To this copier template we have added GitHub Actions to rebuild containers and client libraries and push them to their respective locations in artifact registries and PyPI.

We have also added a Makefile and modified pyproject.toml in order to make it behave more like a standard SQuaRE repository in some ways.

The way our extensions work is that they contain both a backend server component (written in Python, acessible under the /rubin endpoint within the lab) and a frontend UI component, written in TypeScript.

A request from the user's browser (either via an action taken by a user, or something done on UI load at startup) will go to the backend server, and receive a reply which will guide the UI's action.

The "prebuilt" part

The "prebuilt" part of "prebuilt extension" means both that there is no need to install nodejs in the Lab container and that the only thing that needs doing to activate the extension is to pip install it.

The build process within rsp-jupyter-extensions will generate and pack the extension's JavaScript.

Versioning the extension

One not-at-all-obvious corollary of using the copier template to generate the extension framework is that we do not use the standard SQuaRE release process to create a new version tag.

Instead the new version must be specified in package.json in the extension root directory.

Using the Makefile

We use make to initialize the development environment with make init.

make typing typechecks both the TypeScript UI components and the Python server components. As a side effect (!!) it also builds the packed Javascript that is included with the package.

make lint lints both the TypeScript and the Python components.

make test runs the test suite for the Python back-end server.

There are not currently effective tests for the TypeScript components, only a basic smoke test to ensure that it loads into JupyterLab without throwing an exception.

Developing the server backend

The server backend behaves much like a standard SQuaRE service. It is written in typed Python and follows the usual SQuaRE guidelines with respect to linting and typing. It is found in the rsp_jupyter_extensions directory.

Write the service

Write your service; it almost certainly belongs in the handlers subdirectory. The handler should probably derive from the APIHandler superclass; look at another handler for the pattern. The APIHandler will return JSON to the caller, which is trivially parsed by the UI APIRequest() function.

If you end up requiring models of any real complexity for the extension, put them in the models directory and use Pydantic to represent them. In theory we should derive the models for both the UI side and the server side using Swagger or something similar, but in practice that seems like a lot of work for what are pretty trivial bits of code, and keeping them in sync manually is not hard.

One thing that is not obvious about backend services is that you get a brand new object on each HTTP request to the backend. If you need to maintain state between requests, you cannot do it in-process. For servers that need to cache state, I have been using the filesystem to do this, in the user's $HOME/.cache directory.

Add the route

In the top-level __init__.py (that is, in the rsp_jupyter_extensions directory), add your new handler (whose route should start with /rubin) to the map in _setup_handlers(). This will load your server extension into JupyterLab and make it accessible via the route you choose.

Developing the UI

Adding a new TypeScript component is done in the src top-level directory.

Choose a token

First, if your extension includes a UI widget (most do, but not all; for instance, the environment extension extracts the environment from the server side for the UI's consumption, but does not itself have any user-visible interface in the browser), assign a token (an arbitrary string) to the widget in tokens.ts.

Write the extension

The extension should get its own .ts file in the src directory; when you export the extension, its id attribute should be the token and its autostart attribute should be set to false. That is because it will be activated by the top-level index.

Note that, once you have the environment, you can use the logMessage() function to log messages to the console at a specified level. Usually, INFO or higher messages will be shown, but if Enable debug logging was checked on the spawn page, you will get DEBUG messages too. This is often extremely handy for determining why your extension isn't working.

Your extension will probably consume a JSON object via an apiRequest() call to the back end and take action based on the contents of that object's fields.

Update the index to load the extension

Finally, the top-level index, in index.ts, should be modified to load your new extension at the appropriate place in the order.

That place is very likely after the environment has been loaded, and in general should probably go towards the bottom of the order. This explicit activation is why individual components should not be autostarted.

Look at the existing index.ts for the way progress log messages are formatted. Maintaining this format makes it easier to use the browser console to debug startup errors.

Requirements

  • JupyterLab >= 4.0.0

Install

To install the extension, execute:

pip install rsp_jupyter_extensions

Uninstall

To remove the extension, execute:

pip uninstall rsp_jupyter_extensions

Troubleshoot

If you are seeing the frontend extension, but it is not working, check that the server extension is enabled:

jupyter server extension list

If the server extension is installed and enabled, but you are not seeing the frontend extension, check the frontend extension is installed:

jupyter labextension list

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 rsp_jupyter_extensions directory
# Install package in development mode
pip install -e ".[test]"
# Link your development version of the extension with JupyterLab
jupyter labextension develop . --overwrite
# Server extension must be manually installed in develop mode
jupyter server extension enable rsp_jupyter_extensions
# Rebuild extension Typescript source after making changes
jlpm 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 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 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

# Server extension must be manually disabled in develop mode
jupyter server extension disable rsp_jupyter_extensions
pip uninstall rsp_jupyter_extensions

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 rsp-jupyter-extensions within that folder.

Testing the extension

Server tests

This extension is using Pytest for Python code testing.

Install test dependencies (needed only once):

pip install -e ".[test]"
# Each time you install the Python package, you need to restore the front-end extension link
jupyter labextension develop . --overwrite

To execute them, run:

pytest -vv -r ap --cov rsp_jupyter_extensions

Frontend tests

This extension is using Jest for JavaScript code testing.

To execute them, execute:

jlpm
jlpm test

Integration tests

This extension uses Playwright for the integration tests (aka user level tests). More precisely, the JupyterLab helper Galata is used to handle testing the extension in JupyterLab.

More information are provided within the ui-tests README.

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

rsp_jupyter_extensions-0.18.1.tar.gz (294.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rsp_jupyter_extensions-0.18.1-py3-none-any.whl (49.7 kB view details)

Uploaded Python 3

File details

Details for the file rsp_jupyter_extensions-0.18.1.tar.gz.

File metadata

  • Download URL: rsp_jupyter_extensions-0.18.1.tar.gz
  • Upload date:
  • Size: 294.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for rsp_jupyter_extensions-0.18.1.tar.gz
Algorithm Hash digest
SHA256 b0df0f1a70b4e835445830d3486fb78d435ad727a9ade985d6d493975354aaa4
MD5 fd3c17263881af44f33c3130dd19a4c8
BLAKE2b-256 39787c645b8c4262cac4b3fa29b06695752177c20f2fefca835fe26253c9e827

See more details on using hashes here.

Provenance

The following attestation bundles were made for rsp_jupyter_extensions-0.18.1.tar.gz:

Publisher: ci.yaml on lsst-sqre/rsp-jupyter-extensions

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rsp_jupyter_extensions-0.18.1-py3-none-any.whl.

File metadata

File hashes

Hashes for rsp_jupyter_extensions-0.18.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1b1405619e5ebb28fb05546819f953b16a28be6bb50b6cdf985c062e8f47e30b
MD5 27cedab6a21b6bb4d3b524829d2f1b57
BLAKE2b-256 94f5cb300111d79471cb6b829ec531c57675d815b0f49377af203cd0094bab59

See more details on using hashes here.

Provenance

The following attestation bundles were made for rsp_jupyter_extensions-0.18.1-py3-none-any.whl:

Publisher: ci.yaml on lsst-sqre/rsp-jupyter-extensions

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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