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 all 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
235 changes: 159 additions & 76 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,126 +746,175 @@ 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['dest'], 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):
"""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] myext
"""
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(ToggleNBExtensionApp):
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]
jupyter nbextension disable [--user|--sys-prefix] myext
"""
_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="Locate the extension to disable from a Python package")

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

def _config_file_name_default(self):
"""The default config file name."""
return 'jupyter_notebook_config'

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(nbext['section'], nbext['require'],
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)

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):
"""A default format for messages"""
return "%(message)s"


class ListNBExtensionsApp(BaseExtensionApp):
Expand Down