You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			953 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			953 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Python
		
	
"""A tornado based Jupyter lab server."""
 | 
						|
 | 
						|
# Copyright (c) Jupyter Development Team.
 | 
						|
# Distributed under the terms of the Modified BSD License.
 | 
						|
 | 
						|
import dataclasses
 | 
						|
import json
 | 
						|
import os
 | 
						|
import sys
 | 
						|
 | 
						|
from jupyter_core.application import JupyterApp, NoStart, base_aliases, base_flags
 | 
						|
from jupyter_server._version import version_info as jpserver_version_info
 | 
						|
from jupyter_server.serverapp import flags
 | 
						|
from jupyter_server.utils import url_path_join as ujoin
 | 
						|
from jupyterlab_server import (
 | 
						|
    LabServerApp,
 | 
						|
    LicensesApp,
 | 
						|
    WorkspaceExportApp,
 | 
						|
    WorkspaceImportApp,
 | 
						|
    WorkspaceListApp,
 | 
						|
)
 | 
						|
from jupyterlab_server.config import get_static_page_config
 | 
						|
from notebook_shim.shim import NotebookConfigShimMixin
 | 
						|
from traitlets import Bool, Instance, Type, Unicode, default
 | 
						|
 | 
						|
from ._version import __version__
 | 
						|
from .commands import (
 | 
						|
    DEV_DIR,
 | 
						|
    HERE,
 | 
						|
    AppOptions,
 | 
						|
    build,
 | 
						|
    clean,
 | 
						|
    ensure_app,
 | 
						|
    ensure_core,
 | 
						|
    ensure_dev,
 | 
						|
    get_app_dir,
 | 
						|
    get_app_version,
 | 
						|
    get_user_settings_dir,
 | 
						|
    get_workspaces_dir,
 | 
						|
    pjoin,
 | 
						|
    watch,
 | 
						|
    watch_dev,
 | 
						|
)
 | 
						|
from .coreconfig import CoreConfig
 | 
						|
from .debuglog import DebugLogFileMixin
 | 
						|
from .extensions import MANAGERS as EXT_MANAGERS
 | 
						|
from .extensions.manager import PluginManager
 | 
						|
from .extensions.readonly import ReadOnlyExtensionManager
 | 
						|
from .handlers.announcements import (
 | 
						|
    CheckForUpdate,
 | 
						|
    CheckForUpdateABC,
 | 
						|
    CheckForUpdateHandler,
 | 
						|
    NewsHandler,
 | 
						|
    check_update_handler_path,
 | 
						|
    news_handler_path,
 | 
						|
)
 | 
						|
from .handlers.build_handler import Builder, BuildHandler, build_path
 | 
						|
from .handlers.error_handler import ErrorHandler
 | 
						|
from .handlers.extension_manager_handler import ExtensionHandler, extensions_handler_path
 | 
						|
from .handlers.plugin_manager_handler import PluginHandler, plugins_handler_path
 | 
						|
 | 
						|
DEV_NOTE = """You're running JupyterLab from source.
 | 
						|
If you're working on the TypeScript sources of JupyterLab, try running
 | 
						|
 | 
						|
    jupyter lab --dev-mode --watch
 | 
						|
 | 
						|
 | 
						|
to have the system incrementally watch and build JupyterLab for you, as you
 | 
						|
make changes.
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
CORE_NOTE = """
 | 
						|
Running the core application with no additional extensions or settings
 | 
						|
"""
 | 
						|
 | 
						|
build_aliases = dict(base_aliases)
 | 
						|
build_aliases["app-dir"] = "LabBuildApp.app_dir"
 | 
						|
build_aliases["name"] = "LabBuildApp.name"
 | 
						|
build_aliases["version"] = "LabBuildApp.version"
 | 
						|
build_aliases["dev-build"] = "LabBuildApp.dev_build"
 | 
						|
build_aliases["minimize"] = "LabBuildApp.minimize"
 | 
						|
build_aliases["debug-log-path"] = "DebugLogFileMixin.debug_log_path"
 | 
						|
 | 
						|
build_flags = dict(base_flags)
 | 
						|
 | 
						|
build_flags["dev-build"] = (
 | 
						|
    {"LabBuildApp": {"dev_build": True}},
 | 
						|
    "Build in development mode.",
 | 
						|
)
 | 
						|
build_flags["no-minimize"] = (
 | 
						|
    {"LabBuildApp": {"minimize": False}},
 | 
						|
    "Do not minimize a production build.",
 | 
						|
)
 | 
						|
build_flags["splice-source"] = (
 | 
						|
    {"LabBuildApp": {"splice_source": True}},
 | 
						|
    "Splice source packages into app directory.",
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
version = __version__
 | 
						|
app_version = get_app_version()
 | 
						|
if version != app_version:
 | 
						|
    version = f"{__version__} (dev), {app_version} (app)"
 | 
						|
 | 
						|
build_failure_msg = """Build failed.
 | 
						|
Troubleshooting: If the build failed due to an out-of-memory error, you
 | 
						|
may be able to fix it by disabling the `dev_build` and/or `minimize` options.
 | 
						|
 | 
						|
If you are building via the `jupyter lab build` command, you can disable
 | 
						|
these options like so:
 | 
						|
 | 
						|
jupyter lab build --dev-build=False --minimize=False
 | 
						|
 | 
						|
You can also disable these options for all JupyterLab builds by adding these
 | 
						|
lines to a Jupyter config file named `jupyter_config.py`:
 | 
						|
 | 
						|
c.LabBuildApp.minimize = False
 | 
						|
c.LabBuildApp.dev_build = False
 | 
						|
 | 
						|
If you don't already have a `jupyter_config.py` file, you can create one by
 | 
						|
adding a blank file of that name to any of the Jupyter config directories.
 | 
						|
The config directories can be listed by running:
 | 
						|
 | 
						|
jupyter --paths
 | 
						|
 | 
						|
Explanation:
 | 
						|
 | 
						|
- `dev-build`: This option controls whether a `dev` or a more streamlined
 | 
						|
`production` build is used. This option will default to `False` (i.e., the
 | 
						|
`production` build) for most users. However, if you have any labextensions
 | 
						|
installed from local files, this option will instead default to `True`.
 | 
						|
Explicitly setting `dev-build` to `False` will ensure that the `production`
 | 
						|
build is used in all circumstances.
 | 
						|
 | 
						|
- `minimize`: This option controls whether your JS bundle is minified
 | 
						|
during the Webpack build, which helps to improve JupyterLab's overall
 | 
						|
performance. However, the minifier plugin used by Webpack is very memory
 | 
						|
intensive, so turning it off may help the build finish successfully in
 | 
						|
low-memory environments.
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
class LabBuildApp(JupyterApp, DebugLogFileMixin):
 | 
						|
    version = version
 | 
						|
    description = """
 | 
						|
    Build the JupyterLab application
 | 
						|
 | 
						|
    The application is built in the JupyterLab app directory in `/staging`.
 | 
						|
    When the build is complete it is put in the JupyterLab app `/static`
 | 
						|
    directory, where it is used to serve the application.
 | 
						|
    """
 | 
						|
    aliases = build_aliases
 | 
						|
    flags = build_flags
 | 
						|
 | 
						|
    # Not configurable!
 | 
						|
    core_config = Instance(CoreConfig, allow_none=True)
 | 
						|
 | 
						|
    app_dir = Unicode("", config=True, help="The app directory to build in")
 | 
						|
 | 
						|
    name = Unicode("JupyterLab", config=True, help="The name of the built application")
 | 
						|
 | 
						|
    version = Unicode("", config=True, help="The version of the built application")
 | 
						|
 | 
						|
    dev_build = Bool(
 | 
						|
        None,
 | 
						|
        allow_none=True,
 | 
						|
        config=True,
 | 
						|
        help="Whether to build in dev mode. Defaults to True (dev mode) if there are any locally linked extensions, else defaults to False (production mode).",
 | 
						|
    )
 | 
						|
 | 
						|
    minimize = Bool(
 | 
						|
        True,
 | 
						|
        config=True,
 | 
						|
        help="Whether to minimize a production build (defaults to True).",
 | 
						|
    )
 | 
						|
 | 
						|
    pre_clean = Bool(
 | 
						|
        False, config=True, help="Whether to clean before building (defaults to False)"
 | 
						|
    )
 | 
						|
 | 
						|
    splice_source = Bool(False, config=True, help="Splice source packages into app directory.")
 | 
						|
 | 
						|
    def start(self):
 | 
						|
        app_dir = self.app_dir or get_app_dir()
 | 
						|
        app_options = AppOptions(
 | 
						|
            app_dir=app_dir,
 | 
						|
            logger=self.log,
 | 
						|
            core_config=self.core_config,
 | 
						|
            splice_source=self.splice_source,
 | 
						|
        )
 | 
						|
        self.log.info(f"JupyterLab {version}")
 | 
						|
        with self.debug_logging():
 | 
						|
            if self.pre_clean:
 | 
						|
                self.log.info(f"Cleaning {app_dir}")
 | 
						|
                clean(app_options=app_options)
 | 
						|
            self.log.info(f"Building in {app_dir}")
 | 
						|
            try:
 | 
						|
                production = None if self.dev_build is None else not self.dev_build
 | 
						|
                build(
 | 
						|
                    name=self.name,
 | 
						|
                    version=self.version,
 | 
						|
                    app_options=app_options,
 | 
						|
                    production=production,
 | 
						|
                    minimize=self.minimize,
 | 
						|
                )
 | 
						|
            except Exception as e:
 | 
						|
                self.log.error(build_failure_msg)
 | 
						|
                raise e
 | 
						|
 | 
						|
 | 
						|
clean_aliases = dict(base_aliases)
 | 
						|
clean_aliases["app-dir"] = "LabCleanApp.app_dir"
 | 
						|
 | 
						|
ext_warn_msg = "WARNING: this will delete all of your extensions, which will need to be reinstalled"
 | 
						|
 | 
						|
clean_flags = dict(base_flags)
 | 
						|
clean_flags["extensions"] = (
 | 
						|
    {"LabCleanApp": {"extensions": True}},
 | 
						|
    f"Also delete <app-dir>/extensions.\n{ext_warn_msg}",
 | 
						|
)
 | 
						|
clean_flags["settings"] = (
 | 
						|
    {"LabCleanApp": {"settings": True}},
 | 
						|
    "Also delete <app-dir>/settings",
 | 
						|
)
 | 
						|
clean_flags["static"] = (
 | 
						|
    {"LabCleanApp": {"static": True}},
 | 
						|
    "Also delete <app-dir>/static",
 | 
						|
)
 | 
						|
clean_flags["all"] = (
 | 
						|
    {"LabCleanApp": {"all": True}},
 | 
						|
    f"Delete the entire contents of the app directory.\n{ext_warn_msg}",
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
class LabCleanAppOptions(AppOptions):
 | 
						|
    extensions = Bool(False)
 | 
						|
    settings = Bool(False)
 | 
						|
    staging = Bool(True)
 | 
						|
    static = Bool(False)
 | 
						|
    all = Bool(False)
 | 
						|
 | 
						|
 | 
						|
class LabCleanApp(JupyterApp):
 | 
						|
    version = version
 | 
						|
    description = """
 | 
						|
    Clean the JupyterLab application
 | 
						|
 | 
						|
    This will clean the app directory by removing the `staging` directories.
 | 
						|
    Optionally, the `extensions`, `settings`, and/or `static` directories,
 | 
						|
    or the entire contents of the app directory, can also be removed.
 | 
						|
    """
 | 
						|
    aliases = clean_aliases
 | 
						|
    flags = clean_flags
 | 
						|
 | 
						|
    # Not configurable!
 | 
						|
    core_config = Instance(CoreConfig, allow_none=True)
 | 
						|
 | 
						|
    app_dir = Unicode("", config=True, help="The app directory to clean")
 | 
						|
 | 
						|
    extensions = Bool(False, config=True, help=f"Also delete <app-dir>/extensions.\n{ext_warn_msg}")
 | 
						|
 | 
						|
    settings = Bool(False, config=True, help="Also delete <app-dir>/settings")
 | 
						|
 | 
						|
    static = Bool(False, config=True, help="Also delete <app-dir>/static")
 | 
						|
 | 
						|
    all = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help=f"Delete the entire contents of the app directory.\n{ext_warn_msg}",
 | 
						|
    )
 | 
						|
 | 
						|
    def start(self):
 | 
						|
        app_options = LabCleanAppOptions(
 | 
						|
            logger=self.log,
 | 
						|
            core_config=self.core_config,
 | 
						|
            app_dir=self.app_dir,
 | 
						|
            extensions=self.extensions,
 | 
						|
            settings=self.settings,
 | 
						|
            static=self.static,
 | 
						|
            all=self.all,
 | 
						|
        )
 | 
						|
        clean(app_options=app_options)
 | 
						|
 | 
						|
 | 
						|
class LabPathApp(JupyterApp):
 | 
						|
    version = version
 | 
						|
    description = """
 | 
						|
    Print the configured paths for the JupyterLab application
 | 
						|
 | 
						|
    The application path can be configured using the JUPYTERLAB_DIR
 | 
						|
        environment variable.
 | 
						|
    The user settings path can be configured using the JUPYTERLAB_SETTINGS_DIR
 | 
						|
        environment variable or it will fall back to
 | 
						|
        `/lab/user-settings` in the default Jupyter configuration directory.
 | 
						|
    The workspaces path can be configured using the JUPYTERLAB_WORKSPACES_DIR
 | 
						|
        environment variable or it will fall back to
 | 
						|
        '/lab/workspaces' in the default Jupyter configuration directory.
 | 
						|
    """
 | 
						|
 | 
						|
    def start(self):
 | 
						|
        print(f"Application directory:   {get_app_dir()}")
 | 
						|
        print(f"User Settings directory: {get_user_settings_dir()}")
 | 
						|
        print(f"Workspaces directory: {get_workspaces_dir()}")
 | 
						|
 | 
						|
 | 
						|
class LabWorkspaceExportApp(WorkspaceExportApp):
 | 
						|
    version = version
 | 
						|
 | 
						|
    @default("workspaces_dir")
 | 
						|
    def _default_workspaces_dir(self):
 | 
						|
        return get_workspaces_dir()
 | 
						|
 | 
						|
 | 
						|
class LabWorkspaceImportApp(WorkspaceImportApp):
 | 
						|
    version = version
 | 
						|
 | 
						|
    @default("workspaces_dir")
 | 
						|
    def _default_workspaces_dir(self):
 | 
						|
        return get_workspaces_dir()
 | 
						|
 | 
						|
 | 
						|
class LabWorkspaceListApp(WorkspaceListApp):
 | 
						|
    version = version
 | 
						|
 | 
						|
    @default("workspaces_dir")
 | 
						|
    def _default_workspaces_dir(self):
 | 
						|
        return get_workspaces_dir()
 | 
						|
 | 
						|
 | 
						|
class LabWorkspaceApp(JupyterApp):
 | 
						|
    version = version
 | 
						|
    description = """
 | 
						|
    Import or export a JupyterLab workspace or list all the JupyterLab workspaces
 | 
						|
 | 
						|
    There are three sub-commands for export, import or listing of workspaces. This app
 | 
						|
        should not otherwise do any work.
 | 
						|
    """
 | 
						|
    subcommands = {}
 | 
						|
    subcommands["export"] = (
 | 
						|
        LabWorkspaceExportApp,
 | 
						|
        LabWorkspaceExportApp.description.splitlines()[0],
 | 
						|
    )
 | 
						|
    subcommands["import"] = (
 | 
						|
        LabWorkspaceImportApp,
 | 
						|
        LabWorkspaceImportApp.description.splitlines()[0],
 | 
						|
    )
 | 
						|
    subcommands["list"] = (
 | 
						|
        LabWorkspaceListApp,
 | 
						|
        LabWorkspaceListApp.description.splitlines()[0],
 | 
						|
    )
 | 
						|
 | 
						|
    def start(self):
 | 
						|
        try:
 | 
						|
            super().start()
 | 
						|
            self.log.error("One of `export`, `import` or `list` must be specified.")
 | 
						|
            self.exit(1)
 | 
						|
        except NoStart:
 | 
						|
            pass
 | 
						|
        self.exit(0)
 | 
						|
 | 
						|
 | 
						|
class LabLicensesApp(LicensesApp):
 | 
						|
    version = version
 | 
						|
 | 
						|
    dev_mode = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help="""Whether to start the app in dev mode. Uses the unpublished local
 | 
						|
        JavaScript packages in the `dev_mode` folder.  In this case JupyterLab will
 | 
						|
        show a red stripe at the top of the page.  It can only be used if JupyterLab
 | 
						|
        is installed as `pip install -e .`.
 | 
						|
        """,
 | 
						|
    )
 | 
						|
 | 
						|
    app_dir = Unicode("", config=True, help="The app directory for which to show licenses")
 | 
						|
 | 
						|
    aliases = {
 | 
						|
        **LicensesApp.aliases,
 | 
						|
        "app-dir": "LabLicensesApp.app_dir",
 | 
						|
    }
 | 
						|
 | 
						|
    flags = {
 | 
						|
        **LicensesApp.flags,
 | 
						|
        "dev-mode": (
 | 
						|
            {"LabLicensesApp": {"dev_mode": True}},
 | 
						|
            "Start the app in dev mode for running from source.",
 | 
						|
        ),
 | 
						|
    }
 | 
						|
 | 
						|
    @default("app_dir")
 | 
						|
    def _default_app_dir(self):
 | 
						|
        return get_app_dir()
 | 
						|
 | 
						|
    @default("static_dir")
 | 
						|
    def _default_static_dir(self):
 | 
						|
        return pjoin(self.app_dir, "static")
 | 
						|
 | 
						|
 | 
						|
aliases = dict(base_aliases)
 | 
						|
aliases.update(
 | 
						|
    {
 | 
						|
        "ip": "ServerApp.ip",
 | 
						|
        "port": "ServerApp.port",
 | 
						|
        "port-retries": "ServerApp.port_retries",
 | 
						|
        "keyfile": "ServerApp.keyfile",
 | 
						|
        "certfile": "ServerApp.certfile",
 | 
						|
        "client-ca": "ServerApp.client_ca",
 | 
						|
        "notebook-dir": "ServerApp.root_dir",
 | 
						|
        "browser": "ServerApp.browser",
 | 
						|
        "pylab": "ServerApp.pylab",
 | 
						|
    }
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
class LabApp(NotebookConfigShimMixin, LabServerApp):
 | 
						|
    version = version
 | 
						|
 | 
						|
    name = "lab"
 | 
						|
    app_name = "JupyterLab"
 | 
						|
 | 
						|
    # Should your extension expose other server extensions when launched directly?
 | 
						|
    load_other_extensions = True
 | 
						|
 | 
						|
    description = """
 | 
						|
    JupyterLab - An extensible computational environment for Jupyter.
 | 
						|
 | 
						|
    This launches a Tornado based HTML Server that serves up an
 | 
						|
    HTML5/Javascript JupyterLab client.
 | 
						|
 | 
						|
    JupyterLab has three different modes of running:
 | 
						|
 | 
						|
    * Core mode (`--core-mode`): in this mode JupyterLab will run using the JavaScript
 | 
						|
      assets contained in the installed `jupyterlab` Python package. In core mode, no
 | 
						|
      extensions are enabled. This is the default in a stable JupyterLab release if you
 | 
						|
      have no extensions installed.
 | 
						|
    * Dev mode (`--dev-mode`): uses the unpublished local JavaScript packages in the
 | 
						|
      `dev_mode` folder.  In this case JupyterLab will show a red stripe at the top of
 | 
						|
      the page.  It can only be used if JupyterLab is installed as `pip install -e .`.
 | 
						|
    * App mode: JupyterLab allows multiple JupyterLab "applications" to be
 | 
						|
      created by the user with different combinations of extensions. The `--app-dir` can
 | 
						|
      be used to set a directory for different applications. The default application
 | 
						|
      path can be found using `jupyter lab path`.
 | 
						|
    """
 | 
						|
 | 
						|
    examples = """
 | 
						|
        jupyter lab                       # start JupyterLab
 | 
						|
        jupyter lab --dev-mode            # start JupyterLab in development mode, with no extensions
 | 
						|
        jupyter lab --core-mode           # start JupyterLab in core mode, with no extensions
 | 
						|
        jupyter lab --app-dir=~/myjupyterlabapp # start JupyterLab with a particular set of extensions
 | 
						|
        jupyter lab --certfile=mycert.pem # use SSL/TLS certificate
 | 
						|
    """
 | 
						|
 | 
						|
    aliases = aliases
 | 
						|
    aliases.update(
 | 
						|
        {
 | 
						|
            "watch": "LabApp.watch",
 | 
						|
        }
 | 
						|
    )
 | 
						|
    aliases["app-dir"] = "LabApp.app_dir"
 | 
						|
 | 
						|
    flags = flags
 | 
						|
    flags["core-mode"] = (
 | 
						|
        {"LabApp": {"core_mode": True}},
 | 
						|
        "Start the app in core mode.",
 | 
						|
    )
 | 
						|
    flags["dev-mode"] = (
 | 
						|
        {"LabApp": {"dev_mode": True}},
 | 
						|
        "Start the app in dev mode for running from source.",
 | 
						|
    )
 | 
						|
    flags["skip-dev-build"] = (
 | 
						|
        {"LabApp": {"skip_dev_build": True}},
 | 
						|
        "Skip the initial install and JS build of the app in dev mode.",
 | 
						|
    )
 | 
						|
    flags["watch"] = ({"LabApp": {"watch": True}}, "Start the app in watch mode.")
 | 
						|
    flags["splice-source"] = (
 | 
						|
        {"LabApp": {"splice_source": True}},
 | 
						|
        "Splice source packages into app directory.",
 | 
						|
    )
 | 
						|
    flags["expose-app-in-browser"] = (
 | 
						|
        {"LabApp": {"expose_app_in_browser": True}},
 | 
						|
        "Expose the global app instance to browser via window.jupyterapp.",
 | 
						|
    )
 | 
						|
    flags["extensions-in-dev-mode"] = (
 | 
						|
        {"LabApp": {"extensions_in_dev_mode": True}},
 | 
						|
        "Load prebuilt extensions in dev-mode.",
 | 
						|
    )
 | 
						|
    flags["collaborative"] = (
 | 
						|
        {"LabApp": {"collaborative": True}},
 | 
						|
        """To enable real-time collaboration, you must install the extension `jupyter_collaboration`.
 | 
						|
        You can install it using pip for example:
 | 
						|
 | 
						|
            python -m pip install jupyter_collaboration
 | 
						|
 | 
						|
        This flag is now deprecated and will be removed in JupyterLab v5.""",
 | 
						|
    )
 | 
						|
    flags["custom-css"] = (
 | 
						|
        {"LabApp": {"custom_css": True}},
 | 
						|
        "Load custom CSS in template html files. Default is False",
 | 
						|
    )
 | 
						|
 | 
						|
    subcommands = {
 | 
						|
        "build": (LabBuildApp, LabBuildApp.description.splitlines()[0]),
 | 
						|
        "clean": (LabCleanApp, LabCleanApp.description.splitlines()[0]),
 | 
						|
        "path": (LabPathApp, LabPathApp.description.splitlines()[0]),
 | 
						|
        "paths": (LabPathApp, LabPathApp.description.splitlines()[0]),
 | 
						|
        "workspace": (LabWorkspaceApp, LabWorkspaceApp.description.splitlines()[0]),
 | 
						|
        "workspaces": (LabWorkspaceApp, LabWorkspaceApp.description.splitlines()[0]),
 | 
						|
        "licenses": (LabLicensesApp, LabLicensesApp.description.splitlines()[0]),
 | 
						|
    }
 | 
						|
 | 
						|
    default_url = Unicode("/lab", config=True, help="The default URL to redirect to from `/`")
 | 
						|
 | 
						|
    override_static_url = Unicode(
 | 
						|
        config=True, help=("The override url for static lab assets, typically a CDN.")
 | 
						|
    )
 | 
						|
 | 
						|
    override_theme_url = Unicode(
 | 
						|
        config=True,
 | 
						|
        help=("The override url for static lab theme assets, typically a CDN."),
 | 
						|
    )
 | 
						|
 | 
						|
    app_dir = Unicode(None, config=True, help="The app directory to launch JupyterLab from.")
 | 
						|
 | 
						|
    user_settings_dir = Unicode(
 | 
						|
        get_user_settings_dir(), config=True, help="The directory for user settings."
 | 
						|
    )
 | 
						|
 | 
						|
    workspaces_dir = Unicode(get_workspaces_dir(), config=True, help="The directory for workspaces")
 | 
						|
 | 
						|
    core_mode = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help="""Whether to start the app in core mode. In this mode, JupyterLab
 | 
						|
        will run using the JavaScript assets that are within the installed
 | 
						|
        JupyterLab Python package. In core mode, third party extensions are disabled.
 | 
						|
        The `--dev-mode` flag is an alias to this to be used when the Python package
 | 
						|
        itself is installed in development mode (`pip install -e .`).
 | 
						|
        """,
 | 
						|
    )
 | 
						|
 | 
						|
    dev_mode = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help="""Whether to start the app in dev mode. Uses the unpublished local
 | 
						|
        JavaScript packages in the `dev_mode` folder.  In this case JupyterLab will
 | 
						|
        show a red stripe at the top of the page.  It can only be used if JupyterLab
 | 
						|
        is installed as `pip install -e .`.
 | 
						|
        """,
 | 
						|
    )
 | 
						|
 | 
						|
    extensions_in_dev_mode = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help="""Whether to load prebuilt extensions in dev mode. This may be
 | 
						|
        useful to run and test prebuilt extensions in development installs of
 | 
						|
        JupyterLab. APIs in a JupyterLab development install may be
 | 
						|
        incompatible with published packages, so prebuilt extensions compiled
 | 
						|
        against published packages may not work correctly.""",
 | 
						|
    )
 | 
						|
 | 
						|
    extension_manager = Unicode(
 | 
						|
        "pypi",
 | 
						|
        config=True,
 | 
						|
        help="""The extension manager factory to use. The default options are:
 | 
						|
        "readonly" for a manager without installation capability or "pypi" for
 | 
						|
        a manager using PyPi.org and pip to install extensions.""",
 | 
						|
    )
 | 
						|
 | 
						|
    watch = Bool(False, config=True, help="Whether to serve the app in watch mode")
 | 
						|
 | 
						|
    skip_dev_build = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help="Whether to skip the initial install and JS build of the app in dev mode",
 | 
						|
    )
 | 
						|
 | 
						|
    splice_source = Bool(False, config=True, help="Splice source packages into app directory.")
 | 
						|
 | 
						|
    expose_app_in_browser = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help="Whether to expose the global app instance to browser via window.jupyterapp",
 | 
						|
    )
 | 
						|
 | 
						|
    custom_css = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help="""Whether custom CSS is loaded on the page.
 | 
						|
    Defaults to False.
 | 
						|
    """,
 | 
						|
    )
 | 
						|
 | 
						|
    collaborative = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help="""To enable real-time collaboration, you must install the extension `jupyter_collaboration`.
 | 
						|
        You can install it using pip for example:
 | 
						|
 | 
						|
            python -m pip install jupyter_collaboration
 | 
						|
 | 
						|
        This flag is now deprecated and will be removed in JupyterLab v5.""",
 | 
						|
    )
 | 
						|
 | 
						|
    news_url = Unicode(
 | 
						|
        "https://jupyterlab.github.io/assets/feed.xml",
 | 
						|
        allow_none=True,
 | 
						|
        help="""URL that serves news Atom feed; by default the JupyterLab organization announcements will be fetched. Set to None to turn off fetching announcements.""",
 | 
						|
        config=True,
 | 
						|
    )
 | 
						|
 | 
						|
    lock_all_plugins = Bool(
 | 
						|
        False,
 | 
						|
        config=True,
 | 
						|
        help="Whether all plugins are locked (cannot be enabled/disabled from the UI)",
 | 
						|
    )
 | 
						|
 | 
						|
    check_for_updates_class = Type(
 | 
						|
        default_value=CheckForUpdate,
 | 
						|
        klass=CheckForUpdateABC,
 | 
						|
        config=True,
 | 
						|
        help="""A callable class that receives the current version at instantiation and calling it must return asynchronously a string indicating which version is available and how to install or None if no update is available. The string supports Markdown format.""",
 | 
						|
    )
 | 
						|
 | 
						|
    @default("app_dir")
 | 
						|
    def _default_app_dir(self):
 | 
						|
        app_dir = get_app_dir()
 | 
						|
        if self.core_mode:
 | 
						|
            app_dir = HERE
 | 
						|
        elif self.dev_mode:
 | 
						|
            app_dir = DEV_DIR
 | 
						|
        return app_dir
 | 
						|
 | 
						|
    @default("app_settings_dir")
 | 
						|
    def _default_app_settings_dir(self):
 | 
						|
        return pjoin(self.app_dir, "settings")
 | 
						|
 | 
						|
    @default("app_version")
 | 
						|
    def _default_app_version(self):
 | 
						|
        return app_version
 | 
						|
 | 
						|
    @default("cache_files")
 | 
						|
    def _default_cache_files(self):
 | 
						|
        return False
 | 
						|
 | 
						|
    @default("schemas_dir")
 | 
						|
    def _default_schemas_dir(self):
 | 
						|
        return pjoin(self.app_dir, "schemas")
 | 
						|
 | 
						|
    @default("templates_dir")
 | 
						|
    def _default_templates_dir(self):
 | 
						|
        return pjoin(self.app_dir, "static")
 | 
						|
 | 
						|
    @default("themes_dir")
 | 
						|
    def _default_themes_dir(self):
 | 
						|
        if self.override_theme_url:
 | 
						|
            return ""
 | 
						|
        return pjoin(self.app_dir, "themes")
 | 
						|
 | 
						|
    @default("static_dir")
 | 
						|
    def _default_static_dir(self):
 | 
						|
        return pjoin(self.app_dir, "static")
 | 
						|
 | 
						|
    @default("static_url_prefix")
 | 
						|
    def _default_static_url_prefix(self):
 | 
						|
        if self.override_static_url:
 | 
						|
            return self.override_static_url
 | 
						|
        else:
 | 
						|
            static_url = f"/static/{self.name}/"
 | 
						|
            return ujoin(self.serverapp.base_url, static_url)
 | 
						|
 | 
						|
    @default("theme_url")
 | 
						|
    def _default_theme_url(self):
 | 
						|
        if self.override_theme_url:
 | 
						|
            return self.override_theme_url
 | 
						|
        return ""
 | 
						|
 | 
						|
    def initialize_templates(self):
 | 
						|
        # Determine which model to run JupyterLab
 | 
						|
        if self.core_mode or self.app_dir.startswith(HERE + os.sep):
 | 
						|
            self.core_mode = True
 | 
						|
            self.log.info("Running JupyterLab in core mode")
 | 
						|
 | 
						|
        if self.dev_mode or self.app_dir.startswith(DEV_DIR + os.sep):
 | 
						|
            self.dev_mode = True
 | 
						|
            self.log.info("Running JupyterLab in dev mode")
 | 
						|
 | 
						|
        if self.watch and self.core_mode:
 | 
						|
            self.log.warning("Cannot watch in core mode, did you mean --dev-mode?")
 | 
						|
            self.watch = False
 | 
						|
 | 
						|
        if self.core_mode and self.dev_mode:
 | 
						|
            self.log.warning("Conflicting modes, choosing dev_mode over core_mode")
 | 
						|
            self.core_mode = False
 | 
						|
 | 
						|
        # Set the paths based on JupyterLab's mode.
 | 
						|
        if self.dev_mode:
 | 
						|
            dev_static_dir = ujoin(DEV_DIR, "static")
 | 
						|
            self.static_paths = [dev_static_dir]
 | 
						|
            self.template_paths = [dev_static_dir]
 | 
						|
            if not self.extensions_in_dev_mode:
 | 
						|
                # Add an exception for @jupyterlab/galata-extension
 | 
						|
                galata_extension = pjoin(HERE, "galata")
 | 
						|
                self.labextensions_path = (
 | 
						|
                    [galata_extension]
 | 
						|
                    if galata_extension in map(os.path.abspath, self.labextensions_path)
 | 
						|
                    else []
 | 
						|
                )
 | 
						|
                self.extra_labextensions_path = (
 | 
						|
                    [galata_extension]
 | 
						|
                    if galata_extension in map(os.path.abspath, self.extra_labextensions_path)
 | 
						|
                    else []
 | 
						|
                )
 | 
						|
        elif self.core_mode:
 | 
						|
            dev_static_dir = ujoin(HERE, "static")
 | 
						|
            self.static_paths = [dev_static_dir]
 | 
						|
            self.template_paths = [dev_static_dir]
 | 
						|
            self.labextensions_path = []
 | 
						|
            self.extra_labextensions_path = []
 | 
						|
        else:
 | 
						|
            self.static_paths = [self.static_dir]
 | 
						|
            self.template_paths = [self.templates_dir]
 | 
						|
 | 
						|
    def _prepare_templates(self):
 | 
						|
        super()._prepare_templates()
 | 
						|
        self.jinja2_env.globals.update(custom_css=self.custom_css)
 | 
						|
 | 
						|
    def initialize_handlers(self):  # noqa
 | 
						|
        handlers = []
 | 
						|
 | 
						|
        # Set config for Jupyterlab
 | 
						|
        page_config = self.serverapp.web_app.settings.setdefault("page_config_data", {})
 | 
						|
        page_config.update(get_static_page_config(logger=self.log, level="all"))
 | 
						|
 | 
						|
        page_config.setdefault("buildAvailable", not self.core_mode and not self.dev_mode)
 | 
						|
        page_config.setdefault("buildCheck", not self.core_mode and not self.dev_mode)
 | 
						|
        page_config["devMode"] = self.dev_mode
 | 
						|
        page_config["token"] = self.serverapp.identity_provider.token
 | 
						|
        page_config["exposeAppInBrowser"] = self.expose_app_in_browser
 | 
						|
        page_config["quitButton"] = self.serverapp.quit_button
 | 
						|
        page_config["allow_hidden_files"] = self.serverapp.contents_manager.allow_hidden
 | 
						|
        if hasattr(self.serverapp.contents_manager, "delete_to_trash"):
 | 
						|
            page_config["delete_to_trash"] = self.serverapp.contents_manager.delete_to_trash
 | 
						|
 | 
						|
        # Client-side code assumes notebookVersion is a JSON-encoded string
 | 
						|
        page_config["notebookVersion"] = json.dumps(jpserver_version_info)
 | 
						|
 | 
						|
        self.log.info(f"JupyterLab extension loaded from {HERE!s}")
 | 
						|
        self.log.info(f"JupyterLab application directory is {self.app_dir!s}")
 | 
						|
 | 
						|
        if self.custom_css:
 | 
						|
            handlers.append(
 | 
						|
                (
 | 
						|
                    r"/custom/(.*)(?<!\.js)$",
 | 
						|
                    self.serverapp.web_app.settings["static_handler_class"],
 | 
						|
                    {
 | 
						|
                        "path": self.serverapp.web_app.settings["static_custom_path"],
 | 
						|
                        "no_cache_paths": ["/"],  # don't cache anything in custom
 | 
						|
                    },
 | 
						|
                )
 | 
						|
            )
 | 
						|
 | 
						|
        app_options = AppOptions(
 | 
						|
            logger=self.log,
 | 
						|
            app_dir=self.app_dir,
 | 
						|
            labextensions_path=self.extra_labextensions_path + self.labextensions_path,
 | 
						|
            splice_source=self.splice_source,
 | 
						|
        )
 | 
						|
        builder = Builder(self.core_mode, app_options=app_options)
 | 
						|
        build_handler = (build_path, BuildHandler, {"builder": builder})
 | 
						|
        handlers.append(build_handler)
 | 
						|
 | 
						|
        errored = False
 | 
						|
 | 
						|
        if self.core_mode:
 | 
						|
            self.log.info(CORE_NOTE.strip())
 | 
						|
            ensure_core(self.log)
 | 
						|
        elif self.dev_mode:
 | 
						|
            if not (self.watch or self.skip_dev_build):
 | 
						|
                ensure_dev(self.log)
 | 
						|
                self.log.info(DEV_NOTE)
 | 
						|
        else:
 | 
						|
            if self.splice_source:
 | 
						|
                ensure_dev(self.log)
 | 
						|
            msgs = ensure_app(self.app_dir)
 | 
						|
            if msgs:
 | 
						|
                [self.log.error(msg) for msg in msgs]
 | 
						|
                handler = (self.app_url, ErrorHandler, {"messages": msgs})
 | 
						|
                handlers.append(handler)
 | 
						|
                errored = True
 | 
						|
 | 
						|
        if self.watch:
 | 
						|
            self.log.info("Starting JupyterLab watch mode...")
 | 
						|
            if self.dev_mode:
 | 
						|
                watch_dev(self.log)
 | 
						|
            else:
 | 
						|
                watch(app_options=app_options)
 | 
						|
                page_config["buildAvailable"] = False
 | 
						|
            self.cache_files = False
 | 
						|
 | 
						|
        if not self.core_mode and not errored:
 | 
						|
            # Add extension management handlers
 | 
						|
            provider = self.extension_manager
 | 
						|
            entry_point = EXT_MANAGERS.get(provider)
 | 
						|
            if entry_point is None:
 | 
						|
                self.log.error(f"Extension Manager: No manager defined for provider '{provider}'.")
 | 
						|
                raise NotImplementedError()
 | 
						|
            else:
 | 
						|
                self.log.info(f"Extension Manager is '{provider}'.")
 | 
						|
            manager_factory = entry_point.load()
 | 
						|
            config = self.settings.get("config", {}).get("LabServerApp", {})
 | 
						|
 | 
						|
            blocked_extensions_uris = config.get("blocked_extensions_uris", "")
 | 
						|
            allowed_extensions_uris = config.get("allowed_extensions_uris", "")
 | 
						|
 | 
						|
            if (blocked_extensions_uris) and (allowed_extensions_uris):
 | 
						|
                self.log.error(
 | 
						|
                    "Simultaneous LabServerApp.blocked_extensions_uris and LabServerApp.allowed_extensions_uris is not supported. Please define only one of those."
 | 
						|
                )
 | 
						|
                import sys
 | 
						|
 | 
						|
                sys.exit(-1)
 | 
						|
 | 
						|
            listings_config = {
 | 
						|
                "blocked_extensions_uris": set(
 | 
						|
                    filter(lambda uri: len(uri) > 0, blocked_extensions_uris.split(","))
 | 
						|
                ),
 | 
						|
                "allowed_extensions_uris": set(
 | 
						|
                    filter(lambda uri: len(uri) > 0, allowed_extensions_uris.split(","))
 | 
						|
                ),
 | 
						|
                "listings_refresh_seconds": config.get("listings_refresh_seconds", 60 * 60),
 | 
						|
                "listings_tornado_options": config.get("listings_tornado_options", {}),
 | 
						|
            }
 | 
						|
            if len(listings_config["blocked_extensions_uris"]) or len(
 | 
						|
                listings_config["allowed_extensions_uris"]
 | 
						|
            ):
 | 
						|
                self.log.debug(f"Extension manager will be constrained by {listings_config}")
 | 
						|
 | 
						|
            try:
 | 
						|
                ext_manager = manager_factory(app_options, listings_config, self)
 | 
						|
                metadata = dataclasses.asdict(ext_manager.metadata)
 | 
						|
            except Exception as err:
 | 
						|
                self.log.warning(
 | 
						|
                    f"Failed to instantiate the extension manager {provider}. Falling back to read-only manager.",
 | 
						|
                    exc_info=err,
 | 
						|
                )
 | 
						|
                ext_manager = ReadOnlyExtensionManager(app_options, listings_config, self)
 | 
						|
                metadata = dataclasses.asdict(ext_manager.metadata)
 | 
						|
 | 
						|
            page_config["extensionManager"] = metadata
 | 
						|
            ext_handler = (
 | 
						|
                extensions_handler_path,
 | 
						|
                ExtensionHandler,
 | 
						|
                {"manager": ext_manager},
 | 
						|
            )
 | 
						|
            handlers.append(ext_handler)
 | 
						|
 | 
						|
            # Add plugin manager handlers
 | 
						|
            lock_rules = frozenset(
 | 
						|
                {rule for rule, value in page_config.get("lockedExtensions", {}).items() if value}
 | 
						|
            )
 | 
						|
            handlers.append(
 | 
						|
                (
 | 
						|
                    plugins_handler_path,
 | 
						|
                    PluginHandler,
 | 
						|
                    {
 | 
						|
                        "manager": PluginManager(
 | 
						|
                            app_options=app_options,
 | 
						|
                            ext_options={
 | 
						|
                                "lock_rules": lock_rules,
 | 
						|
                                "all_locked": self.lock_all_plugins,
 | 
						|
                            },
 | 
						|
                            parent=self,
 | 
						|
                        )
 | 
						|
                    },
 | 
						|
                )
 | 
						|
            )
 | 
						|
 | 
						|
            # Add announcement handlers
 | 
						|
            page_config["news"] = {"disabled": self.news_url is None}
 | 
						|
            handlers.extend(
 | 
						|
                [
 | 
						|
                    (
 | 
						|
                        check_update_handler_path,
 | 
						|
                        CheckForUpdateHandler,
 | 
						|
                        {
 | 
						|
                            "update_checker": self.check_for_updates_class(__version__),
 | 
						|
                        },
 | 
						|
                    ),
 | 
						|
                    (
 | 
						|
                        news_handler_path,
 | 
						|
                        NewsHandler,
 | 
						|
                        {
 | 
						|
                            "news_url": self.news_url,
 | 
						|
                        },
 | 
						|
                    ),
 | 
						|
                ]
 | 
						|
            )
 | 
						|
 | 
						|
        # If running under JupyterHub, add more metadata.
 | 
						|
        if "hub_prefix" in self.serverapp.tornado_settings:
 | 
						|
            tornado_settings = self.serverapp.tornado_settings
 | 
						|
            hub_prefix = tornado_settings["hub_prefix"]
 | 
						|
            page_config["hubPrefix"] = hub_prefix
 | 
						|
            page_config["hubHost"] = tornado_settings["hub_host"]
 | 
						|
            page_config["hubUser"] = tornado_settings["user"]
 | 
						|
            page_config["shareUrl"] = ujoin(hub_prefix, "user-redirect")
 | 
						|
            # Assume the server_name property indicates running JupyterHub 1.0.
 | 
						|
            if hasattr(self.serverapp, "server_name"):
 | 
						|
                page_config["hubServerName"] = self.serverapp.server_name
 | 
						|
            # avoid setting API token in page config
 | 
						|
            # $JUPYTERHUB_API_TOKEN identifies the server, not the client
 | 
						|
            # but at least make sure we don't use the token
 | 
						|
            # if the serverapp set one
 | 
						|
            page_config["token"] = ""
 | 
						|
 | 
						|
        # Update Jupyter Server's webapp settings with jupyterlab settings.
 | 
						|
        self.serverapp.web_app.settings["page_config_data"] = page_config
 | 
						|
 | 
						|
        # Extend Server handlers with jupyterlab handlers.
 | 
						|
        self.handlers.extend(handlers)
 | 
						|
        super().initialize_handlers()
 | 
						|
 | 
						|
    def initialize(self, argv=None):
 | 
						|
        """Subclass because the ExtensionApp.initialize() method does not take arguments"""
 | 
						|
        super().initialize()
 | 
						|
        if self.collaborative:
 | 
						|
            try:
 | 
						|
                import jupyter_collaboration  # noqa
 | 
						|
            except ImportError:
 | 
						|
                self.log.critical(
 | 
						|
                    """\
 | 
						|
Jupyter Lab cannot start, because `jupyter_collaboration` was configured but cannot be `import`ed.
 | 
						|
 | 
						|
To fix this, either:
 | 
						|
 | 
						|
1) install the extension `jupyter-collaboration`; for example: `python -m pip install jupyter-collaboration`
 | 
						|
 | 
						|
2) disable collaboration; for example, remove the `--collaborative` flag from the commandline.  To see more ways to adjust the collaborative behavior, see https://jupyterlab-realtime-collaboration.readthedocs.io/en/latest/configuration.html .
 | 
						|
"""
 | 
						|
                )
 | 
						|
                sys.exit(1)
 | 
						|
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# Main entry point
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
main = launch_new_instance = LabApp.launch_instance
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    main()
 |