瀏覽代碼

Merge pull request #2168 from blink1073/commands-cleanup

Add ability to uninstall core extensions
Afshin Darian 8 年之前
父節點
當前提交
e9c9f16f01
共有 5 個文件被更改,包括 113 次插入41 次删除
  1. 61 24
      jupyterlab/commands.py
  2. 11 1
      jupyterlab/tests/test_jupyterlab.py
  3. 3 10
      setup.py
  4. 22 3
      setupbase.py
  5. 16 3
      tutorial/extensions_user.md

+ 61 - 24
jupyterlab/commands.py

@@ -71,6 +71,10 @@ def install_extension(extension, app_dir=None):
     data = _read_package(pjoin(target, name))
     _validate_package(data, extension)
 
+    _ensure_package(app_dir)
+    staging = pjoin(app_dir, 'staging')
+    run(['npm', 'install', pjoin(target, name)], cwd=staging)
+
 
 def link_package(path, app_dir=None):
     """Link a package against the JupyterLab build.
@@ -95,11 +99,10 @@ def link_package(path, app_dir=None):
     if is_extension:
         install_extension(path, app_dir)
 
-    linked = _get_linked_packages(app_dir)
-    linked[data['name']] = path
-    target = pjoin(app_dir, 'settings', 'linked_packages.json')
-    with open(target, 'w') as fid:
-        json.dump(linked, fid)
+    config = _get_build_config(app_dir)
+    config.setdefault('linked_packages', dict())
+    config['linked_packages'][data['name']] = path
+    _write_build_config(config, app_dir)
 
 
 def unlink_package(package, app_dir=None):
@@ -110,7 +113,9 @@ def unlink_package(package, app_dir=None):
     app_dir = get_app_dir(app_dir)
     if app_dir == here:
         raise ValueError('Cannot link packages in core app')
-    linked = _get_linked_packages(app_dir)
+
+    config = _get_build_config(app_dir)
+    linked = config.setdefault('linked_packages', dict())
     for (key, value) in linked.items():
         if value == package or key == package:
             name = key
@@ -121,9 +126,8 @@ def unlink_package(package, app_dir=None):
         return False
 
     del linked[name]
-    target = pjoin(app_dir, 'settings', 'linked_packages.json')
-    with open(target, 'w') as fid:
-        json.dump(linked, fid)
+    config['linked_packages'] = linked
+    _write_build_config(config, app_dir)
 
     extensions = _get_extensions(app_dir)
     if name in extensions:
@@ -132,12 +136,45 @@ def unlink_package(package, app_dir=None):
     return True
 
 
+def _get_build_config(app_dir):
+    """Get the build config data for the given app dir
+    """
+    target = pjoin(app_dir, 'settings', 'build_config.json')
+    if not os.path.exists(target):
+        return {}
+    else:
+        with open(target) as fid:
+            return json.load(fid)
+
+
+def _write_build_config(config, app_dir):
+    """Write the build config to the app dir.
+    """
+    _ensure_package(app_dir)
+    target = pjoin(app_dir, 'settings', 'build_config.json')
+    with open(target, 'w') as fid:
+        json.dump(config, fid, indent=4)
+
+
 def uninstall_extension(name, app_dir=None):
     """Uninstall an extension by name.
     """
     app_dir = get_app_dir(app_dir)
     if app_dir == here:
         raise ValueError('Cannot install packages in core app')
+    # Allow for uninstalled core extensions here.
+    with open(pjoin(here, 'package.json')) as fid:
+        data = json.load(fid)
+        if name in data['jupyterlab']['extensions']:
+            print('Uninstalling core extension %s' % name)
+            config = _get_build_config(app_dir)
+            uninstalled = config.get('uninstalled_core_extensions', [])
+            if name not in uninstalled:
+                uninstalled.append(name)
+                config['uninstalled_core_extensions'] = uninstalled
+                _write_build_config(config, app_dir)
+            return True
+
     for (extname, path) in _get_extensions(app_dir).items():
         if extname == name:
             print('Uninstalling %s from %s' % (name, os.path.dirname(path)))
@@ -160,7 +197,7 @@ def clean(app_dir=None):
     app_dir = get_app_dir(app_dir)
     if app_dir == here:
         raise ValueError('Cannot clean the core app')
-    for name in ['static', 'build']:
+    for name in ['static', 'staging']:
         target = pjoin(app_dir, name)
         if osp.exists(target):
             shutil.rmtree(target)
@@ -230,6 +267,10 @@ def _ensure_package(app_dir, name='JupyterLab', version=None, publicPath=None):
         data['dependencies'][key] = value
         data['jupyterlab']['extensions'].append(key)
 
+    config = _get_build_config(app_dir)
+    for item in config.get('uninstalled_core_extensions', []):
+        data['jupyterlab']['extensions'].remove(item)
+
     data['jupyterlab']['name'] = name
     if version:
         data['jupyterlab']['version'] = version
@@ -285,6 +326,14 @@ def _get_extensions(app_dir):
     return extensions
 
 
+def _get_linked_packages(app_dir=None):
+    """Get the linked packages metadata.
+    """
+    app_dir = get_app_dir(app_dir)
+    config = _get_build_config(app_dir)
+    return config.get('linked_packages', dict())
+
+
 def _read_package(target):
     """Read the package data in a given target tarball.
     """
@@ -293,21 +342,9 @@ def _read_package(target):
     return json.loads(f.read().decode('utf8'))
 
 
-def _get_linked_packages(app_dir=None):
-    """Get the linked packages in the app dir.
-    """
-    app_dir = get_app_dir(app_dir)
-    extensions = dict()
-
-    link_file = pjoin(app_dir, 'settings', 'linked_packages.json')
-    if not os.path.exists(link_file):
-        return extensions
-
-    with open(link_file) as fid:
-        return json.load(fid)
-
-
 def _normalize_path(extension):
+    """Normalize a given extension if it is a path.
+    """
     extension = osp.expanduser(extension)
     if osp.exists(extension):
         extension = osp.abspath(extension)

+ 11 - 1
jupyterlab/tests/test_jupyterlab.py

@@ -4,6 +4,7 @@
 # Copyright (c) Jupyter Development Team.
 # Distributed under the terms of the Modified BSD License.
 import glob
+import json
 import os
 import sys
 from os.path import join as pjoin
@@ -26,7 +27,7 @@ from jupyterlab.extension import (
 from jupyterlab.commands import (
     install_extension, uninstall_extension, list_extensions,
     build, link_package, unlink_package, get_app_dir,
-    _get_linked_packages
+    _get_linked_packages, _ensure_package
 )
 
 here = os.path.dirname(os.path.abspath(__file__))
@@ -118,6 +119,15 @@ class TestExtension(TestCase):
         assert not glob.glob(path)
         assert '@jupyterlab/python-tests' not in list_extensions()
 
+    def test_uninstall_core_extension(self):
+        uninstall_extension('@jupyterlab/console-extension')
+        app_dir = get_app_dir()
+        _ensure_package(app_dir)
+        with open(pjoin(app_dir, 'staging', 'package.json')) as fid:
+            data = json.load(fid)
+        extensions = data['jupyterlab']['extensions']
+        assert '@jupyterlab/console-extension' not in extensions
+
     def test_link_package(self):
         link_package(self.source_dir)
         linked = _get_linked_packages().keys()

+ 3 - 10
setup.py

@@ -6,9 +6,6 @@
 
 from __future__ import print_function
 
-# the name of the project
-name = 'jupyterlab'
-
 #-----------------------------------------------------------------------------
 # Minimal Python version sanity check
 #-----------------------------------------------------------------------------
@@ -28,7 +25,6 @@ PY3 = (sys.version_info[0] >= 3)
 #-----------------------------------------------------------------------------
 
 from distutils import log
-import io
 import json
 import os
 from glob import glob
@@ -45,7 +41,9 @@ from setupbase import (
     find_packages,
     find_package_data,
     js_prerelease,
-    CheckAssets
+    CheckAssets,
+    version_ns,
+    name
 )
 
 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
@@ -60,11 +58,6 @@ DESCRIPTION = 'An alpha preview of the JupyterLab notebook server extension.'
 LONG_DESCRIPTION = 'This is an alpha preview of JupyterLab. It is not ready for general usage yet. Development happens on https://github.com/jupyter/jupyterlab, with chat on https://gitter.im/jupyter/jupyterlab.'
 
 
-version_ns = {}
-with io.open(pjoin(here, name, '_version.py'), encoding="utf8") as f:
-    exec(f.read(), {}, version_ns)
-
-
 setup_args = dict(
     name             = name,
     description      = DESCRIPTION,

+ 22 - 3
setupbase.py

@@ -9,9 +9,12 @@ This includes:
 # Copyright (c) Jupyter Development Team.
 # Distributed under the terms of the Modified BSD License.
 
+import io
+import json
 import os
 import pipes
 import sys
+from os.path import join as pjoin
 
 from distutils import log
 from distutils.cmd import Command
@@ -25,9 +28,16 @@ else:
     def list2cmdline(cmd_list):
         return ' '.join(map(pipes.quote, cmd_list))
 
+# the name of the project
+name = 'jupyterlab'
+
 
 here = os.path.dirname(os.path.abspath(__file__))
-is_repo = os.path.exists(os.path.join(here, '.git'))
+is_repo = os.path.exists(pjoin(here, '.git'))
+
+version_ns = {}
+with io.open(pjoin(here, name, '_version.py'), encoding="utf8") as f:
+    exec(f.read(), {}, version_ns)
 
 
 def run(cmd, *args, **kwargs):
@@ -109,8 +119,8 @@ class CheckAssets(Command):
 
     # Representative files that should exist after a successful build
     targets = [
-        os.path.join(here, 'jupyterlab', 'build', 'release_data.json'),
-        os.path.join(here, 'jupyterlab', 'build', 'main.bundle.js'),
+        pjoin(here, 'jupyterlab', 'build', 'release_data.json'),
+        pjoin(here, 'jupyterlab', 'build', 'main.bundle.js'),
     ]
 
     def initialize_options(self):
@@ -125,6 +135,15 @@ class CheckAssets(Command):
                 msg = 'Missing file: %s' % t
                 raise ValueError(msg)
 
+        target = pjoin(here, 'jupyterlab', 'build', 'release_data.json')
+        with open(target) as fid:
+            data = json.load(fid)
+
+        if data['version'] != version_ns['__version__']:
+            msg = 'Release assets version mismatch, please run npm publish'
+            raise ValueError(msg)
+
+
         # update package data in case this created new files
         update_package_data(self.distribution)
 

+ 16 - 3
tutorial/extensions_user.md

@@ -65,12 +65,25 @@ variable.  If not specified, it will default to
 site-specific directory prefix of the current Python environment.  You can
 query the current application path using `jupyter lab path`.
 
-The `settings` directory contains `page_config.json` and `linked_packages.json`
+The `settings` directory contains `page_config.json` and `build_config.json`
 files.
 The `page_config.json` data is used to provide config data to the application
 environment.  For example, the `ignoredPlugins` data is used to ignore registered plugins by the name of the token they provide.
-The `linked_packages.json` file is used to track the folders that have been
-added using `jupyter labextension link <folder>`.
+The `build_config.json` file is used to track the folders that have been
+added using `jupyter labextension link <folder>`, as well as core extensions
+that have been explicitly uninstalled.  e.g.
+
+```bash
+$ cat settings/build_config.json
+{
+    "uninstalled_core_extensions": [
+        "@jupyterlab/markdownwidget-extension"
+    ],
+    "linked_packages": {
+        "@jupyterlab/python-tests": "/path/to/my/extension"
+    }
+}
+```
 
 The other folders in the app directory are: `extensions`, `static`, and
 `staging`.  The `extensions` folder has the packed tarballs for each of the