diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index 06fd313f1..71977833c 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -215,14 +215,19 @@ def install_nbextension(path, overwrite=False, symlink=False, def install_nbextension_python(package, overwrite=False, symlink=False, user=False, sys_prefix=False, prefix=None, nbextensions_dir=None, logger=None): """Install an nbextension bundled in a Python package. - + + Returns a list of installed/updated directories. + See install_nbextension for parameter information.""" m, nbexts = _get_nbextension_metadata(package) base_path = os.path.split(m.__file__)[0] + + full_dests = [] + for nbext in nbexts: src = os.path.join(base_path, nbext['src']) dest = nbext['dest'] - require = nbext['require'] + if logger: logger.info("Installing %s -> %s" % (src, dest)) full_dest = install_nbextension( @@ -231,8 +236,9 @@ def install_nbextension_python(package, overwrite=False, symlink=False, destination=dest, logger=logger ) validate_nbextension_python(nbext, full_dest, logger) + full_dests.append(full_dest) - + return full_dests def uninstall_nbextension(dest, require, user=False, sys_prefix=False, prefix=None, nbextensions_dir=None, logger=None): @@ -379,16 +385,26 @@ def validate_nbextension(require, logger=None): return warnings + def validate_nbextension_python(spec, full_dest, logger=None): """Assess the health of an installed nbextension + Returns a list of warnings. + Parameters ---------- spec : dict - A single entry of _jupyter_nbextension_paths() + A single entry of _jupyter_nbextension_paths(): + [{ + 'section': 'notebook', + 'src': 'mockextension', + 'dest': '_mockdestination', + 'require': '_mockdestination/index' + }] full_dest : str - The on-disk location of the installed nbextension + The on-disk location of the installed nbextension: this should end + with `nbextensions/` logger : Jupyter logger [optional] Logger instance to use """ @@ -403,7 +419,9 @@ def validate_nbextension_python(spec, full_dest, logger=None): require = spec.get("require", None) if require is not None: - require_path = os.path.join(full_dest, "{}.js".format(require)) + require_path = os.path.join( + full_dest[0:-len(spec["dest"])], + "{}.js".format(require)) if os.path.exists(require_path): infos.append(" {} require: {}".format(GREEN_OK, require_path)) else: @@ -418,7 +436,7 @@ def validate_nbextension_python(spec, full_dest, logger=None): else: logger.info("- Validating: {}".format(GREEN_OK)) - return infos, warnings + return warnings #---------------------------------------------------------------------- diff --git a/notebook/serverextensions.py b/notebook/serverextensions.py index a78cacfa7..13b8d0f01 100644 --- a/notebook/serverextensions.py +++ b/notebook/serverextensions.py @@ -85,6 +85,8 @@ def toggle_serverextension_python(import_name, enabled=None, parent=None, def validate_serverextension(import_name, logger=None): """Assess the health of an installed server extension + Returns a list of validation warnings. + Parameters ---------- @@ -123,7 +125,7 @@ def validate_serverextension(import_name, logger=None): else: logger.info(post_mortem.format(import_name, "", GREEN_OK)) - return not warnings + return warnings # ---------------------------------------------------------------------- diff --git a/notebook/tests/test_nbextensions.py b/notebook/tests/test_nbextensions.py index 39d73f21c..a99790a46 100644 --- a/notebook/tests/test_nbextensions.py +++ b/notebook/tests/test_nbextensions.py @@ -24,7 +24,8 @@ from ipython_genutils.tempdir import TemporaryDirectory from notebook import nbextensions from notebook.nbextensions import (install_nbextension, check_nbextension, install_nbextension_python, uninstall_nbextension_python, - enable_nbextension_python, disable_nbextension_python, _get_config_dir + enable_nbextension_python, disable_nbextension_python, _get_config_dir, + validate_nbextension, validate_nbextension_python ) from traitlets.config.manager import BaseJSONConfigManager @@ -346,20 +347,26 @@ class TestInstallNBExtension(TestCase): with self.assertRaises(ValueError): install_nbextension(zsrc, destination='foo') - + + def _mock_extension_spec_meta(self, section='notebook'): + return { + 'section': section, + 'src': 'mockextension', + 'dest': '_mockdestination', + 'require': '_mockdestination/index' + } + def _inject_mock_extension(self, section='notebook'): outer_file = __file__ + + meta = self._mock_extension_spec_meta(section) + class mock(): __file__ = outer_file @staticmethod def _jupyter_nbextension_paths(): - return [{ - 'section': section, - 'src': 'mockextension', - 'dest': '_mockdestination', - 'require': '_mockdestination/index' - }] + return [meta] import sys sys.modules['mockextension'] = mock @@ -406,3 +413,27 @@ class TestInstallNBExtension(TestCase): cm = BaseJSONConfigManager(config_dir=config_dir) enabled = cm.get('notebook').get('load_extensions', {}).get('_mockdestination/index', False) assert not enabled + + def test_nbextensionpy_validate(self): + self._inject_mock_extension('notebook') + + paths = install_nbextension_python('mockextension') + enable_nbextension_python('mockextension', user=True) + + meta = self._mock_extension_spec_meta() + warnings = validate_nbextension_python(meta, paths[0]) + self.assertEqual([], warnings, warnings) + + def test_nbextensionpy_validate_bad(self): + # Break the metadata (correct file will still be copied) + self._inject_mock_extension('notebook') + + paths = install_nbextension_python('mockextension') + enable_nbextension_python('mockextension', user=True) + + meta = self._mock_extension_spec_meta() + meta.update(require="bad-require") + + warnings = validate_nbextension_python(meta, paths[0]) + + self.assertNotEqual([], warnings, warnings)