From cfe8a233765dc3f7b55b39876e2842a496e89efa Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 4 Aug 2017 11:48:37 +0100 Subject: [PATCH 1/5] Disable nbextension in first location found --- notebook/nbextensions.py | 58 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index b22fbefda1..d161f29fc5 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -20,6 +20,7 @@ from urlparse import urlparse from urllib import urlretrieve +from jupyter_core.application import JupyterApp from jupyter_core.paths import ( jupyter_data_dir, jupyter_config_path, jupyter_path, SYSTEM_JUPYTER_PATH, ENV_JUPYTER_PATH, @@ -29,6 +30,7 @@ from ipython_genutils.tempdir import TemporaryDirectory from ._version import __version__ +from tornado.log import LogFormatter from traitlets.config.manager import BaseJSONConfigManager from traitlets.utils.importstring import import_item @@ -419,6 +421,22 @@ def disable_nbextension(section, require, user=True, sys_prefix=False, user=user, sys_prefix=sys_prefix, logger=logger) +def _find_disable_nbextension(section, require, logger=None): + """Disable an nbextension from the first config location where it is enabled. + + Returns True if it changed any config, False otherwise. + """ + for config_dir in jupyter_config_path(): + cm = BaseJSONConfigManager(config_dir=os.path.join(config_dir, 'nbconfig')) + d = cm.get(section) + if d.get('load_extensions', {}).get(require, None): + if logger: + logger.info("Disabling %s extension in %s", require, config_dir) + cm.update(section, {'load_extensions': {require: None}}) + return True + + return False + def enable_nbextension_python(module, user=True, sys_prefix=False, logger=None): @@ -822,7 +840,7 @@ class EnableNBExtensionApp(ToggleNBExtensionApp): _toggle_value = True -class DisableNBExtensionApp(ToggleNBExtensionApp): +class DisableNBExtensionApp(JupyterApp): """An App that disables nbextensions""" name = "jupyter nbextension disable" description = """ @@ -831,7 +849,43 @@ class DisableNBExtensionApp(ToggleNBExtensionApp): Usage jupyter nbextension disable [--system|--sys-prefix] """ - _toggle_value = None + version = __version__ + flags = {"py" : ({ + "DisableNBExtensionApp" : { + "python" : True, + }}, "Disable an extension from a Python package name") + } + flags['python'] = flags['py'] + aliases = {'section': 'ToggleNBExtensionApp.section'} + + python = Bool(False, config=True, help="Install from a Python package") + + section = Unicode('notebook', config=True, + help="""Which config section to disable the extension in.""" + ) + + def start(self): + name = self.extra_args[0] + if self.python: + _, nbexts = _get_nbextension_metadata(name) + changed = False + for nbext in nbexts: + if _find_disable_nbextension(self.section, nbext, + logger=self.log): + changed = True + + else: + changed = _find_disable_nbextension(self.section, name, + logger=self.log) + + if not changed: + print("No config found enabling", name) + + _log_formatter_cls = LogFormatter + + def _log_format_default(self): + """A default format for messages""" + return "%(message)s" class ListNBExtensionsApp(BaseExtensionApp): From 5eca411d44069002729712d2a32112e053a59d81 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 4 Aug 2017 16:18:59 +0100 Subject: [PATCH 2/5] Add options for explicitly disabling extension in user or sys.prefix config --- notebook/nbextensions.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index d161f29fc5..52df921b9b 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -850,13 +850,20 @@ class DisableNBExtensionApp(JupyterApp): jupyter nbextension disable [--system|--sys-prefix] """ version = __version__ - flags = {"py" : ({ - "DisableNBExtensionApp" : { - "python" : True, - }}, "Disable an extension from a Python package name") + flags = { + "py" : ({"DisableNBExtensionApp" : {"python" : True}}, + "Disable an extension from a Python package name"), + "user": ({"DisableNBExtensionApp" : {"user": True}}, + "Explicitly disable in user config"), + "sys-prefix": ({"DisableNBExtensionApp": {"sys_prefix": True}}, + "Explicitly disable in sys.prefix config") } flags['python'] = flags['py'] - aliases = {'section': 'ToggleNBExtensionApp.section'} + aliases = {'section': 'DisableNBExtensionApp.section'} + + user = Bool(False, config=True, help="Explicitly disable in user config") + sys_prefix = Bool(False, config=True, + help="Explicitly disable in sys.prefix config") python = Bool(False, config=True, help="Install from a Python package") @@ -866,6 +873,16 @@ class DisableNBExtensionApp(JupyterApp): def start(self): name = self.extra_args[0] + + disable_func = disable_nbextension_python if self.python \ + else disable_nbextension + if self.user: + return disable_func(self.section, name, user=True, logger=self.log) + if self.sys_prefix: + return disable_func(self.section, name, sys_prefix=True, + logger=self.log) + + # Default behaviour: find and remove config enabling an extension. if self.python: _, nbexts = _get_nbextension_metadata(name) changed = False From e6a7fe6296f25238ccc0cd79f1036339012dada6 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 4 Aug 2017 16:50:16 +0100 Subject: [PATCH 3/5] Add searching behaviour to 'jupyter nbextension uninstall' --- notebook/nbextensions.py | 65 ++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index 52df921b9b..a4d59e3d07 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -243,10 +243,7 @@ def uninstall_nbextension(dest, require=None, user=False, sys_prefix=False, pref ---------- dest : str - path to file, directory, zip or tarball archive, or URL to install - name the nbextension is installed to. For example, if destination is 'foo', then - the source file will be installed to 'nbextensions/foo', regardless of the source name. - This cannot be specified if an archive is given as the source. + Name of the installed nbextension file or folder. require : str [optional] require.js path used to load the extension. If specified, frontend config loading extension will be removed. @@ -280,6 +277,25 @@ def uninstall_nbextension(dest, require=None, user=False, sys_prefix=False, pref for section in NBCONFIG_SECTIONS: cm.update(section, {"load_extensions": {require: None}}) +def _find_uninstall_nbextension(filename, logger=None): + """Remove nbextension files from the first location they are found. + + Returns True if files were removed, False otherwise. + """ + filename = cast_unicode_py2(filename) + for nbext in jupyter_path('nbextensions'): + path = pjoin(nbext, filename) + if os.path.lexists(path): + if logger: + logger.info("Removing: %s" % path) + if os.path.isdir(path) and not os.path.islink(path): + shutil.rmtree(path) + else: + os.remove(path) + return True + + return False + def uninstall_nbextension_python(module, user=False, sys_prefix=False, prefix=None, nbextensions_dir=None, @@ -730,8 +746,8 @@ def _config_file_name_default(self): """The default config file name.""" return 'jupyter_notebook_config' - def uninstall_extensions(self): - """Uninstall some nbextensions""" + def uninstall_extension(self): + """Uninstall an nbextension from a specific location""" kwargs = { 'user': self.user, 'sys_prefix': self.sys_prefix, @@ -739,28 +755,47 @@ def uninstall_extensions(self): 'nbextensions_dir': self.nbextensions_dir, 'logger': self.log } - - arg_count = 1 - if len(self.extra_args) > arg_count: - raise ValueError("only one nbextension allowed at a time. Call multiple times to uninstall multiple extensions.") - if len(self.extra_args) < arg_count: - raise ValueError("not enough arguments") - + if self.python: uninstall_nbextension_python(self.extra_args[0], **kwargs) else: if self.require: kwargs['require'] = self.require uninstall_nbextension(self.extra_args[0], **kwargs) + + def find_uninstall_extension(self): + """Uninstall an nbextension from an unspecified location""" + name = self.extra_args[0] + if self.python: + _, nbexts = _get_nbextension_metadata(name) + changed = False + for nbext in nbexts: + if _find_uninstall_nbextension(nbext, logger=self.log): + changed = True + + else: + changed = _find_uninstall_nbextension(name, logger=self.log) + + if not changed: + print("No installed extension %r found." % name) + + if self.require: + for section in NBCONFIG_SECTIONS: + _find_disable_nbextension(section, self.require, logger=self.log) def start(self): if not self.extra_args: sys.exit('Please specify an nbextension to uninstall') - else: + elif len(self.extra_args) > 1: + sys.exit("Only one nbextension allowed at a time. " + "Call multiple times to uninstall multiple extensions.") + elif self.user or self.sys_prefix or self.prefix or self.nbextensions_dir: try: - self.uninstall_extensions() + self.uninstall_extension() except ArgumentConflict as e: sys.exit(str(e)) + else: + self.find_uninstall_extension() class ToggleNBExtensionApp(BaseExtensionApp): From a17f3848f56eb791d5e6405ede80a39558bc2b88 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 4 Aug 2017 17:19:53 +0100 Subject: [PATCH 4/5] Fold ToggleNBExtensionApp into EnableNBExtensionApp, other fixes --- notebook/nbextensions.py | 127 ++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 75 deletions(-) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index a4d59e3d07..16eca0944a 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -770,7 +770,7 @@ def find_uninstall_extension(self): _, nbexts = _get_nbextension_metadata(name) changed = False for nbext in nbexts: - if _find_uninstall_nbextension(nbext, logger=self.log): + if _find_uninstall_nbextension(nbext['dest'], logger=self.log): changed = True else: @@ -798,88 +798,54 @@ def start(self): self.find_uninstall_extension() -class ToggleNBExtensionApp(BaseExtensionApp): - """A base class for apps that enable/disable extensions""" - name = "jupyter nbextension enable/disable" +class EnableNBExtensionApp(BaseExtensionApp): + """An App that enables nbextensions""" + name = "jupyter nbextension enable" + description = """ + Enable an nbextension in frontend configuration. + + Usage + jupyter nbextension enable [--system|--sys-prefix] + """ version = __version__ - description = "Enable/disable an nbextension in configuration." section = Unicode('notebook', config=True, help="""Which config section to add the extension to, 'common' will affect all pages.""" ) - user = Bool(True, config=True, help="Apply the configuration only for the current user (default)") + user = Bool(True, config=True, + help="Enable the extension for the current user (default)" + ) - aliases = {'section': 'ToggleNBExtensionApp.section'} - - _toggle_value = None + aliases = {'section': 'EnableNBExtensionApp.section'} + flags = BaseExtensionApp.flags.copy() + flags['user'] = ({'EnableNBExtensionApp': {'user': True}}, + "Enable the extension for the current user (default)") def _config_file_name_default(self): """The default config file name.""" return 'jupyter_notebook_config' - - def toggle_nbextension_python(self, module): - """Toggle some extensions in an importable Python module. - - Returns a list of booleans indicating whether the state was changed as - requested. - - Parameters - ---------- - module : str - Importable Python module exposing the - magic-named `_jupyter_nbextension_paths` function - """ - toggle = (enable_nbextension_python if self._toggle_value - else disable_nbextension_python) - return toggle(module, - user=self.user, - sys_prefix=self.sys_prefix, - logger=self.log) - - def toggle_nbextension(self, require): - """Toggle some a named nbextension by require-able AMD module. - - Returns whether the state was changed as requested. - - Parameters - ---------- - require : str - require.js path used to load the nbextension - """ - toggle = (enable_nbextension if self._toggle_value - else disable_nbextension) - return toggle(self.section, require, - user=self.user, sys_prefix=self.sys_prefix, - logger=self.log) def start(self): if not self.extra_args: sys.exit('Please specify an nbextension/package to enable or disable') elif len(self.extra_args) > 1: sys.exit('Please specify one nbextension/package at a time') + name = self.extra_args[0] + if self.python: - self.toggle_nbextension_python(self.extra_args[0]) + enable_nbextension_python(name, user=self.user, + sys_prefix=self.sys_prefix, logger=self.log) else: - self.toggle_nbextension(self.extra_args[0]) + enable_nbextension(self.section, name, + user=self.user, sys_prefix=self.sys_prefix, + logger=self.log) -class EnableNBExtensionApp(ToggleNBExtensionApp): - """An App that enables nbextensions""" - name = "jupyter nbextension enable" - description = """ - Enable an nbextension in frontend configuration. - - Usage - jupyter nbextension enable [--system|--sys-prefix] - """ - _toggle_value = True - - -class DisableNBExtensionApp(JupyterApp): +class DisableNBExtensionApp(BaseExtensionApp): """An App that disables nbextensions""" name = "jupyter nbextension disable" description = """ - Enable an nbextension in frontend configuration. + Disable an nbextension in frontend configuration. Usage jupyter nbextension disable [--system|--sys-prefix] @@ -900,29 +866,23 @@ class DisableNBExtensionApp(JupyterApp): sys_prefix = Bool(False, config=True, help="Explicitly disable in sys.prefix config") - python = Bool(False, config=True, help="Install from a Python package") + python = Bool(False, config=True, + help="Locate the extension to disable from a Python package") section = Unicode('notebook', config=True, - help="""Which config section to disable the extension in.""" - ) - - def start(self): - name = self.extra_args[0] + help="Which config section to disable the extension in.") - disable_func = disable_nbextension_python if self.python \ - else disable_nbextension - if self.user: - return disable_func(self.section, name, user=True, logger=self.log) - if self.sys_prefix: - return disable_func(self.section, name, sys_prefix=True, - logger=self.log) + def _config_file_name_default(self): + """The default config file name.""" + return 'jupyter_notebook_config' - # Default behaviour: find and remove config enabling an extension. + def find_disable_extension(self, name): + """Default behaviour: find and remove config enabling an extension.""" if self.python: _, nbexts = _get_nbextension_metadata(name) changed = False for nbext in nbexts: - if _find_disable_nbextension(self.section, nbext, + if _find_disable_nbextension(nbext['section'], nbext['require'], logger=self.log): changed = True @@ -933,6 +893,23 @@ def start(self): if not changed: print("No config found enabling", name) + def start(self): + if not self.extra_args: + sys.exit('Please specify an nbextension/package to disable') + elif len(self.extra_args) > 1: + sys.exit('Please specify one nbextension/package at a time') + name = self.extra_args[0] + + disable_func = disable_nbextension_python if self.python \ + else disable_nbextension + if self.user: + return disable_func(self.section, name, user=True, logger=self.log) + if self.sys_prefix: + return disable_func(self.section, name, sys_prefix=True, + logger=self.log) + + self.find_disable_extension(name) + _log_formatter_cls = LogFormatter def _log_format_default(self): From 1127c6e6d5c1dd8467ef8dd6bbb29ace392eedc7 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 4 Aug 2017 17:26:20 +0100 Subject: [PATCH 5/5] Fix usage string --- notebook/nbextensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index 16eca0944a..dd51b1cc39 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -805,7 +805,7 @@ class EnableNBExtensionApp(BaseExtensionApp): Enable an nbextension in frontend configuration. Usage - jupyter nbextension enable [--system|--sys-prefix] + jupyter nbextension enable [--system|--sys-prefix] myext """ version = __version__ @@ -848,7 +848,7 @@ class DisableNBExtensionApp(BaseExtensionApp): Disable an nbextension in frontend configuration. Usage - jupyter nbextension disable [--system|--sys-prefix] + jupyter nbextension disable [--user|--sys-prefix] myext """ version = __version__ flags = {