Server Extensions#
A Jupyter Server extension is typically a module or package that extends to Server’s REST API/endpoints—i.e. adds extra request handlers to Server’s Tornado Web Application.
For examples of jupyter server extensions, see the homepage.
To get started writing your own extension, see the simple examples in the examples folder in the GitHub jupyter_server repository.
Distributing a server extension#
Putting it all together, authors can distribute their extension following this steps:
- Add a
_jupyter_server_extension_points()
function at the extension’s root. This function should likely live in the
__init__.py
found at the root of the extension package. It will look something like this:# Found in the __init__.py of package def _jupyter_server_extension_points(): return [{"module": "myextension.app", "app": MyExtensionApp}]
- Add a
- Create an extension by writing a
_load_jupyter_server_extension()
function or subclassingExtensionApp
. This is where the extension logic will live (i.e. custom extension handlers, config, etc). See the sections above for more information on how to create an extension.
- Create an extension by writing a
- Add the following JSON config file to the extension package.
The file should be named after the extension (e.g.
myextension.json
) and saved in a subdirectory of the package with the prefix:jupyter-config/jupyter_server_config.d/
. The extension package will have a similar structure to this example:myextension ├── myextension/ │ ├── __init__.py │ └── app.py ├── jupyter-config/ │ └── jupyter_server_config.d/ │ └── myextension.json └── setup.py
The contents of the JSON file will tell Jupyter Server to load the extension when a user installs the package:
{ "ServerApp": { "jpserver_extensions": { "myextension": true } } }
When the extension is installed, this JSON file will be copied to the
jupyter_server_config.d
directory found in one of Jupyter’s paths.Users can toggle the enabling/disableing of extension using the command:
jupyter server extension disable myextension
which will change the boolean value in the JSON file above.
- Create a
setup.py
that automatically enables the extension. Add a few extra lines the extension package’s
setup
functionfrom setuptools import setup setup( name="myextension", # ... include_package_data=True, data_files=[ ( "etc/jupyter/jupyter_server_config.d", ["jupyter-config/jupyter_server_config.d/myextension.json"], ), ], )
- Create a
Migrating an extension to use Jupyter Server#
If you’re a developer of a classic Notebook Server extension, your extension
should be able to work with both the classic notebook server and
jupyter_server
.
There are a few key steps to make this happen:
- Point Jupyter Server to the
load_jupyter_server_extension
function with a new reference name. The
load_jupyter_server_extension
function was the key to loading a server extension in the classic Notebook Server. Jupyter Server expects the name of this function to be prefixed with an underscore—i.e._load_jupyter_server_extension
. You can easily achieve this by adding a reference to the old function name with the new name in the same module.def load_jupyter_server_extension(nb_server_app): ... # Reference the old function name with the new function name. _load_jupyter_server_extension = load_jupyter_server_extension
- Point Jupyter Server to the
- Add new data files to your extension package that enable it with Jupyter Server.
This new file can go next to your classic notebook server data files. Create a new sub-directory,
jupyter_server_config.d
, and add a new.json
file there:myextension ├── myextension/ │ ├── __init__.py │ └── app.py ├── jupyter-config/ │ └── jupyter_notebook_config.d/ │ └── myextension.json │ └── jupyter_server_config.d/ │ └── myextension.json └── setup.py
The new
.json
file should look something like this (you’ll notice the changes in the configured class and trait names):{ "ServerApp": { "jpserver_extensions": { "myextension": true } } }
Update your extension package’s
setup.py
so that the data-files are moved into the jupyter configuration directories when users download the package.from setuptools import setup setup( name="myextension", # ... include_package_data=True, data_files=[ ( "etc/jupyter/jupyter_server_config.d", ["jupyter-config/jupyter_server_config.d/myextension.json"], ), ( "etc/jupyter/jupyter_notebook_config.d", ["jupyter-config/jupyter_notebook_config.d/myextension.json"], ), ], )
- (Optional) Point extension at the new favicon location.
The favicons in the Jupyter Notebook have been moved to a new location in Jupyter Server. If your extension is using one of these icons, you’ll want to add a set of redirect handlers this. (In
ExtensionApp
, this is handled automatically).This usually means adding a chunk to your
load_jupyter_server_extension
function similar to this:def load_jupyter_server_extension(nb_server_app): web_app = nb_server_app.web_app host_pattern = ".*$" base_url = web_app.settings["base_url"] # Add custom extensions handler. custom_handlers = [ # ... ] # Favicon redirects. favicon_redirects = [ ( url_path_join(base_url, "/static/favicons/favicon.ico"), RedirectHandler, { "url": url_path_join( serverapp.base_url, "static/base/images/favicon.ico" ) }, ), ( url_path_join(base_url, "/static/favicons/favicon-busy-1.ico"), RedirectHandler, { "url": url_path_join( serverapp.base_url, "static/base/images/favicon-busy-1.ico" ) }, ), ( url_path_join(base_url, "/static/favicons/favicon-busy-2.ico"), RedirectHandler, { "url": url_path_join( serverapp.base_url, "static/base/images/favicon-busy-2.ico" ) }, ), ( url_path_join(base_url, "/static/favicons/favicon-busy-3.ico"), RedirectHandler, { "url": url_path_join( serverapp.base_url, "static/base/images/favicon-busy-3.ico" ) }, ), ( url_path_join(base_url, "/static/favicons/favicon-file.ico"), RedirectHandler, { "url": url_path_join( serverapp.base_url, "static/base/images/favicon-file.ico" ) }, ), ( url_path_join(base_url, "/static/favicons/favicon-notebook.ico"), RedirectHandler, { "url": url_path_join( serverapp.base_url, "static/base/images/favicon-notebook.ico" ) }, ), ( url_path_join(base_url, "/static/favicons/favicon-terminal.ico"), RedirectHandler, { "url": url_path_join( serverapp.base_url, "static/base/images/favicon-terminal.ico" ) }, ), ( url_path_join(base_url, "/static/logo/logo.png"), RedirectHandler, {"url": url_path_join(serverapp.base_url, "static/base/images/logo.png")}, ), ] web_app.add_handlers(host_pattern, custom_handlers + favicon_redirects)