Quellcode durchsuchen

Finish cleanup and config implementation

Steven Silvester vor 8 Jahren
Ursprung
Commit
7651464bdb
4 geänderte Dateien mit 112 neuen und 72 gelöschten Zeilen
  1. 13 33
      jupyterlab/__init__.py
  2. 7 1
      jupyterlab/labapp.py
  3. 42 21
      jupyterlab/labextensions.py
  4. 50 17
      jupyterlab/tests/test_labextensions.py

+ 13 - 33
jupyterlab/__init__.py

@@ -3,15 +3,18 @@
 # Copyright (c) Jupyter Development Team.
 # Distributed under the terms of the Modified BSD License.
 
-import glob
-import json
 import os
 from tornado import web
 from notebook.base.handlers import IPythonHandler, FileFindHandler
 from jinja2 import FileSystemLoader
 from notebook.utils import url_path_join as ujoin
-from traitlets.config.manager import BaseJSONConfigManager
-from jupyter_core.paths import jupyter_config_path
+from jupyter_core.paths import jupyter_path
+
+from .labapp import LabApp
+from .labextensions import (
+    get_labextension_manifest_data_by_name, get_labextension_config_python,
+    get_labextension_manifest_data_by_folder
+)
 
 try:
     from ._version import __version__
@@ -40,28 +43,6 @@ PREFIX = '/lab'
 EXTENSION_PREFIX = '/labextension'
 
 
-def get_labextension_manifest_data_by_folder(folder):
-    """Get the manifest data for a given lab extension folder
-    """
-    manifest_files = glob.glob(os.path.join(folder, '*.manifest'))
-    manifests = {}
-    for file in manifest_files:
-        with open(file) as fid:
-            manifest = json.load(fid)
-        manifests[manifest['name']] = manifest
-    return manifests
-
-
-def get_labextension_manifest_data_by_name(name):
-    """Get the manifest data for a given lab extension folder
-    """
-    from .labextensions import _labextension_dirs
-    for exts in _labextension_dirs():
-        full_dest = os.path.join(exts, name)
-        if os.path.exists(full_dest):
-            return get_labextension_manifest_data_by_folder(full_dest)
-
-
 class LabHandler(IPythonHandler):
     """Render the Jupyter Lab View."""
 
@@ -106,9 +87,12 @@ class LabHandler(IPythonHandler):
         )
 
         # Gather the lab extension files and entry points.
-        for (name, value) in labextensions.items():
-            if not value['enabled']:
+        # Make sure we only load an extension once for each name.
+        seen = set()
+        for (name, value) in sorted(labextensions.items()):
+            if not value['enabled'] or name in seen:
                 continue
+            seen.add(name)
             data = get_labextension_manifest_data_by_name(name)
             if data is None:
                 self.log.warn('Could not locate extension: ' + name)
@@ -124,8 +108,7 @@ class LabHandler(IPythonHandler):
                         css_files.append('%s/%s/%s' % (
                             EXTENSION_PREFIX, name, fname
                         ))
-            python_module = value['python_module']
-            from .labextensions import get_labextension_config_python
+            python_module = value.get('python_module', None)
             if python_module:
                 try:
                     value = get_labextension_config_python(python_module)
@@ -157,9 +140,6 @@ def _jupyter_server_extension_paths():
 
 
 def load_jupyter_server_extension(nbapp):
-    from jupyter_core.paths import jupyter_path
-    from .labapp import LabApp
-
     if not isinstance(nbapp, LabApp):
         labapp = LabApp()
         labapp.load_config_file()

+ 7 - 1
jupyterlab/labapp.py

@@ -37,7 +37,13 @@ class LabApp(NotebookApp):
         help="The default URL to redirect to from `/`"
     )
 
-    labextensions = Dict({}, config=True, help='foo')
+    labextensions = Dict({}, config=True,
+        help=('Dict of Python modules to load as lab extensions.'
+            'Each entry consists of a required `enabled` key used'
+            'to enable or disable the extension, and an optional'
+            '`python_module` key for the associated python module.'
+            'Extensions are loaded in alphabetical order')
+    )
 
 #-----------------------------------------------------------------------------
 # Main entry point

+ 42 - 21
jupyterlab/labextensions.py

@@ -6,6 +6,8 @@
 
 from __future__ import print_function
 
+import glob
+import json
 import os
 import shutil
 import sys
@@ -25,9 +27,8 @@ from traitlets.utils.importstring import import_item
 
 from tornado.log import LogFormatter
 
-from . import (
-    get_labextension_manifest_data_by_folder, semver
-)
+from .semver import satisfies
+
 
 # Constants for pretty print extension listing function.
 # Window doesn't support coloring in the commandline
@@ -38,6 +39,8 @@ GREEN_ENABLED = '\033[32menabled \033[0m' if os.name != 'nt' else 'enabled '
 RED_DISABLED = '\033[31mdisabled\033[0m' if os.name != 'nt' else 'disabled'
 
 
+CONFIG_NAME = 'jupyter_notebook_config'
+
 #------------------------------------------------------------------------------
 # Public API
 #------------------------------------------------------------------------------
@@ -279,15 +282,12 @@ def _set_labextension_state(name, state,
         The name of the python module associated with the extension.
     """
     user = False if sys_prefix else user
-    config_dir = os.path.join(
-        _get_config_dir(user=user, sys_prefix=sys_prefix))
-    cm = BaseJSONConfigManager(config_dir=config_dir)
+    cfg = _read_config_data(user=user, sys_prefix=sys_prefix)
     if logger:
         logger.info("{} extension {}...".format(
             "Enabling" if state else "Disabling",
             name
         ))
-    cfg = cm.get("jupyter_notebook_config")
     labextensions = (
         cfg.setdefault("LabApp", {})
         .setdefault("labextensions", {})
@@ -312,10 +312,7 @@ def _set_labextension_state(name, state,
         python_module=python_module
     )
 
-    if logger:
-        logger.info(u"- Writing config: {}".format(config_dir))
-
-    cm.update("jupyter_notebook_config", cfg)
+    _write_config_data(cfg, user=user, sys_prefix=sys_prefix, logger=logger)
 
     if new_enabled:
         full_dest = find_labextension(name)
@@ -350,7 +347,8 @@ def _set_labextension_state_python(state, module, user, sys_prefix,
     return [_set_labextension_state(name=labext["name"],
                                    state=state,
                                    user=user, sys_prefix=sys_prefix,
-                                   logger=logger)
+                                   logger=logger,
+                                   python_module=module)
             for labext in labexts]
 
 
@@ -511,7 +509,7 @@ def validate_labextension_folder(name, full_dest, logger=None):
             for dep in deps:
                 if dep.startswith('jupyterlab@'):
                     dep = dep.split('/')[0].split('@')[-1]
-                    if not semver.satisfies(__version__, dep, False):
+                    if not satisfies(__version__, dep, False):
                         version_compatible.append('Expects JupyterLab version %s from packaged module %s'%(dep, mod))
                         break
 
@@ -546,6 +544,27 @@ def validate_labextension_folder(name, full_dest, logger=None):
     return warnings
 
 
+def get_labextension_manifest_data_by_folder(folder):
+    """Get the manifest data for a given lab extension folder
+    """
+    manifest_files = glob.glob(os.path.join(folder, '*.manifest'))
+    manifests = {}
+    for file in manifest_files:
+        with open(file) as fid:
+            manifest = json.load(fid)
+        manifests[manifest['name']] = manifest
+    return manifests
+
+
+def get_labextension_manifest_data_by_name(name):
+    """Get the manifest data for a given lab extension folder
+    """
+    for exts in _labextension_dirs():
+        full_dest = os.path.join(exts, name)
+        if os.path.exists(full_dest):
+            return get_labextension_manifest_data_by_folder(full_dest)
+
+
 def get_labextension_config_python(module):
     """Get the labextension configuration data associated  with a Python module. 
 
@@ -872,13 +891,11 @@ class ListLabExtensionsApp(BaseLabExtensionApp):
     
     def list_labextensions(self):
         """List all the labextensions"""
-        config_dirs = [os.path.join(p, 'labconfig') for p in jupyter_config_path()]
-        
         print("Known labextensions:")
-        
-        for config_dir in config_dirs:
+
+        for config_dir in jupyter_config_path():
             cm = BaseJSONConfigManager(parent=self, config_dir=config_dir)
-            data = cm.get("jupyterlab_config")
+            data = cm.get("jupyter_notebook_config")
             labextensions = (
                 data.setdefault("LabApp", {})
                 .setdefault("labextensions", {})
@@ -1110,10 +1127,10 @@ def _read_config_data(user=False, sys_prefix=False):
     """
     config_dir = _get_config_dir(user=user, sys_prefix=sys_prefix)
     config_man = BaseJSONConfigManager(config_dir=config_dir)
-    return config_man.get('jupyterlab_config')
+    return config_man.get('jupyter_notebook_config')
 
 
-def _write_config_data(data, user=False, sys_prefix=False):
+def _write_config_data(data, user=False, sys_prefix=False, logger=None):
     """Update the config for the current context
 
     Parameters
@@ -1124,10 +1141,14 @@ def _write_config_data(data, user=False, sys_prefix=False):
         Get the user's .jupyter config directory
     sys_prefix : bool [default: False]
         Get sys.prefix, i.e. ~/.envs/my-env/etc/jupyter
+    logger: logger instance
+        The logger instance.
     """
     config_dir = _get_config_dir(user=user, sys_prefix=sys_prefix)
+    if logger:
+        logger.info(u"- Writing config: {}".format(config_dir))
     config_man = BaseJSONConfigManager(config_dir=config_dir)
-    config_man.update('jupyterlab_config', data)
+    config_man.update('jupyter_notebook_config', data)
 
 
 if __name__ == '__main__':

+ 50 - 17
jupyterlab/tests/test_labextensions.py

@@ -27,7 +27,9 @@ from jupyterlab.labextensions import (install_labextension, check_labextension,
     install_labextension_python, uninstall_labextension_python,
     enable_labextension_python, disable_labextension_python, _get_config_dir,
     find_labextension, validate_labextension_folder,
-    get_labextension_config_python
+    get_labextension_config_python,
+    get_labextension_manifest_data_by_name,
+    get_labextension_manifest_data_by_folder
 )
 
 from traitlets.config.manager import BaseJSONConfigManager
@@ -226,21 +228,22 @@ class TestInstallLabExtension(TestCase):
             touch(src)
             install_labextension(src, self.name, user=True)
             enable_labextension(self.name)
-        
-        config_dir = os.path.join(_get_config_dir(user=True), 'labconfig')
+
+        config_dir = os.path.join(_get_config_dir(user=True))
         cm = BaseJSONConfigManager(config_dir=config_dir)
-        enabled = cm.get('jupyterlab_config').get('LabApp', {}).get('labextensions', {}).get(self.name, False)
-        assert enabled
-    
+        config = cm.get('jupyter_notebook_config').get('LabApp', {}).get('labextensions', {}).get(self.name, {})
+        assert config['enabled'] == True
+        assert 'python_module' not in config
+
     def test_labextension_disable(self):
         self.test_labextension_enable()
         disable_labextension(self.name)
-        
-        config_dir = os.path.join(_get_config_dir(user=True), 'labconfig')
+
+        config_dir = os.path.join(_get_config_dir(user=True))
         cm = BaseJSONConfigManager(config_dir=config_dir)
-        enabled = cm.get('jupyterlab_config').get('LabApp', {}).get('labextensions', {}).get(self.name, False)
-        assert not enabled
-        
+        config = cm.get('jupyter_notebook_config').get('LabApp', {}).get('labextensions', {}).get(self.name, {})
+        assert not config['enabled']
+        assert 'python_module' not in config
 
     def _mock_extension_spec_meta(self):
         return {
@@ -294,10 +297,11 @@ class TestInstallLabExtension(TestCase):
         install_labextension_python('mockextension', user=True)
         enable_labextension_python('mockextension')
         
-        config_dir = os.path.join(_get_config_dir(user=True), 'labconfig')
+        config_dir = os.path.join(_get_config_dir(user=True))
         cm = BaseJSONConfigManager(config_dir=config_dir)
-        enabled = cm.get('jupyterlab_config').get('LabApp', {}).get('labextensions', {}).get('mockextension', False)
-        assert enabled
+        config = cm.get('jupyter_notebook_config').get('LabApp', {}).get('labextensions', {}).get('mockextension', False)
+        assert config['enabled'] == True
+        assert config['python_module'] == 'mockextension'
         
     def test_labextensionpy_disable(self):
         self._inject_mock_extension()
@@ -305,10 +309,10 @@ class TestInstallLabExtension(TestCase):
         enable_labextension_python('mockextension')
         disable_labextension_python('mockextension', user=True)
         
-        config_dir = os.path.join(_get_config_dir(user=True), 'labconfig')
+        config_dir = os.path.join(_get_config_dir(user=True))
         cm = BaseJSONConfigManager(config_dir=config_dir)
-        enabled = cm.get('jupyterlab_config').get('LabApp', {}).get('labextensions', {}).get('mockextension', False)
-        assert not enabled
+        config = cm.get('jupyter_notebook_config').get('LabApp', {}).get('labextensions', {}).get('mockextension', {})
+        assert not config['enabled']
 
     def test_labextensionpy_validate(self):
         self._inject_mock_extension()
@@ -328,3 +332,32 @@ class TestInstallLabExtension(TestCase):
 
         config = get_labextension_config_python('mockextension')
         assert config['mockextension_foo'] == 1
+
+    def test_get_labextension_manifest_data_by_name(self):
+        self._inject_mock_extension()
+
+        install_labextension_python('mockextension', user=True)
+        enable_labextension_python('mockextension')
+
+        manifest = get_labextension_manifest_data_by_name('mockextension')
+        self.check_manifest(manifest)
+
+    def test_get_labextension_manifest_data_by_folder(self):
+        self._inject_mock_extension()
+
+        path = install_labextension_python('mockextension', user=True)[0]
+        enable_labextension_python('mockextension')
+
+        manifest = get_labextension_manifest_data_by_folder(path)
+        self.check_manifest(manifest)
+
+    def check_manifest(self, manifest):
+        assert 'mockextension' in manifest
+        mod = manifest['mockextension']
+        assert mod['name'] == 'mockextension'
+        assert 'jupyterlab/tests/mockextension/index.js' in mod['entry']
+        filename = 'mockextension.bundle.js'
+        assert mod['files'][0] == filename
+        assert mod['id'] == 0
+        assert len(mod['hash']) == 32
+        assert len(mod['modules']) == 1