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

switchable install #774

Merged
merged 2 commits into from
Oct 21, 2016
Merged
Show file tree
Hide file tree
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
88 changes: 57 additions & 31 deletions src/jupyter_contrib_nbextensions/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from __future__ import print_function, unicode_literals

import copy
import sys

from jupyter_contrib_core.notebook_compat.nbextensions import ArgumentConflict
Expand All @@ -11,7 +12,9 @@
from traitlets import Bool, Unicode, default

import jupyter_contrib_nbextensions
from jupyter_contrib_nbextensions.install import install, uninstall
from jupyter_contrib_nbextensions.install import (
install, toggle_install_config, toggle_install_files, uninstall,
)
from jupyter_contrib_nbextensions.migrate import migrate


Expand All @@ -37,7 +40,7 @@ def _log_format_default(self):


class BaseContribNbextensionsInstallApp(BaseContribNbextensionsApp):
"""Base jupyter_contrib_nbextensions (un)installer app."""
"""Install/Uninstall jupyter_contrib_nbextensions."""

aliases = {
'prefix': 'BaseContribNbextensionsInstallApp.prefix',
Expand Down Expand Up @@ -70,6 +73,8 @@ class BaseContribNbextensionsInstallApp(BaseContribNbextensionsApp):
),
}

_conflicting_flagsets = [['--user', '--system', '--sys-prefix'], ]

user = Bool(False, config=True, help='Whether to do a user install')
sys_prefix = Bool(False, config=True,
help='Use the sys.prefix as the prefix')
Expand All @@ -93,11 +98,11 @@ def parse_command_line(self, argv=None):

Since notebook version doesn't do it very well
"""
conflicting_flags = set(['--user', '--system', '--sys-prefix'])

if len(conflicting_flags.intersection(set(argv))) > 1:
raise ArgumentConflict(
'cannot specify more than one of user, sys_prefix, or system')
for conflicting_flags in map(set, self._conflicting_flagsets):
if len(conflicting_flags.intersection(set(argv))) > 1:
raise ArgumentConflict(
'cannot specify more than one of {}'.format(
', '.join(conflicting_flags)))
return super(BaseContribNbextensionsInstallApp,
self).parse_command_line(argv)

Expand All @@ -106,40 +111,61 @@ def parse_command_line(self, argv=None):


class InstallContribNbextensionsApp(BaseContribNbextensionsInstallApp):
"""Install all jupyter_contrib_nbextensions."""
"""Install jupyter_contrib_nbextensions."""

name = 'jupyter contrib nbextension install'
description = (
'Install all jupyter_contrib_nbextensions.'
)
_toggle_value = True # whether to install or uninstall

def start(self):
"""Perform the App's actions as configured."""
if self.extra_args:
sys.exit('{} takes no extra arguments'.format(self.name))
self.log.info('{} {}'.format(self.name, ' '.join(self.argv)))
return install(
user=self.user, sys_prefix=self.sys_prefix, prefix=self.prefix,
nbextensions_dir=self.nbextensions_dir, logger=self.log,
overwrite=self.overwrite, symlink=self.symlink)
flags = copy.deepcopy(BaseContribNbextensionsInstallApp.flags)
flags.update({
'only-config': (
{'BaseContribNbextensionsInstallApp': {'only_config': True}},
'Edit config files, but do not install/remove nbextensions files'
),
'only-files': (
{'BaseContribNbextensionsInstallApp': {'only_files': True}},
'Install/remove nbextensions files, but do not edit config files'
),
})

_conflicting_flagsets = (
BaseContribNbextensionsInstallApp._conflicting_flagsets +
['--only-config', '--only-files'])

class UninstallContribNbextensionsApp(BaseContribNbextensionsInstallApp):
"""Uninstall all jupyter_contrib_nbextensions."""
only_config = Bool(False, config=True, help=(
'Edit config files, but do not install/remove nbextensions files'))
only_files = Bool(False, config=True, help=(
'Install/remove nbextensions files, but do not edit config files'))

name = 'jupyter contrib nbextension uninstall'
description = (
'Uninstall all jupyter_contrib_nbextensions.'
)
@property
def name(self):
return 'jupyter contrib nbextension {}'.format(
'install' if self._toggle_value else 'uninstall')

@property
def description(self):
return '{} jupyter_contrib_nbextensions.'.format(
'Install' if self._toggle_value else 'Uninstall')

def start(self):
"""Perform the App's actions as configured."""
if self.extra_args:
sys.exit('{} takes no extra arguments'.format(self.name))
self.log.info('{} {}'.format(self.name, ' '.join(self.argv)))
return uninstall(
user=self.user, sys_prefix=self.sys_prefix, prefix=self.prefix,
nbextensions_dir=self.nbextensions_dir, logger=self.log)
kwargs = dict(
user=self.user, sys_prefix=self.sys_prefix, logger=self.log)
kwargs_files = dict(**kwargs)
kwargs_files.update(dict(
prefix=self.prefix, nbextensions_dir=self.nbextensions_dir,
overwrite=self.overwrite, symlink=self.symlink))
if not self.only_config:
toggle_install_files(self._toggle_value, **kwargs_files)
if not self.only_files:
toggle_install_config(self._toggle_value, **kwargs)


class UninstallContribNbextensionsApp(InstallContribNbextensionsApp):
"""Uninstall jupyter_contrib_nbextensions."""
_toggle_value = False


class MigrateContribNbextensionsApp(BaseContribNbextensionsInstallApp):
Expand Down Expand Up @@ -169,7 +195,7 @@ class ContribNbextensionsApp(BaseContribNbextensionsApp):
examples = '\n'.join(['jupyter contrib nbextension ' + t for t in [
'install # {}'.format(install.__doc__),
'uninstall # {}'.format(uninstall.__doc__),
'migrate # {}'.format(migrate.__doc__),
'migrate # {}'.format(migrate.__doc__),
]])
subcommands = dict(
install=(InstallContribNbextensionsApp, install.__doc__),
Expand Down
84 changes: 62 additions & 22 deletions src/jupyter_contrib_nbextensions/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,41 +51,81 @@ def notebook_is_running():
def toggle_install(install, user=False, sys_prefix=False, overwrite=False,
symlink=False, prefix=None, nbextensions_dir=None,
logger=None):
"""Install or remove all jupyter_contrib_nbextensions."""
"""Install or remove all jupyter_contrib_nbextensions files & config."""
if notebook_is_running():
raise NotebookRunningError(
'Cannot configure while the Jupyter notebook server is running')
_check_conflicting_kwargs(user=user, sys_prefix=sys_prefix, prefix=prefix,
nbextensions_dir=nbextensions_dir)
config_dir = nbextensions._get_config_dir(user=user, sys_prefix=sys_prefix)

verb = 'Installing' if install else 'Uninstalling'
toggle_install_files(
install, user=user, sys_prefix=sys_prefix, overwrite=overwrite,
symlink=symlink, prefix=prefix, nbextensions_dir=nbextensions_dir,
logger=logger)
toggle_install_config(
install, user=user, sys_prefix=sys_prefix, logger=logger)


def toggle_install_files(install, user=False, sys_prefix=False, logger=None,
overwrite=False, symlink=False, prefix=None,
nbextensions_dir=None):
"""Install/remove jupyter_contrib_nbextensions files."""
if notebook_is_running():
raise NotebookRunningError(
'Cannot configure while the Jupyter notebook server is running')
kwargs = dict(user=user, sys_prefix=sys_prefix, prefix=prefix,
nbextensions_dir=nbextensions_dir)
_check_conflicting_kwargs(**kwargs)
kwargs['logger'] = logger
if logger:
logger.info(
'{} jupyter_contrib_nbextensions, using config in {}'.format(
verb, config_dir))

# Configure the jupyter_nbextensions_configurator serverextension to load
if install:
conf_app = EnableJupyterNbextensionsConfiguratorApp(
user=user, sys_prefix=sys_prefix, symlink=symlink, logger=logger)
conf_app.start()

# nbextensions:
kwargs = dict(user=user, sys_prefix=sys_prefix, prefix=prefix,
nbextensions_dir=nbextensions_dir, logger=logger)
'{} jupyter_contrib_nbextensions nbextension files {} {}'.format(
'Installing' if install else 'Uninstalling',
'to' if install else 'from',
'jupyter data directory'))
if install:
nbextensions.install_nbextension_python(
jupyter_contrib_nbextensions.__name__,
overwrite=overwrite, symlink=symlink, **kwargs)
# enable contrib_nbextensions_help_item (item in help menu)
nbextensions.enable_nbextension('notebook',
'contrib_nbextensions_help_item/main',
user=user, sys_prefix=sys_prefix)
else:
nbextensions.uninstall_nbextension_python(
jupyter_contrib_nbextensions.__name__, **kwargs)


def toggle_install_config(install, user=False, sys_prefix=False, logger=None):
"""Install/remove contrib nbextensions to/from jupyter_nbconvert_config."""
if notebook_is_running():
raise NotebookRunningError(
'Cannot configure while the Jupyter notebook server is running')
_check_conflicting_kwargs(user=user, sys_prefix=sys_prefix)
config_dir = nbextensions._get_config_dir(user=user, sys_prefix=sys_prefix)
if logger:
logger.info(
'{} jupyter_contrib_nbextensions items {} config in {}'.format(
'Installing' if install else 'Uninstalling',
'to' if install else 'from',
config_dir))

# Configure the jupyter_nbextensions_configurator serverextension to load
if install:
configurator_app = EnableJupyterNbextensionsConfiguratorApp(
user=user, sys_prefix=sys_prefix, logger=logger)
configurator_app.start()
nbextensions.enable_nbextension(
'notebook', 'contrib_nbextensions_help_item/main',
user=user, sys_prefix=sys_prefix, logger=logger)
else:
nbconf_cm = BaseJSONConfigManager(
config_dir=os.path.join(config_dir, 'nbconfig'))
for require, section in {
'contrib_nbextensions_help_item/main': 'notebook'}.items():
if logger:
logger.info('- Disabling {}'.format(require))
logger.info(
'-- Editing config: {}'.format(
nbconf_cm.file_name(section)))
# disabled_conf['load_extensions'][require] = None
nbconf_cm.update('notebook', {'load_extensions': {require: None}})

# Set extra template path, pre- and post-processors for nbconvert
cm = BaseJSONConfigManager(config_dir=config_dir)
config_basename = 'jupyter_nbconvert_config'
Expand Down Expand Up @@ -120,7 +160,7 @@ def toggle_install(install, user=False, sys_prefix=False, overwrite=False,

def install(user=False, sys_prefix=False, prefix=None, nbextensions_dir=None,
logger=None, overwrite=False, symlink=False):
"""Edit jupyter config files to use jupyter_contrib_nbextensions things."""
"""Install all jupyter_contrib_nbextensions files & config."""
return toggle_install(
True, user=user, sys_prefix=sys_prefix, prefix=prefix,
nbextensions_dir=nbextensions_dir, logger=logger,
Expand All @@ -129,7 +169,7 @@ def install(user=False, sys_prefix=False, prefix=None, nbextensions_dir=None,

def uninstall(user=False, sys_prefix=False, prefix=None, nbextensions_dir=None,
logger=None):
"""Edit jupyter config files to not use jupyter_contrib_nbextensions."""
"""Uninstall all jupyter_contrib_nbextensions files & config."""
return toggle_install(
False, user=user, sys_prefix=sys_prefix, prefix=prefix,
nbextensions_dir=nbextensions_dir, logger=logger)
Expand Down
30 changes: 26 additions & 4 deletions tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,24 @@ def _check_subproc(self, args):
retcode = proc.poll()
nt.assert_equal(retcode, 0, 'command should exit with code 0')

def check_app_install(self, argv=None, dirs=None):
def check_app_install(self, argv=None, dirs=None, dirs_install=None):
"""Check files were installed in the correct place."""
argv, dirs = self._get_default_check_kwargs(argv, dirs)
if dirs_install is None:
dirs_install = dirs
self._call_main_app(argv=['install'] + argv)
installed_files = self._check_install(dirs)
installed_files = self._check_install(dirs_install)
self._call_main_app(argv=['uninstall'] + argv)
self._check_uninstall(dirs, installed_files)

def check_cli_install(self, argv=None, dirs=None,
def check_cli_install(self, argv=None, dirs=None, dirs_install=None,
app_name='jupyter contrib nbextension'):
argv, dirs = self._get_default_check_kwargs(argv, dirs)
if dirs_install is None:
dirs_install = dirs
args = app_name.split(' ') + ['install'] + argv
self._check_subproc(args)
installed_files = self._check_install(dirs)
installed_files = self._check_install(dirs_install)
args = app_name.split(' ') + ['uninstall'] + argv
self._check_subproc(args)
self._check_uninstall(dirs, installed_files)
Expand All @@ -183,6 +187,8 @@ def test_01_help_output(self):
# sys.exit should be called if empty argv specified
with nt.assert_raises(SystemExit):
main_app([])
for klass in app_classes:
klass.clear_instance()

def test_02_argument_conflict(self):
"""Check that install objects to multiple flags."""
Expand All @@ -196,6 +202,8 @@ def test_02_argument_conflict(self):
self.log.info('testing conflicting flagset {}'.format(flagset))
nt.assert_raises(nbextensions.ArgumentConflict,
main_app, [subcommand] + list(flagset))
for klass in app_classes:
klass.clear_instance()

def test_03_app_install_defaults(self):
"""Check that app install works correctly using defaults."""
Expand Down Expand Up @@ -260,3 +268,17 @@ def test_13_app_install_prefix(self):
dirs = self._get_default_check_kwargs()[1]
dirs['data'] = self.jupyter_dirs['custom']['data']
self.check_app_install(dirs=dirs, argv=['--prefix=' + dirs['data']])

def test_14_app_install_only_files(self):
"""Check that install works correctly using --only-files flag."""
argv, dirs = self._get_default_check_kwargs()
self.check_app_install(
argv=argv + ['--only-files'], dirs=dirs,
dirs_install={'data': dirs['data']})

def test_15_app_install_only_config(self):
"""Check that install works correctly using --only-config flag."""
argv, dirs = self._get_default_check_kwargs()
self.check_app_install(
argv=argv + ['--only-config'], dirs=dirs,
dirs_install={'conf': dirs['conf']})