# Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. import configparser import json import re import shutil import subprocess import sys from typing import Optional try: import tomllib except ImportError: import tomli as tomllib try: from importlib.resources import files except ImportError: from importlib_resources import files from pathlib import Path try: import copier except ModuleNotFoundError: msg = "Please install copier; you can use `pip install jupyterlab[upgrade-extension]`" raise RuntimeError(msg) from None # List of files recommended to be overridden RECOMMENDED_TO_OVERRIDE = [ ".github/workflows/binder-on-pr.yml", ".github/workflows/build.yml", ".github/workflows/check-release.yml", ".github/workflows/enforce-label.yml", ".github/workflows/prep-release.yml", ".github/workflows/publish-release.yml", ".github/workflows/update-integration-tests.yml", "binder/postBuild", ".eslintignore", ".eslintrc.js", ".gitignore", ".prettierignore", ".prettierrc", ".stylelintrc", "RELEASE.md", "babel.config.js", "conftest.py", "jest.config.js", "pyproject.toml", "setup.py", "tsconfig.json", "tsconfig.test.json", "ui-tests/README.md", "ui-tests/jupyter_server_test_config.py", "ui-tests/package.json", "ui-tests/playwright.config.js", ] JUPYTER_SERVER_REQUIREMENT = re.compile("^jupyter_server([^\\w]|$)") def update_extension( # noqa target: str, vcs_ref: Optional[str] = None, interactive: bool = True ) -> None: """Update an extension to the current JupyterLab target: str Path to the extension directory containing the extension vcs_ref: str [default: None] Template vcs_ref to checkout interactive: bool [default: true] Whether to ask before overwriting content """ # Input is a directory with a package.json or the current directory # Use the extension template as the source # Pull in the relevant config # Pull in the Python parts if possible # Pull in the scripts if possible target = Path(target).resolve() package_file = target / "package.json" pyproject_file = target / "pyproject.toml" setup_file = target / "setup.py" if not package_file.exists(): msg = f"No package.json exists in {target!s}" raise RuntimeError(msg) # Infer the options from the current directory with open(package_file) as fid: data = json.load(fid) python_name = None if pyproject_file.exists(): pyproject = tomllib.loads(pyproject_file.read_text()) python_name = pyproject.get("project", {}).get("name") if python_name is None: if setup_file.exists(): python_name = ( subprocess.check_output( [sys.executable, "setup.py", "--name"], # noqa: S603 cwd=target, ) .decode("utf8") .strip() ) else: python_name = data["name"] if "@" in python_name: python_name = python_name[1:] # Clean up the name to be valid package module name python_name = python_name.replace("/", "_").replace("-", "_") output_dir = target / "_temp_extension" if output_dir.exists(): shutil.rmtree(output_dir) # Build up the template answers and run the template engine author = data.get("author", "") author_email = "" if isinstance(author, dict): author_name = author.get("name", "") author_email = author.get("email", author_email) else: author_name = author kind = "frontend" if (target / "jupyter-config").exists(): kind = "server" elif data.get("jupyterlab", {}).get("themePath", ""): kind = "theme" has_test = ( (target / "conftest.py").exists() or (target / "jest.config.js").exists() or (target / "ui-tests").exists() ) extra_context = { "kind": kind, "author_name": author_name, "author_email": author_email, "labextension_name": data["name"], "python_name": python_name, "project_short_description": data.get("description", ""), "has_settings": bool(data.get("jupyterlab", {}).get("schemaDir", "")), "has_binder": bool((target / "binder").exists()), "test": bool(has_test), "repository": data.get("repository", {}).get("url", "= ("8", "0", "0"): msg += " --UNSAFE" print(msg) else: update_extension(args.path, args.vcs_ref, args.no_input is False)