Browse Source

Build cleanup

Steven Silvester 7 years ago
parent
commit
8dc2eae377
3 changed files with 94 additions and 58 deletions
  1. 2 7
      docs/extensions_dev.md
  2. 84 29
      jupyterlab/commands.py
  3. 8 22
      jupyterlab/labextensions.py

+ 2 - 7
docs/extensions_dev.md

@@ -84,13 +84,8 @@ jupyter labextension link <path>
 This causes the builder to re-install the source folder before building
 the application files.  You can re-build at any time using `jupyter lab build` and it will reinstall these packages.  You can also link other npm packages
 that you are working on simultaneously; they will be re-installed but not
-considered as extensions if they lack the metadata.
-
-You can see the list of linked extensions using:
-
-```
-jupyter labextension listlinked
-```
+considered as extensions if they lack the metadata.  Linked extensions and 
+packages are included in `jupyter labextension list`.
 
 You can also use `jupyter labextension install <path>`, but that will
 only copy the current contents of the source folder.

+ 84 - 29
jupyterlab/commands.py

@@ -104,7 +104,7 @@ def install_extension(extension, app_dir=None, logger=None):
     os.makedirs(target)
 
     # npm pack the extension
-    run(['npm', 'pack', extension], cwd=target, logger=logger)
+    run([get_npm_name(), 'pack', extension], cwd=target, logger=logger)
 
     fname = os.path.basename(glob.glob(pjoin(target, '*.*'))[0])
     data = _read_package(pjoin(target, fname))
@@ -147,8 +147,6 @@ def install_extension(extension, app_dir=None, logger=None):
         with open(path, 'w') as fid:
             fid.write(value)
 
-    return fname
-
 
 def link_package(path, app_dir=None, logger=None):
     """Link a package against the JupyterLab build.
@@ -177,12 +175,11 @@ def link_package(path, app_dir=None, logger=None):
 
     is_extension = _is_extension(data)
     if is_extension:
-        fname = install_extension(path, app_dir)
+        install_extension(path, app_dir)
     else:
         msg = ('*** Note: Linking non-extension package "%s" (lacks ' +
                '`jupyterlab.extension` metadata)')
         logger.info(msg % data['name'])
-        fname = path
 
     core_data = _get_core_data()
     deps = data.get('dependencies', dict())
@@ -197,8 +194,6 @@ def link_package(path, app_dir=None, logger=None):
     config['linked_packages'][data['name']] = path
     _write_build_config(config, app_dir, logger=logger)
 
-    return fname
-
 
 def unlink_package(package, app_dir=None, logger=None):
     """Unlink a package from JupyterLab by path or name.
@@ -244,6 +239,12 @@ def disable_extension(extension, app_dir=None, logger=None):
     _toggle_extension(extension, True, app_dir, logger)
 
 
+def get_npm_name():
+    """Get the appropriate npm executable name.
+    """
+    return 'npm.cmd' if os.name == 'nt' else 'npm'
+
+
 def check_node():
     """Check for the existence of node and whether it is the right version.
     """
@@ -373,7 +374,7 @@ def list_extensions(app_dir=None, logger=None):
     app_dir = get_app_dir(app_dir)
     extensions = _get_extensions(app_dir)
     disabled = _get_disabled(app_dir)
-    linked = _get_linked_packages(app_dir, logger=logger)
+    all_linked = _get_linked_packages(app_dir, logger=logger)
     app = []
     sys = []
     linked = []
@@ -386,7 +387,7 @@ def list_extensions(app_dir=None, logger=None):
     for (key, value) in extensions.items():
         deps = extensions[key].get('dependencies', dict())
         errors[key] = _validate_compatibility(key, deps, core_data)
-        if key in linked:
+        if key in all_linked:
             linked.append(key)
         if value['path'] == sys_path and sys_path != app_dir:
             sys.append(key)
@@ -398,6 +399,7 @@ def list_extensions(app_dir=None, logger=None):
     if app:
         logger.info('   app dir: %s' % app_dir)
         for item in sorted(app):
+            version = extensions[item]['version']
             extra = ''
             if item in disabled:
                 extra += ' %s' % RED_DISABLED
@@ -407,10 +409,7 @@ def list_extensions(app_dir=None, logger=None):
                 extra += ' %s' % RED_X
             else:
                 extra += ' %s' % GREEN_OK
-            if item in linked:
-                extra += '*'
-            logger.info('        %s%s' % (item, extra))
-            version = extensions[item]['version']
+            logger.info('        %s@%s%s' % (item, version, extra))
             if errors[item]:
                 msg = _format_compatibility_errors(item, version, errors[item])
                 logger.warn(msg + '\n')
@@ -418,6 +417,7 @@ def list_extensions(app_dir=None, logger=None):
     if sys:
         logger.info('   sys dir: %s' % sys_path)
         for item in sorted(sys):
+            version = extensions[item]['version']
             extra = ''
             if item in disabled:
                 extra += ' %s' % RED_DISABLED
@@ -430,14 +430,22 @@ def list_extensions(app_dir=None, logger=None):
                 extra += ' %s' % GREEN_OK
             if item in linked:
                 extra += '*'
-            logger.info('        %s%s' % (item, extra))
-            version = extensions[item]['version']
+            logger.info('        %s@%s%s' % (item, version, extra))
             if errors[item]:
                 msg = _format_compatibility_errors(item, version, errors[item])
                 logger.warn(msg + '\n')
 
     if linked:
-        logger.info('* Denotes linked extensions. Use `jupyter labextension listlinked` to see details')
+        logger.info('   linked extensions:')
+        for item in sorted(linked):
+            logger.info('        %s: %s' % (item, all_linked[item]))
+
+    if len(all_linked) > len(linked):
+        logger.info('   linked packages:')
+        for key in sorted(all_linked.keys()):
+            if (key in linked):
+                continue
+            logger.info('        %s: %s' % (key, all_linked[key]))
 
     # Handle uninstalled and disabled core packages
     uninstalled_core = _get_uinstalled_core_extensions(app_dir)
@@ -482,6 +490,12 @@ def build(app_dir=None, name=None, version=None, logger=None):
 
     extensions = _get_extensions(app_dir)
 
+    # Ensure an empty linked_packages directory
+    linked_packages = pjoin(staging, 'linked_packages')
+    if osp.exists(linked_packages):
+        shutil.rmtree(linked_packages)
+    os.makedirs(linked_packages)
+
     # Install the linked packages.
     for (name, path) in _get_linked_packages(app_dir, logger=logger).items():
         # Handle linked extensions.
@@ -489,17 +503,15 @@ def build(app_dir=None, name=None, version=None, logger=None):
             install_extension(path, app_dir)
         # Handle linked packages that are not extensions.
         else:
-            # Remove any existing package from staging/node_modules
-            target = pjoin(staging, 'node_modules', name)
-            target = target.replace('/', os.sep)
-            if os.path.exists(target):
-                shutil.rmtree(target)
+            _install_linked_package(staging, name, path, logger)
+
+    npm = get_npm_name()
 
     # Make sure packages are installed.
-    run(['npm', 'install'], cwd=staging, logger=logger)
+    run([npm, 'install'], cwd=staging, logger=logger)
 
     # Build the app.
-    run(['npm', 'run', 'build'], cwd=staging, logger=logger)
+    run([npm, 'run', 'build'], cwd=staging, logger=logger)
 
     # Move the app to the static dir.
     static = pjoin(app_dir, 'static')
@@ -508,6 +520,55 @@ def build(app_dir=None, name=None, version=None, logger=None):
     shutil.copytree(pjoin(staging, 'build'), static)
 
 
+def _install_linked_package(staging, name, path, logger):
+    """Install a linked non-extension package using a package tarball
+    to prevent it from being treated as a symlink.
+    """
+    # Remove any existing package from staging/node_modules
+    target = pjoin(staging, 'node_modules', name)
+    target = target.replace('/', os.sep)
+    if os.path.exists(target):
+        shutil.rmtree(target)
+
+    linked = pjoin(staging, 'linked_pacakges')
+
+    target = pjoin(linked, 'temp')
+    if os.path.exists(target):
+        shutil.rmtree(target)
+    os.makedirs(target)
+
+    # npm pack the extension
+    run([get_npm_name(), 'pack', path], cwd=target, logger=logger)
+
+    fname = os.path.basename(glob.glob(pjoin(target, '*.*'))[0])
+    data = _read_package(pjoin(target, fname))
+
+    # Remove the tarball if the package is not compatible.
+    core_data = _get_core_data()
+    deps = data.get('dependencies', dict())
+    errors = _validate_compatibility(path, deps, core_data)
+    if errors:
+        shutil.rmtree(target)
+        msg = _format_compatibility_errors(
+            data['name'], data['version'], errors
+        )
+        raise ValueError(msg)
+
+    # Remove an existing extension tarball.
+    ext_path = pjoin(linked, fname)
+    if os.path.exists(ext_path):
+        os.remove(ext_path)
+
+    # Move
+    shutil.move(pjoin(target, fname), pjoin(staging, 'linked_pakages'))
+    shutil.rmtree(target)
+
+    # Set the dependency in the package.
+    core_data['dependencies'][data['name']] = ext_path
+    with open(pjoin(staging, 'package.json'), 'w') as fid:
+        json.dump(core_data, fid, indent=4)
+
+
 def _get_build_config(app_dir):
     """Get the build config data for the given app dir
     """
@@ -732,12 +793,6 @@ def _get_package_template(app_dir, logger):
         else:
             data['jupyterlab']['mimeExtensions'].append(key)
 
-    # Handle linked packages that are not extensions.
-    for (key, path) in _get_linked_packages(app_dir).items():
-        if key in extensions:
-            continue
-        data['dependencies'][key] = path
-
     for item in _get_uinstalled_core_extensions(app_dir):
         if item in data['jupyterlab']['extensions']:
             data['jupyterlab']['extensions'].remove(item)

+ 8 - 22
jupyterlab/labextensions.py

@@ -53,16 +53,14 @@ class InstallLabExtensionApp(BaseExtensionApp):
 
     def start(self):
         self.extra_args = self.extra_args or [os.getcwd()]
-        paths = [
-            install_extension(arg, self.app_dir, logger=self.log)
-            for arg in self.extra_args
-        ]
+        [install_extension(arg, self.app_dir, logger=self.log)
+         for arg in self.extra_args]
         if self.should_build:
             try:
                 build(self.app_dir, logger=self.log)
             except Exception as e:
-                for path in paths:
-                    os.remove(path)
+                for arg in self.extra_args:
+                    uninstall_extension(arg, self.app_dir, logger=self.log)
                 raise e
 
 
@@ -80,16 +78,14 @@ class LinkLabExtensionApp(BaseExtensionApp):
 
     def start(self):
         self.extra_args = self.extra_args or [os.getcwd()]
-        paths = [
-            link_package(arg, self.app_dir, logger=self.log)
-            for arg in self.extra_args
-        ]
+        [link_package(arg, self.app_dir, logger=self.log)
+         for arg in self.extra_args]
         if self.should_build:
             try:
                 build(self.app_dir, logger=self.log)
             except Exception as e:
-                for path in paths:
-                    os.remove(path)
+                for arg in self.extra_args:
+                    unlink_package(arg, self.app_dir, logger=self.log)
                 raise e
 
 
@@ -126,15 +122,6 @@ class ListLabExtensionsApp(BaseExtensionApp):
         list_extensions(self.app_dir, logger=self.log)
 
 
-class ListLinkedLabExtensionsApp(BaseExtensionApp):
-    description = "List the linked packages"
-
-    def start(self):
-        linked = _get_linked_packages(self.app_dir, logger=self.log)
-        for path in linked.values():
-            print(path)
-
-
 class EnableLabExtensionsApp(BaseExtensionApp):
     description = "Enable labextension(s) by name"
 
@@ -171,7 +158,6 @@ class LabExtensionApp(JupyterApp):
         list=(ListLabExtensionsApp, "List labextensions"),
         link=(LinkLabExtensionApp, "Link labextension(s)"),
         unlink=(UnlinkLabExtensionApp, "Unlink labextension(s)"),
-        listlinked=(ListLinkedLabExtensionsApp, "List linked extensions"),
         enable=(EnableLabExtensionsApp, "Enable labextension(s)"),
         disable=(DisableLabExtensionsApp, "Disable labextensions(s)")
     )