How to update handler of server extension without restarting Jupyter?

Hello I am following the example of how to create a server-side extension here https://github.com/jupyterlab/extension-examples/tree/main/server-extension

When I make changes to the handler.py file, the the changes are not available in Jupyter until I stop Jupyter completely and then re run it. Is there way to update that module without totally restarting Jupyter?

This is basically the same question that was asked here Jupyterlab 2 --watch does not update on server extension python changes but it was not answered.

@fcollonval since you are creator of this fine example. Thank you!

An approach is to run two servers:

  • start one, as usual, from the command line on e.g. 8888
  • start a second one on 8889 with the Python API from inside a running IPython kernel on the same event loop as the kernel
from jupyter_server.serverapp import ServerApp

app = ServerApp()
app.initialize(["--port=8889"])
app.start_app()
print(app.display_url.splitlines()[0])

In that mess, one should see the new token, so a second browser tab can be opened to the display_url that gets printed.

This also generally works with IPython’s %autoreload 2, such that one can work on at-rest .py files, but there are limits… sometimes it can be effective to do prototyping in a notebook, then move the code to .py when they are ready to be tested.

While in this prototyping phase, one has access to the second running server, and can change just about anything about what it knows. For example, one can make a handler…

from jupyter_server.base.handlers import APIHandler
from tornado.web import authenticated

class SumHandler(APIHandler):
    @authenticated
    async def get(self, a, b):
        self.finish({"sum": float(a) + float(b)})

app.web_app.add_handlers(".*", [
    (app.base_url + r"sum/(-?[0-9.]+)/(-?[0-9.]+)", SumHandler),
])

… and make a request against it from within the same kernel:

from tornado.escape import json_decode
from tornado.httpclient import AsyncHTTPClient

client = AsyncHTTPClient()
r = await client.fetch(f"http://127.0.0.1:8890/sum/1/2?token={app.token}")
print(json_decode(r.body))

Note that something like requests would block, so the whole thing would grind to a halt: one can use a second kernel to make such requests, but then the token has to be known between servers.

2 Likes