    def get_env(self):
        """Return the environment dict to use for the Spawner.

        This applies things like `env_keep`, anything defined in `Spawner.environment`,
        and adds the API token to the env.

        When overriding in subclasses, subclasses must call `super().get_env()`, extend the
        returned dict and return it.

        Use this to access the env in Spawner.start to allow extension in subclasses.
        """
        env = {}
        if self.env:
            warnings.warn(
                f"Spawner.env is deprecated, found {self.env}", DeprecationWarning
            )
            env.update(self.env)

        for key in self.env_keep:
            if key in os.environ:
                env[key] = os.environ[key]

        env['JUPYTERHUB_API_TOKEN'] = self.api_token
        # deprecated (as of 0.7.2), for old versions of singleuser
        env['JPY_API_TOKEN'] = self.api_token
        if self.admin_access:
            env['JUPYTERHUB_ADMIN_ACCESS'] = '1'
        # OAuth settings
        env['JUPYTERHUB_CLIENT_ID'] = self.oauth_client_id
        if self.cookie_options:
            env['JUPYTERHUB_COOKIE_OPTIONS'] = json.dumps(self.cookie_options)

        env["JUPYTERHUB_COOKIE_HOST_PREFIX_ENABLED"] = str(
            int(self.cookie_host_prefix_enabled)
        )
        env['JUPYTERHUB_HOST'] = self.hub.public_host
        env['JUPYTERHUB_OAUTH_CALLBACK_URL'] = url_path_join(
            self.user.url, url_escape_path(self.name), 'oauth_callback'
        )

        # deprecated env, renamed in 3.0 for disambiguation
        env['JUPYTERHUB_OAUTH_SCOPES'] = json.dumps(self.oauth_access_scopes)
        env['JUPYTERHUB_OAUTH_ACCESS_SCOPES'] = json.dumps(self.oauth_access_scopes)

        # added in 3.0
        env['JUPYTERHUB_OAUTH_CLIENT_ALLOWED_SCOPES'] = json.dumps(
            self.oauth_client_allowed_scopes
        )

        # Info previously passed on args
        env['JUPYTERHUB_USER'] = self.user.name
        env['JUPYTERHUB_SERVER_NAME'] = self.name
        if self.hub_connect_url is not None:
            hub_api_url = url_path_join(
                self.hub_connect_url, urlparse(self.hub.api_url).path
            )
        else:
            hub_api_url = self.hub.api_url
        env['JUPYTERHUB_API_URL'] = hub_api_url
        env['JUPYTERHUB_ACTIVITY_URL'] = url_path_join(
            hub_api_url,
            'users',
            # tolerate mocks defining only user.name
            getattr(self.user, 'escaped_name', self.user.name),
            'activity',
        )
        env['JUPYTERHUB_BASE_URL'] = self.hub.base_url[:-4]

        if self.server:
            base_url = self.server.base_url
            env['JUPYTERHUB_SERVICE_PREFIX'] = self.server.base_url
        else:
            # this should only occur in mock/testing scenarios
            base_url = '/'

        proto = 'https' if self.internal_ssl else 'http'
        bind_url = f"{proto}://{self.ip}:{self.port}{base_url}"
        env["JUPYTERHUB_SERVICE_URL"] = bind_url

        # the public URLs of this server and the Hub
        env["JUPYTERHUB_PUBLIC_URL"] = self.public_url
        env["JUPYTERHUB_PUBLIC_HUB_URL"] = self.public_hub_url

        # Put in limit and guarantee info if they exist.
        # Note that this is for use by the humans / notebook extensions in the
        # single-user notebook server, and not for direct usage by the spawners
        # themselves. Spawners should just use the traitlets directly.
        if self.mem_limit:
            env['MEM_LIMIT'] = str(self.mem_limit)
        if self.mem_guarantee:
            env['MEM_GUARANTEE'] = str(self.mem_guarantee)
        if self.cpu_limit:
            env['CPU_LIMIT'] = str(self.cpu_limit)
        if self.cpu_guarantee:
            env['CPU_GUARANTEE'] = str(self.cpu_guarantee)

        if self.cert_paths:
            env['JUPYTERHUB_SSL_KEYFILE'] = self.cert_paths['keyfile']
            env['JUPYTERHUB_SSL_CERTFILE'] = self.cert_paths['certfile']
            env['JUPYTERHUB_SSL_CLIENT_CA'] = self.cert_paths['cafile']

        if self.notebook_dir:
            notebook_dir = self.format_string(self.notebook_dir)
            env["JUPYTERHUB_ROOT_DIR"] = notebook_dir

        if self.default_url:
            default_url = self.format_string(self.default_url)
            env["JUPYTERHUB_DEFAULT_URL"] = default_url

        if self.debug:
            env["JUPYTERHUB_DEBUG"] = "1"

        if self.disable_user_config:
            env["JUPYTERHUB_DISABLE_USER_CONFIG"] = "1"

        # env overrides from config. If the value is a callable, it will be called with
        # one parameter - the current spawner instance - and the return value
        # will be assigned to the environment variable. This will be called at
        # spawn time.
        # Called last to ensure highest priority, in case of overriding other
        # 'default' variables like the API url
        for key, value in self.environment.items():
            if callable(value):
                env[key] = value(self)
            else:
                env[key] = value
        return env
Return the environment dict to use for the Spawner.

This applies things like `env_keep`, anything defined in `Spawner.environment`,
and adds the API token to the env.

When overriding in subclasses, subclasses must call `super().get_env()`, extend the
returned dict and return it.

Use this to access the env in Spawner.start to allow extension in subclasses.
jupyterhub/spawner.py