Skip to content

Commit add7c44

Browse files
committed
Uninstall an nbextension from the first location it is found
Closes gh-2725 (see discussion on that PR)
1 parent fcc5b0f commit add7c44

File tree

1 file changed

+102
-18
lines changed

1 file changed

+102
-18
lines changed

notebook/nbextensions.py

Lines changed: 102 additions & 18 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,16 @@ 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
694740
"""
695741

696742
examples = """
@@ -703,46 +749,84 @@ class UninstallNBExtensionApp(BaseExtensionApp):
703749
"nbextensions" : "UninstallNBExtensionApp.nbextensions_dir",
704750
"require": "UninstallNBExtensionApp.require",
705751
}
752+
flags = BaseExtensionApp.flags.copy()
753+
flags['system'] = ({'UninstallNBExtensionApp': {'system': True}},
754+
"Uninstall specifically from systemwide installation directory")
706755

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.")
756+
prefix = Unicode('', config=True,
757+
help="Installation prefix. Overrides --user, --sys-prefix and --system"
758+
)
759+
nbextensions_dir = Unicode('', config=True,
760+
help="Full path to nbextensions dir (probably use prefix or user)"
761+
)
762+
require = Unicode('', config=True, help="require.js module to disable loading")
763+
system = Bool(False, config=True,
764+
help="Uninstall specifically from systemwide installation directory"
765+
)
710766

711767
def _config_file_name_default(self):
712768
"""The default config file name."""
713769
return 'jupyter_notebook_config'
714-
715-
def uninstall_extensions(self):
716-
"""Uninstall some nbextensions"""
770+
771+
def uninstall_extension(self):
772+
"""Uninstall an nbextension from a specific location"""
717773
kwargs = {
718774
'user': self.user,
719775
'sys_prefix': self.sys_prefix,
720776
'prefix': self.prefix,
721777
'nbextensions_dir': self.nbextensions_dir,
722778
'logger': self.log
723779
}
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-
780+
731781
if self.python:
732782
uninstall_nbextension_python(self.extra_args[0], **kwargs)
733783
else:
734784
if self.require:
735785
kwargs['require'] = self.require
736786
uninstall_nbextension(self.extra_args[0], **kwargs)
737-
787+
788+
def find_uninstall_extension(self):
789+
"""Uninstall an nbextension from an unspecified location"""
790+
name = self.extra_args[0]
791+
if self.python:
792+
_, nbexts = _get_nbextension_metadata(name)
793+
changed = False
794+
for nbext in nbexts:
795+
if _find_uninstall_nbextension(nbext['dest'], logger=self.log):
796+
changed = True
797+
798+
# Also disable it in config.
799+
for section in NBCONFIG_SECTIONS:
800+
_find_disable_nbextension(section, nbext['require'],
801+
logger=self.log)
802+
803+
else:
804+
changed = _find_uninstall_nbextension(name, logger=self.log)
805+
806+
if not changed:
807+
print("No installed extension %r found." % name)
808+
809+
if self.require:
810+
for section in NBCONFIG_SECTIONS:
811+
_find_disable_nbextension(section, self.require,
812+
logger=self.log)
813+
738814
def start(self):
739815
if not self.extra_args:
740816
sys.exit('Please specify an nbextension to uninstall')
741-
else:
817+
elif len(self.extra_args) > 1:
818+
sys.exit("Only one nbextension allowed at a time. "
819+
"Call multiple times to uninstall multiple extensions.")
820+
elif (self.user or self.sys_prefix or self.system or self.prefix
821+
or self.nbextensions_dir):
822+
# The user has specified a location from which to uninstall.
742823
try:
743-
self.uninstall_extensions()
824+
self.uninstall_extension()
744825
except ArgumentConflict as e:
745826
sys.exit(str(e))
827+
else:
828+
# Uninstall wherever it is.
829+
self.find_uninstall_extension()
746830

747831

748832
class ToggleNBExtensionApp(BaseExtensionApp):

0 commit comments

Comments
 (0)