Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable nbextension in first location found #2725

Closed
wants to merge 5 commits into from
Closed
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 123 additions & 17 deletions notebook/nbextensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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

Expand Down Expand Up @@ -241,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.
Expand Down Expand Up @@ -278,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,
Expand Down Expand Up @@ -419,6 +437,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):
Expand Down Expand Up @@ -712,37 +746,56 @@ 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,
'prefix': self.prefix,
'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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍



class ToggleNBExtensionApp(BaseExtensionApp):
Expand Down Expand Up @@ -822,7 +875,7 @@ class EnableNBExtensionApp(ToggleNBExtensionApp):
_toggle_value = True


class DisableNBExtensionApp(ToggleNBExtensionApp):
class DisableNBExtensionApp(JupyterApp):
"""An App that disables nbextensions"""
name = "jupyter nbextension disable"
description = """
Expand All @@ -831,7 +884,60 @@ 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"),
"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': '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")

section = Unicode('notebook', config=True,
help="""Which config section to disable the extension in."""
)

def start(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get away with only overriding start, instead of losing the ToggleNBExtensionApp inheritance?

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
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):
Expand Down