Skip to content

Commit 114b916

Browse files
authored
Merge pull request #2729 from takluyver/uninstall-nbext-find
Uninstall an nbextension from the first location it is found
2 parents fcc5b0f + 2884afc commit 114b916

File tree

1 file changed

+107
-19
lines changed

1 file changed

+107
-19
lines changed

notebook/nbextensions.py

Lines changed: 107 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,26 @@ def uninstall_nbextension(dest, require=None, user=False, sys_prefix=False, pref
279279
cm.update(section, {"load_extensions": {require: None}})
280280

281281

282+
def _find_uninstall_nbextension(filename, logger=None):
283+
"""Remove nbextension files from the first location they are found.
284+
285+
Returns True if files were removed, False otherwise.
286+
"""
287+
filename = cast_unicode_py2(filename)
288+
for nbext in jupyter_path('nbextensions'):
289+
path = pjoin(nbext, filename)
290+
if os.path.lexists(path):
291+
if logger:
292+
logger.info("Removing: %s" % path)
293+
if os.path.isdir(path) and not os.path.islink(path):
294+
shutil.rmtree(path)
295+
else:
296+
os.remove(path)
297+
return True
298+
299+
return False
300+
301+
282302
def uninstall_nbextension_python(module,
283303
user=False, sys_prefix=False, prefix=None, nbextensions_dir=None,
284304
logger=None):
@@ -420,6 +440,24 @@ def disable_nbextension(section, require, user=True, sys_prefix=False,
420440
logger=logger)
421441

422442

443+
def _find_disable_nbextension(section, require, logger=None):
444+
"""Disable an nbextension from the first config location where it is enabled.
445+
446+
Returns True if it changed any config, False otherwise.
447+
"""
448+
for config_dir in jupyter_config_path():
449+
cm = BaseJSONConfigManager(
450+
config_dir=os.path.join(config_dir, 'nbconfig'))
451+
d = cm.get(section)
452+
if d.get('load_extensions', {}).get(require, None):
453+
if logger:
454+
logger.info("Disabling %s extension in %s", require, config_dir)
455+
cm.update(section, {'load_extensions': {require: None}})
456+
return True
457+
458+
return False
459+
460+
423461
def enable_nbextension_python(module, user=True, sys_prefix=False,
424462
logger=None):
425463
"""Enable some nbextensions associated with a Python module.
@@ -689,8 +727,20 @@ class UninstallNBExtensionApp(BaseExtensionApp):
689727
690728
jupyter nbextension uninstall path/url path/url/entrypoint
691729
jupyter nbextension uninstall --py pythonPackageName
692-
693-
This uninstalls an nbextension.
730+
731+
This uninstalls an nbextension. By default, it uninstalls from the
732+
first directory on the search path where it finds the extension, but you can
733+
uninstall from a specific location using the --user, --sys-prefix or
734+
--system flags, or the --prefix option.
735+
736+
If you specify the --require option, the named extension will be disabled,
737+
e.g.::
738+
739+
jupyter nbextension uninstall myext --require myext/main
740+
741+
If you use the --py or --python flag, the name should be a Python module.
742+
It will uninstall nbextensions listed in that module, but not the module
743+
itself (which you should uninstall using a package manager such as pip).
694744
"""
695745

696746
examples = """
@@ -703,46 +753,84 @@ class UninstallNBExtensionApp(BaseExtensionApp):
703753
"nbextensions" : "UninstallNBExtensionApp.nbextensions_dir",
704754
"require": "UninstallNBExtensionApp.require",
705755
}
756+
flags = BaseExtensionApp.flags.copy()
757+
flags['system'] = ({'UninstallNBExtensionApp': {'system': True}},
758+
"Uninstall specifically from systemwide installation directory")
706759

707-
prefix = Unicode('', config=True, help="Installation prefix")
708-
nbextensions_dir = Unicode('', config=True, help="Full path to nbextensions dir (probably use prefix or user)")
709-
require = Unicode('', config=True, help="require.js module to load.")
760+
prefix = Unicode('', config=True,
761+
help="Installation prefix. Overrides --user, --sys-prefix and --system"
762+
)
763+
nbextensions_dir = Unicode('', config=True,
764+
help="Full path to nbextensions dir (probably use prefix or user)"
765+
)
766+
require = Unicode('', config=True, help="require.js module to disable loading")
767+
system = Bool(False, config=True,
768+
help="Uninstall specifically from systemwide installation directory"
769+
)
710770

711771
def _config_file_name_default(self):
712772
"""The default config file name."""
713773
return 'jupyter_notebook_config'
714-
715-
def uninstall_extensions(self):
716-
"""Uninstall some nbextensions"""
774+
775+
def uninstall_extension(self):
776+
"""Uninstall an nbextension from a specific location"""
717777
kwargs = {
718778
'user': self.user,
719779
'sys_prefix': self.sys_prefix,
720780
'prefix': self.prefix,
721781
'nbextensions_dir': self.nbextensions_dir,
722782
'logger': self.log
723783
}
724-
725-
arg_count = 1
726-
if len(self.extra_args) > arg_count:
727-
raise ValueError("only one nbextension allowed at a time. Call multiple times to uninstall multiple extensions.")
728-
if len(self.extra_args) < arg_count:
729-
raise ValueError("not enough arguments")
730-
784+
731785
if self.python:
732786
uninstall_nbextension_python(self.extra_args[0], **kwargs)
733787
else:
734788
if self.require:
735789
kwargs['require'] = self.require
736790
uninstall_nbextension(self.extra_args[0], **kwargs)
737-
791+
792+
def find_uninstall_extension(self):
793+
"""Uninstall an nbextension from an unspecified location"""
794+
name = self.extra_args[0]
795+
if self.python:
796+
_, nbexts = _get_nbextension_metadata(name)
797+
changed = False
798+
for nbext in nbexts:
799+
if _find_uninstall_nbextension(nbext['dest'], logger=self.log):
800+
changed = True
801+
802+
# Also disable it in config.
803+
for section in NBCONFIG_SECTIONS:
804+
_find_disable_nbextension(section, nbext['require'],
805+
logger=self.log)
806+
807+
else:
808+
changed = _find_uninstall_nbextension(name, logger=self.log)
809+
810+
if not changed:
811+
print("No installed extension %r found." % name)
812+
813+
if self.require:
814+
for section in NBCONFIG_SECTIONS:
815+
_find_disable_nbextension(section, self.require,
816+
logger=self.log)
817+
738818
def start(self):
739819
if not self.extra_args:
740820
sys.exit('Please specify an nbextension to uninstall')
741-
else:
821+
elif len(self.extra_args) > 1:
822+
sys.exit("Only one nbextension allowed at a time. "
823+
"Call multiple times to uninstall multiple extensions.")
824+
elif (self.user or self.sys_prefix or self.system or self.prefix
825+
or self.nbextensions_dir):
826+
# The user has specified a location from which to uninstall.
742827
try:
743-
self.uninstall_extensions()
828+
self.uninstall_extension()
744829
except ArgumentConflict as e:
745830
sys.exit(str(e))
831+
else:
832+
# Uninstall wherever it is.
833+
self.find_uninstall_extension()
746834

747835

748836
class ToggleNBExtensionApp(BaseExtensionApp):
@@ -826,7 +914,7 @@ class DisableNBExtensionApp(ToggleNBExtensionApp):
826914
"""An App that disables nbextensions"""
827915
name = "jupyter nbextension disable"
828916
description = """
829-
Enable an nbextension in frontend configuration.
917+
Disable an nbextension in frontend configuration.
830918
831919
Usage
832920
jupyter nbextension disable [--system|--sys-prefix]

0 commit comments

Comments
 (0)