Run a Jupyter ExtensionApp on Binder

I’m writing a Jupyter Extension Application that can be started with jupyter myapp, but I’m not sure how I can run it on Binder.

I found a page that explains how to use some popular interfaces, which seems to come down to “it’s automatically available” for stuff such as classic notebooks or RStudio.

Then I looked over the documentation which mentions the start file, but that needs to end in exec so it seems you can’t just launch you own UI there.

Then I looked over some examples.

I found this example on how to enable extensions for JupyterLab, but I think that’s not what I need as mine is its own app, not an extension to lab: GitHub - binder-examples/jupyter-extension: Enabling jupyter extensions for use in Binder

Then I looked at the Bokeh example, but that seems to use a whole proxy setup. GitHub - binder-examples/bokeh

So now I’m not sure if for a Jupyter server extension app, it will just automatically work, I need to run some magic command to enable my extension app, or I need to proxy it.

I hit my two links per post limit. Here is the page on server extension apps: Server Extensions — Jupyter Server 1.14.0.dev0 documentation

And the docs about configuring the UI I saw: Configure the user interface — Binder 0.1b documentation

I suspect you have a port that it is served on. If that is the case if you install the Jupyter server proxy along with it in your repo, you can use the proxy port in the URL to open the correct page. At least that is what has been my experience in the past, like what is discussed in this thread.

I think once you know it works, you can somehow customize the server proxy for your app and use URLs that handle the port behind the scenes to make appropriate entrypoints but I haven’t done any of that myself yet. I believe it is covered in the Jupyter server proxy documentation.

1 Like

Hm so when you run lab and nteract and classic notebooks, are nteract and classic notebooks just an extension to lab? Basically that’s exactly what I want to do.

I guess I could rewrite my app as a lab extension rather than its own app. Basically my app is JupyterLab, static pages, and a Bokeh app.

The main reason that outside Binder I want it to be an app is so that it has a nice entrypoint and the homepage is my app rather than /lab.

Somehow nteract is an extension that can be loaded into jupyterlab, yet also has its own entrypoint. How do I do just that?

Starting up a fresh cookiecutter it, will offer to build all of the boilerplate for a serverextension with a REST API.

During _load_jupyter_server_extension, an extension can change the ExtensionApplication.default_url and even have access to the underlying tornado.web.Application.

As for bokeh: while I don’t have any examples of it, I don’t see why one wouldn’t be able to run some bokeh handlers on the same loop as jupyter server. Likely one could subclass bokeh.server.Server and add in some special sauce to reuse the existing loop.

2 Likes

No, JupyterLab and the classic notebooks running via MyBinder aren’t extensions to each other as far as I know. I cannot comment on nteract.

The extension being talked with how to provide entrypoints without the proxy/1315/-type stuff being necessary in the URL when you want to switch to your app is a server extension. Not a JupyterLab extension. It’s an extension to Jupyter Server which is described here as:

Jupyter Server is the backend—the core services, APIs, and REST endpoints—to Jupyter web applications. Jupyter Server is a replacement for the Tornado Web Server in Jupyter Notebook. Jupyter web applications should move to using Jupyter Server.

1 Like

Okay so I followed the steps for distributing a server extension, and now if I install my package locally it shows up as an enabled server extension, and is available. I’ve also managed to enable the server proxy extension and use that together with a Bokeh server running on the Jupyter server IOLoop. So locally everything works perfectly now.

But on binder nothing works at all. The JSON file and Python module got installed and are importable, but listing the server extensions warns that it’s not importable. Huh?!

jovyan@jupyter-nyancad-2dpyttoresque-2dtemplates-2ddqyto6zs:~$ jupyter serverextension list
config dir: /srv/conda/envs/notebook/etc/jupyter
    jupyter_server_proxy  enabled 
    - Validating...
      jupyter_server_proxy  OK
    jupyter_resource_usage  enabled 
    - Validating...
      jupyter_resource_usage 0.6.1 OK
    jupyterlab  enabled 
    - Validating...
      jupyterlab 3.2.5 OK
    nteract_on_jupyter  enabled 
    - Validating...
      nteract_on_jupyter 2.1.3 OK
    jupyter_offlinenotebook  enabled 
    - Validating...
      jupyter_offlinenotebook  OK
    pyttoresque  enabled 
    - Validating...
      X is pyttoresque importable?
jovyan@jupyter-nyancad-2dpyttoresque-2dtemplates-2ddqyto6zs:~$ python
Python 3.7.12 | packaged by conda-forge | (default, Oct 26 2021, 06:08:53) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyttoresque
>>> 
jovyan@jupyter-nyancad-2dpyttoresque-2dtemplates-2ddqyto6zs:~$ cat /srv/conda/envs/notebook/etc/jupyter/jupyter_server_config.d/jupyter-server-proxy-jupyterserverextension.json 
{
  "ServerApp": {
    "jpserver_extensions": {
      "jupyter_server_proxy": true
    }
  }
}
jovyan@jupyter-nyancad-2dpyttoresque-2dtemplates-2ddqyto6zs:~$ cat /srv/conda/envs/notebook/etc/jupyter/jupyter_server_config.d/pyttoresque.json 
{
    "ServerApp": {
        "jpserver_extensions": {
            "pyttoresque": true
        }
    }
}

My template repo: GitHub - NyanCAD/Pyttoresque-templates: Notebook templates for Pyttoresque simulations
My extension: GitHub - NyanCAD/Pyttoresque

Perhaps you’ve run aground on the transition between jupyter lab (which you are running locally) and jupyter notebook which is what binder runs.

The nickle fix on this is to deploy files to all of etc/jupyter/jupyter_(notebook|server)_config.d, a la this extension.

Confusingly, these are reported by jupyter server extension list (which your pacakging should be impacting) and jupyter serverextension list (which you are assessing) for jupyter_server and notebook respectively.

Note that, correctly packaged, you shouldn’t even need your postBuild but it is definitely useful in this case for listing things…

As to why your package is not importable… no idea. Since you have proxy installed, you could actually fire up a second instance of jupyter notebook --debug from a terminal and watch the log spoor there…

2 Likes

Ah! It’s working! In addition to that I also needed to add this compat mechanism, which was the cause of the import error: Server Extensions — Jupyter Server 1.14.0.dev0 documentation

Now there are just a few minor nits to pick.

Currently I can only access Bokeh via the proxy URL. I tried to use server_document but it gets confused about the URLs and tries to load stuff from the wrong place. Not the end of the world.

I’m currently starting Bokeh in ExtensionApp.initialize_handlers which feels a bit improper. Most importantly, it seems that with the notebook server this gets called before starting the server, which means io_loop is not yet set. I think in practice it doesn’t matter since they both use the default singleton IOLoop instance.

I noticed that jupyter-proxy-server adds launcher icons to JupyterLab if you configure it correctly. I should try to figure out how to do that for my app so there is an easy way to open it from JupyterLab.

1 Like

jupyter-proxy-server adds launcher icons to JupyterLab

Welp… if you don’t need proxy, and your stuff can run (well) in-loop, then you might just need a very simple (e.g. close to one-liner activate) launcher extension… but that would bring along the whole nodejs shooting match. Otherwise, yeah, proxy is pretty good for getting stuff up and going, and the new request rewriting stuff is great.

3 Likes