DEPRECATED: c.JupyterHub.extra_handlers - how to switch to services?

This is how we are currently doing it:

from jupyterhub.handlers.base import BaseHandler

class AboutHandler(BaseHandler):
    def initialize(self, profile_vars):
        self.profile_vars = profile_vars

    @web.authenticated
    async def get(self):
        html = await self.render_template(
            "about.html",
            juptyterhub_version=os.environ.get("JUPYTERHUB_IMAGE"),
            jupyterdb_version=os.environ.get("JUPYTERHUB_DB_IMAGE"),
            jupyterlab_versions=self.profile_vars,
        )
        await self.finish(html)

c.JupyterHub.extra_handlers.extend(
    [(r"about", AboutHandler, dict(profile_vars=profile_vars))]
)

This code works fine, but since extra_handlers were deprecated, how would I set these extra routes up as a new service? Are there any examples available how to set this up?

Would I use a Custom Class which inherites from Service like this?

from jupyterhub.services.service import Service

from smat_jupyterhub.views.handler import AboutHandler


class AboutService(Service):
    def __init__(self, profile_vars=None, **kwargs):
        super().__init__(**kwargs)
        self.handlers = [(r"/about", AboutHandler, dict(profile_vars=profile_vars))]

c.JupyterHub.services = [
    {
        "name": "aboutservice",
        "command": [sys.executable, "/usr/local/bin/aboutservice.py"],
        "url": "http://127.0.0.1:8080",
    },

It improved a bit, I see a new “Service” Link in my Navbar, however it is empty. I also tried just using the handler, but also results in an empty website.

This is my handler:

class AboutHandler(BaseHandler):
    def initialize(self, profile_vars):
        self.profile_vars = profile_vars

    @web.authenticated
    async def get(self):
        html = await self.render_template(
            "about.html",
            juptyterhub_version=os.environ.get("JUPYTERHUB_IMAGE"),
            jupyterdb_version=os.environ.get("JUPYTERHUB_DB_IMAGE"),
            jupyterlab_versions=self.profile_vars,
        )
        await self.finish(html)

We have an example service integrating with Hub authentication into a tornado application.

I encounter the same problem.
I have already made a service using RequestHandler with success.

But I need another service which display a template, so I want to use the BaseHandler which contains the render_template method.
In this case, I can’t get my service to run.

KeyError: 'hub'
Uncaught exception
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/tornado/http1connection.py", line 276, in _read_message
    delegate.finish()
  File "/usr/local/lib/python3.10/dist-packages/tornado/routing.py", line 268, in finish
    self.delegate.finish()
  File "/usr/local/lib/python3.10/dist-packages/tornado/web.py", line 2378, in finish
    self.execute()
  File "/usr/local/lib/python3.10/dist-packages/tornado/web.py", line 2400, in execute
    self.handler = self.handler_class(
  File "/usr/local/lib/python3.10/dist-packages/tornado/web.py", line 231, in __init__
    self.clear()
  File "/usr/local/lib/python3.10/dist-packages/tornado/web.py", line 332, in clear
    self.set_default_headers()
  File "/usr/local/lib/python3.10/dist-packages/jupyterhub/handlers/base.py", line 229, in set_default_headers
    self.set_header('Content-Security-Policy', self.content_security_policy)
  File "/usr/local/lib/python3.10/dist-packages/jupyterhub/handlers/base.py", line 204, in content_security_policy
    ["frame-ancestors 'self'", "report-uri " + self.csp_report_uri]
  File "/usr/local/lib/python3.10/dist-packages/jupyterhub/handlers/base.py", line 194, in csp_report_uri
    'csp_report_uri', url_path_join(self.hub.base_url, 'security/csp-report')
  File "/usr/local/lib/python3.10/dist-packages/jupyterhub/handlers/base.py", line 154, in hub
    return self.settings['hub']
KeyError: 'hub'

Here the code:

class MyHandler(BaseHandler):

    async def get(self):
        html = await self.render_template("custom_template.html", args=[])
        self.finish(html)

def main():
    app = Application(
        [
            (os.environ['JUPYTERHUB_SERVICE_PREFIX'], MyHandler),
            (r'.*', MyHandler),
        ],
        cookie_secret=os.urandom(32),
    )

    http_server = HTTPServer(app)
    url = urlparse(os.environ['JUPYTERHUB_SERVICE_URL'])

    http_server.listen(url.port, url.hostname)

    IOLoop.current().start()


if __name__ == '__main__':
    main()

@Data_Mastery Did you managed to adapt your code to service ?

Since, the services are separates applications, we can’t use jupyterhub handlers.

extra_handlers allow you to integrate new pages into jupyterhub (by extending jupyterhub templates).

Unfortunately, I don’t see how to reuse templates from jupyterhub without using extra_handlers. I feel like I’m going to have to review my entire application :frowning: .
I’m disappointed with the decision to remove this feature.

So everything we could do with extra_handler is not possible with services?

1 Like