| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- # Copyright (c) Jupyter Development Team.
- # Distributed under the terms of the Modified BSD License.
- """A workspace management CLI"""
- from __future__ import annotations
- import json
- import sys
- import warnings
- from pathlib import Path
- from typing import Any
- from jupyter_core.application import JupyterApp
- from traitlets import Bool, Unicode
- from ._version import __version__
- from .config import LabConfig
- from .workspaces_handler import WorkspacesManager
- # Default workspace ID
- # Needs to match PageConfig.defaultWorkspace define in packages/coreutils/src/pageconfig.ts
- DEFAULT_WORKSPACE = "default"
- class WorkspaceListApp(JupyterApp, LabConfig):
- """An app to list workspaces."""
- version = __version__
- description = """
- Print all the workspaces available
- If '--json' flag is passed in, a single 'json' object is printed.
- If '--jsonlines' flag is passed in, 'json' object of each workspace separated by a new line is printed.
- If nothing is passed in, workspace ids list is printed.
- """
- flags = dict(
- jsonlines=(
- {"WorkspaceListApp": {"jsonlines": True}},
- ("Produce machine-readable JSON Lines output."),
- ),
- json=(
- {"WorkspaceListApp": {"json": True}},
- ("Produce machine-readable JSON object output."),
- ),
- )
- jsonlines = Bool(
- False,
- config=True,
- help=(
- "If True, the output will be a newline-delimited JSON (see https://jsonlines.org/) of objects, "
- "one per JupyterLab workspace, each with the details of the relevant workspace"
- ),
- )
- json = Bool(
- False,
- config=True,
- help=(
- "If True, each line of output will be a JSON object with the "
- "details of the workspace."
- ),
- )
- def initialize(self, *args: Any, **kwargs: Any) -> None:
- """Initialize the app."""
- super().initialize(*args, **kwargs)
- self.manager = WorkspacesManager(self.workspaces_dir)
- def start(self) -> None:
- """Start the app."""
- workspaces = self.manager.list_workspaces()
- if self.jsonlines:
- for workspace in workspaces:
- print(json.dumps(workspace))
- elif self.json:
- print(json.dumps(workspaces))
- else:
- for workspace in workspaces:
- print(workspace["metadata"]["id"])
- class WorkspaceExportApp(JupyterApp, LabConfig):
- """A workspace export app."""
- version = __version__
- description = """
- Export a JupyterLab workspace
- If no arguments are passed in, this command will export the default
- workspace.
- If a workspace name is passed in, this command will export that workspace.
- If no workspace is found, this command will export an empty workspace.
- """
- def initialize(self, *args: Any, **kwargs: Any) -> None:
- """Initialize the app."""
- super().initialize(*args, **kwargs)
- self.manager = WorkspacesManager(self.workspaces_dir)
- def start(self) -> None:
- """Start the app."""
- if len(self.extra_args) > 1: # pragma: no cover
- warnings.warn("Too many arguments were provided for workspace export.")
- self.exit(1)
- raw = DEFAULT_WORKSPACE if not self.extra_args else self.extra_args[0]
- try:
- workspace = self.manager.load(raw)
- print(json.dumps(workspace))
- except Exception: # pragma: no cover
- self.log.error(json.dumps(dict(data=dict(), metadata=dict(id=raw))))
- class WorkspaceImportApp(JupyterApp, LabConfig):
- """A workspace import app."""
- version = __version__
- description = """
- Import a JupyterLab workspace
- This command will import a workspace from a JSON file. The format of the
- file must be the same as what the export functionality emits.
- """
- workspace_name = Unicode(
- None,
- config=True,
- allow_none=True,
- help="""
- Workspace name. If given, the workspace ID in the imported
- file will be replaced with a new ID pointing to this
- workspace name.
- """,
- )
- aliases = {"name": "WorkspaceImportApp.workspace_name"}
- def initialize(self, *args: Any, **kwargs: Any) -> None:
- """Initialize the app."""
- super().initialize(*args, **kwargs)
- self.manager = WorkspacesManager(self.workspaces_dir)
- def start(self) -> None:
- """Start the app."""
- if len(self.extra_args) != 1: # pragma: no cover
- self.log.info("One argument is required for workspace import.")
- self.exit(1)
- with self._smart_open() as fid:
- try: # to load, parse, and validate the workspace file.
- workspace = self._validate(fid)
- except Exception as e: # pragma: no cover
- self.log.info("%s is not a valid workspace:\n%s", fid.name, e)
- self.exit(1)
- try:
- workspace_path = self.manager.save(workspace["metadata"]["id"], json.dumps(workspace))
- except Exception as e: # pragma: no cover
- self.log.info("Workspace could not be exported:\n%s", e)
- self.exit(1)
- self.log.info("Saved workspace: %s", workspace_path)
- def _smart_open(self) -> Any:
- file_name = self.extra_args[0]
- if file_name == "-": # pragma: no cover
- return sys.stdin
- file_path = Path(file_name).resolve()
- if not file_path.exists(): # pragma: no cover
- self.log.info("%s does not exist.", file_name)
- self.exit(1)
- return file_path.open(encoding="utf-8")
- def _validate(self, data: Any) -> Any:
- workspace = json.load(data)
- if "data" not in workspace:
- msg = "The `data` field is missing."
- raise Exception(msg)
- # If workspace_name is set in config, inject the
- # name into the workspace metadata.
- if self.workspace_name is not None and self.workspace_name:
- workspace["metadata"] = {"id": self.workspace_name}
- elif "id" not in workspace["metadata"]:
- msg = "The `id` field is missing in `metadata`."
- raise Exception(msg)
- return workspace
|