def make_singleuser_app(App):
    """Make and return a singleuser notebook app

    given existing notebook or jupyter_server Application classes,
    mix-in jupyterhub auth.

    Instances of App must have the following attributes defining classes:

    - .login_handler_class
    - .logout_handler_class
    - .base_handler_class (only required if not a subclass of the default app
      in jupyter_server or notebook)

    App should be a subclass of `notebook.notebookapp.NotebookApp`
    or `jupyter_server.serverapp.ServerApp`.
    """

    empty_parent_app = App()
    log = empty_parent_app.log

    # detect base handler classes
    if not getattr(empty_parent_app, "login_handler_class", None) and hasattr(
        empty_parent_app, "identity_provider_class"
    ):
        # Jupyter Server 2 moves the login handler classes to the identity provider
        has_handlers = empty_parent_app.identity_provider_class(parent=empty_parent_app)
    else:
        # prior to Jupyter Server 2, the app itself had handler class config
        has_handlers = empty_parent_app
    LoginHandler = has_handlers.login_handler_class
    LogoutHandler = has_handlers.logout_handler_class
    BaseHandler = _patch_app_base_handlers(empty_parent_app)

    # create Handler classes from mixins + bases

    class JupyterHubLoginHandler(JupyterHubLoginHandlerMixin, LoginHandler):
        pass

    class JupyterHubLogoutHandler(JupyterHubLogoutHandlerMixin, LogoutHandler):
        pass

    class OAuthCallbackHandler(OAuthCallbackHandlerMixin, BaseHandler):
        pass

    # create merged aliases & flags
    merged_aliases = {}
    merged_aliases.update(empty_parent_app.aliases or {})
    merged_aliases.update(aliases)

    merged_flags = {}
    merged_flags.update(empty_parent_app.flags or {})
    merged_flags.update(flags)

    # create mixed-in App class, bringing it all together
    class SingleUserNotebookApp(SingleUserNotebookAppMixin, App):
        aliases = merged_aliases
        flags = merged_flags
        classes = empty_parent_app.classes + [HubOAuth]

        login_handler_class = JupyterHubLoginHandler
        logout_handler_class = JupyterHubLogoutHandler
        oauth_callback_handler_class = OAuthCallbackHandler

        def initialize(self, *args, **kwargs):
            result = super().initialize(*args, **kwargs)
            # run patch again after initialize, so extensions have already been loaded
            # probably a no-op most of the time
            _patch_app_base_handlers(self)
            return result

    return SingleUserNotebookApp
Make and return a singleuser notebook app

given existing notebook or jupyter_server Application classes,
mix-in jupyterhub auth.

Instances of App must have the following attributes defining classes:

- .login_handler_class
- .logout_handler_class
- .base_handler_class (only required if not a subclass of the default app
in jupyter_server or notebook)

App should be a subclass of `notebook.notebookapp.NotebookApp`
or `jupyter_server.serverapp.ServerApp`.
jupyterhub/singleuser/mixins.py