    def _patch_get_login_url(self, handler):
        original_get_login_url = handler.get_login_url

        _hub_login_url = None

        def get_login_url():
            """Return the Hub's login URL, to begin login redirect"""
            nonlocal _hub_login_url
            if _hub_login_url is not None:
                # cached value, don't call this more than once per handler
                return _hub_login_url
            # temporary override at settings level,
            # to allow any subclass overrides of get_login_url to preserve their effect;
            # for example, APIHandler raises 403 to prevent redirects
            with mock.patch.dict(
                handler.application.settings, {"login_url": self.hub_auth.login_url}
            ):
                login_url = original_get_login_url()
            self.log.debug("Redirecting to login url: %s", login_url)
            # add state argument to OAuth url
            # must do this _after_ allowing get_login_url to raise
            # so we don't set unused cookies
            state = self.hub_auth.set_state_cookie(
                handler, next_url=handler.request.uri
            )
            _hub_login_url = url_concat(login_url, {'state': state})
            return _hub_login_url

        handler.get_login_url = get_login_url
Return the Hub's login URL, to begin login redirect
jupyterhub/singleuser/extension.py