From 29fabd3c168c14ec8ed38499976eba8fddf08d80 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Sun, 19 Jul 2015 11:43:22 -0700 Subject: [PATCH 1/5] Add command-line interface to enable/disable nbextensions i.e. to enable cite2c, you'd use `jupyter nbextension enable cite2c/main`. Open questions: 1. Should these commands accept multiple extension names in one call? nbextension install currently doesn't, so there's a symmetry argument, but it would be easy to handle more than one if necessary. 2. Especially for disable, should it give an error if you try to disable something that's not enabled? Currently it will just pass silently. Enable could also fail if it's already enabled, but that seems less useful. --- notebook/nbextensions.py | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index 86650e64a..946c36769 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -24,6 +24,7 @@ from jupyter_core.paths import jupyter_data_dir, jupyter_path, SYSTEM_JUPYTER_PA from ipython_genutils.path import ensure_dir_exists from ipython_genutils.py3compat import string_types, cast_unicode_py2 from ipython_genutils.tempdir import TemporaryDirectory +from notebook.services.config import ConfigManager from ._version import __version__ class ArgumentConflict(ValueError): @@ -316,6 +317,57 @@ class InstallNBExtensionApp(JupyterApp): print(str(e), file=sys.stderr) self.exit(1) + +class EnableNBExtensionApp(JupyterApp): + name = "jupyter nbextension enable" + version = __version__ + description = "Configure an nbextension to be automatically loaded" + + section = Unicode('notebook', config=True, + help=("Which config section to add the extension to. " + "'common' will affect all pages.") + ) + + aliases = {'section': 'EnableNBExtensionApp.section', + } + + def enable_nbextension(self, name): + cm = ConfigManager(parent=self, config=self.config) + cm.update(self.section, {"load_extensions": {name: True}}) + + def start(self): + if not self.extra_args: + self.print_help() + sys.exit('No extensions specified') + + self.enable_nbextension(self.extra_args[0]) + + +class DisableNBExtensionApp(JupyterApp): + name = "jupyter nbextension disable" + version = __version__ + description = "Remove the configuration to automatically load an extension" + + section = Unicode('notebook', config=True, + help=("Which config section to remove the extension from. " + "This should match the one it was previously added to.") + ) + + aliases = {'section': 'DisableNBExtensionApp.section', + } + + def disable_nbextension(self, name): + cm = ConfigManager(parent=self, config=self.config) + # We're using a dict as a set - updating with None removes the key + cm.update(self.section, {"load_extensions": {name: None}}) + + def start(self): + if not self.extra_args: + self.print_help() + sys.exit('No extensions specified') + + self.disable_nbextension(self.extra_args[0]) + class NBExtensionApp(JupyterApp): name = "jupyter nbextension" version = __version__ @@ -325,6 +377,8 @@ class NBExtensionApp(JupyterApp): install=(InstallNBExtensionApp, """Install notebook extensions""" ), + enable=(EnableNBExtensionApp, "Enable a notebook extension"), + disable=(DisableNBExtensionApp, "Disable a notebook extension"), ) def start(self): From ff9f36e7f0e507faf41c36359d26451e677eb1f4 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Sun, 19 Jul 2015 11:58:49 -0700 Subject: [PATCH 2/5] Local imports of ConfigManager to avoid Py2 circular import issue --- notebook/nbextensions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index 946c36769..b1277719d 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -24,7 +24,6 @@ from jupyter_core.paths import jupyter_data_dir, jupyter_path, SYSTEM_JUPYTER_PA from ipython_genutils.path import ensure_dir_exists from ipython_genutils.py3compat import string_types, cast_unicode_py2 from ipython_genutils.tempdir import TemporaryDirectory -from notebook.services.config import ConfigManager from ._version import __version__ class ArgumentConflict(ValueError): @@ -332,6 +331,8 @@ class EnableNBExtensionApp(JupyterApp): } def enable_nbextension(self, name): + # Local import to avoid circular import issue on Py 2 + from notebook.services.config import ConfigManager cm = ConfigManager(parent=self, config=self.config) cm.update(self.section, {"load_extensions": {name: True}}) @@ -357,6 +358,8 @@ class DisableNBExtensionApp(JupyterApp): } def disable_nbextension(self, name): + # Local import to avoid circular import issue on Py 2 + from notebook.services.config import ConfigManager cm = ConfigManager(parent=self, config=self.config) # We're using a dict as a set - updating with None removes the key cm.update(self.section, {"load_extensions": {name: None}}) From c21debfc4a32f260ec75b12348dc39393fafb98e Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Sun, 19 Jul 2015 21:04:33 -0700 Subject: [PATCH 3/5] Use notebook config file for nbextension commands --- notebook/nbextensions.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index b1277719d..98e31f441 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -288,6 +288,9 @@ class InstallNBExtensionApp(JupyterApp): verbose = Enum((0,1,2), default_value=1, config=True, help="Verbosity level" ) + + def _config_file_name_default(self): + return 'jupyter_notebook_config' def install_extensions(self): if len(self.extra_args)>1: @@ -330,6 +333,9 @@ class EnableNBExtensionApp(JupyterApp): aliases = {'section': 'EnableNBExtensionApp.section', } + def _config_file_name_default(self): + return 'jupyter_notebook_config' + def enable_nbextension(self, name): # Local import to avoid circular import issue on Py 2 from notebook.services.config import ConfigManager @@ -357,6 +363,9 @@ class DisableNBExtensionApp(JupyterApp): aliases = {'section': 'DisableNBExtensionApp.section', } + def _config_file_name_default(self): + return 'jupyter_notebook_config' + def disable_nbextension(self, name): # Local import to avoid circular import issue on Py 2 from notebook.services.config import ConfigManager From 095e77dfd7e1dca278a06160cffac727ea3331d6 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Mon, 20 Jul 2015 10:22:15 -0700 Subject: [PATCH 4/5] Error on >1 extension for enable/disable commands --- notebook/nbextensions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index 98e31f441..92b42c179 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -344,8 +344,9 @@ class EnableNBExtensionApp(JupyterApp): def start(self): if not self.extra_args: - self.print_help() sys.exit('No extensions specified') + elif len(self.extra_args) > 1: + sys.exit('Please specify one extension at a time') self.enable_nbextension(self.extra_args[0]) @@ -375,8 +376,9 @@ class DisableNBExtensionApp(JupyterApp): def start(self): if not self.extra_args: - self.print_help() sys.exit('No extensions specified') + elif len(self.extra_args) > 1: + sys.exit('Please specify one extension at a time') self.disable_nbextension(self.extra_args[0]) From 00bb47fb244a610a8e600cee04f18e8fbe9b0c56 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Mon, 20 Jul 2015 10:22:32 -0700 Subject: [PATCH 5/5] Error on disabling extension not currently enabled --- notebook/nbextensions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py index 92b42c179..a9ea2c5e2 100644 --- a/notebook/nbextensions.py +++ b/notebook/nbextensions.py @@ -371,6 +371,8 @@ class DisableNBExtensionApp(JupyterApp): # Local import to avoid circular import issue on Py 2 from notebook.services.config import ConfigManager cm = ConfigManager(parent=self, config=self.config) + if name not in cm.get(self.section).get('load_extensions', {}): + sys.exit('{} is not enabled in section {}'.format(name, self.section)) # We're using a dict as a set - updating with None removes the key cm.update(self.section, {"load_extensions": {name: None}})