More changes to the nbextensions.

This commit is contained in:
Brian E. Granger 2015-12-18 10:17:47 -08:00 committed by Jonathan Frederic
parent 49b7b8c071
commit b208d29d84

View File

@ -6,6 +6,8 @@
from __future__ import print_function
import io
import json
import os
import shutil
import sys
@ -20,63 +22,26 @@ except ImportError:
from urlparse import urlparse
from urllib import urlretrieve
from jupyter_core.paths import jupyter_data_dir, jupyter_path, SYSTEM_JUPYTER_PATH
from jupyter_core.paths import (
jupyter_data_dir, jupyter_path, jupyter_config_dir, SYSTEM_JUPYTER_PATH,
ENV_JUPYTER_PATH, ENV_CONFIG_PATH, SYSTEM_CONFIG_PATH
)
from ipython_genutils.path import ensure_dir_exists
from ipython_genutils.py3compat import string_types, cast_unicode_py2
from ipython_genutils.py3compat import string_types, cast_unicode_py2, PY3
from ipython_genutils.tempdir import TemporaryDirectory
from ._version import __version__
class ArgumentConflict(ValueError):
pass
def _should_copy(src, dest, verbose=1):
"""should a file be copied?"""
if not os.path.exists(dest):
return True
if os.stat(src).st_mtime - os.stat(dest).st_mtime > 1e-6:
# we add a fudge factor to work around a bug in python 2.x
# that was fixed in python 3.x: http://bugs.python.org/issue12904
if verbose >= 2:
print("%s is out of date" % dest)
return True
if verbose >= 2:
print("%s is up to date" % dest)
return False
#------------------------------------------------------------------------------
# Public API
#------------------------------------------------------------------------------
def _maybe_copy(src, dest, verbose=1):
"""copy a file if it needs updating"""
if _should_copy(src, dest, verbose):
if verbose >= 1:
print("copying %s -> %s" % (src, dest))
shutil.copy2(src, dest)
def _safe_is_tarfile(path):
"""safe version of is_tarfile, return False on IOError"""
try:
return tarfile.is_tarfile(path)
except IOError:
return False
def _get_nbext_dir(nbextensions_dir=None, user=False, prefix=None):
"""Return the nbextension directory specified"""
if sum(map(bool, [user, prefix, nbextensions_dir])) > 1:
raise ArgumentConflict("Cannot specify more than one of user, prefix, or nbextensions_dir.")
if user:
nbext = pjoin(jupyter_data_dir(), u'nbextensions')
else:
if prefix:
nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
elif nbextensions_dir:
nbext = nbextensions_dir
else:
nbext = pjoin(SYSTEM_JUPYTER_PATH[0], 'nbextensions')
return nbext
def check_nbextension(files, user=False, prefix=None, nbextensions_dir=None):
def check_nbextension(files, user=False, sys_prefix=False, prefix=None, nbextensions_dir=None):
"""Check whether nbextension files have been installed
Returns True if all files are found, False if any are missing.
@ -95,7 +60,7 @@ def check_nbextension(files, user=False, prefix=None, nbextensions_dir=None):
nbextensions_dir : str [optional]
Specify absolute path of nbextensions directory explicitly.
"""
nbext = _get_nbext_dir(nbextensions_dir, user, prefix)
nbext = _get_nbextension_dir(user=user, sys_prefix=sys_prefix, prefix=prefix, nbextensions_dir=nbextensions_dir)
# make sure nbextensions dir exists
if not os.path.exists(nbext):
return False
@ -107,7 +72,9 @@ def check_nbextension(files, user=False, prefix=None, nbextensions_dir=None):
return all(os.path.exists(pjoin(nbext, f)) for f in files)
def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, verbose=1):
def install_nbextension(path, overwrite=False, symlink=False,
user=False, sys_prefix=False, prefix=None, nbextensions_dir=None,
destination=None, verbose=1):
"""Install a Javascript extension for the notebook
Stages files and/or directories into the nbextensions directory.
@ -144,7 +111,7 @@ def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix
Set verbosity level. The default is 1, where file actions are printed.
set verbose=2 for more output, or verbose=0 for silence.
"""
nbext = _get_nbext_dir(nbextensions_dir, user, prefix)
nbext = _get_nbextension_dir(user=user, sys_prefix=sys_prefix, prefix=prefix, nbextensions_dir=nbextensions_dir)
# make sure nbextensions dir exists
ensure_dir_exists(nbext)
@ -219,8 +186,56 @@ def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix
src = path
_maybe_copy(src, full_dest, verbose)
def install_nbextension_python(package, overwrite=False, symlink=False,
user=False, sys_prefix=False, prefix=None, nbextensions_dir=None,
verbose=1):
"""Install an nbextension bundled in a Python package."""
m, nbexts = _get_nbextension_metadata(package)
base_path = os.path.split(m.__file__)[0]
for nbext in nbexts:
src = os.path.join(base_path, nbext['src'])
dest = nbext['dest']
require = nbext['require']
print(src, dest, require)
install_nbextension(src, overwrite=overwrite, symlink=symlink,
user=user, sys_prefix=sys_prefix, prefix=prefix, nbextensions_dir=nbextensions_dir,
destination=dest, verbose=verbose
)
def enable_nbextension_python(package, user=False, sys_prefix=False):
"""Enable an nbextension associated with a Python package."""
data = _read_config_data(user=user, sys_prefix=sys_prefix)
module, nbexts = _get_nbextension_metadata(package)
for nbext in nbexts:
require = nbext['require']
section = nbext['section']
if section == 'notebook':
diff = {'NotebookApp': {'nbextensions_notebook': {require: True}}}
elif section == 'tree':
diff = {'NotebookApp': {'nbextensions_tree': {require: True}}}
_recursive_update(data, diff)
_write_config_data(data, user=user, sys_prefix=sys_prefix)
def disable_nbextension_python(package, user=False, sys_prefix=False):
"""Disable an nbextension associated with a Python package."""
data = _read_config_data(user=user, sys_prefix=sys_prefix)
module, nbexts = _get_nbextension_metadata(package)
for nbext in nbexts:
require = nbext['require']
section = nbext['section']
if section == 'notebook':
diff = {'NotebookApp': {'nbextensions_notebook': {require: False}}}
elif section == 'tree':
diff = {'NotebookApp': {'nbextensions_tree': {require: False}}}
_recursive_update(data, diff)
_write_config_data(data, user=user, sys_prefix=sys_prefix)
#----------------------------------------------------------------------
# install nbextension app
# Applications
#----------------------------------------------------------------------
from traitlets import Bool, Enum, Unicode
@ -250,7 +265,17 @@ flags = {
"user" : ({
"InstallNBExtensionApp" : {
"user" : True,
}}, "Install to the user's IPython directory"
}}, "Install to the user's Jupyter directory"
),
"sys-prefix" : ({
"InstallNBExtensionApp" : {
"sys_prefix" : True,
}}, "Use sys.prefix as the prefix for installing nbextensions"
),
"python" : ({
"InstallNBExtensionApp" : {
"python" : True,
}}, "Install from a Python package"
),
}
flags['s'] = flags['symlink']
@ -286,9 +311,11 @@ class InstallNBExtensionApp(JupyterApp):
overwrite = Bool(False, config=True, help="Force overwrite of existing files")
symlink = Bool(False, config=True, help="Create symlinks instead of copying files")
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")
prefix = Unicode('', config=True, help="Installation prefix")
nbextensions_dir = Unicode('', config=True, help="Full path to nbextensions dir (probably use prefix or user)")
destination = Unicode('', config=True, help="Destination for the copy or symlink")
python = Bool(False, config=True, help="Install from a Python package")
verbose = Enum((0,1,2), default_value=1, config=True,
help="Verbosity level"
)
@ -299,14 +326,25 @@ class InstallNBExtensionApp(JupyterApp):
def install_extensions(self):
if len(self.extra_args)>1:
raise ValueError("only one nbextension allowed at a time. Call multiple times to install multiple extensions.")
install_nbextension(self.extra_args[0],
overwrite=self.overwrite,
symlink=self.symlink,
verbose=self.verbose,
user=self.user,
prefix=self.prefix,
destination=self.destination,
nbextensions_dir=self.nbextensions_dir,
if self.python:
install_nbextension_python(self.extra_args[0],
overwrite=self.overwrite,
symlink=self.symlink,
verbose=self.verbose,
user=self.user,
sys_prefix=self.sys_prefix,
prefix=self.prefix,
nbextensions_dir=self.nbextensions_dir
)
else:
install_nbextension(self.extra_args[0],
overwrite=self.overwrite,
symlink=self.symlink,
verbose=self.verbose,
user=self.user,
prefix=self.prefix,
destination=self.destination,
nbextensions_dir=self.nbextensions_dir,
)
def start(self):
@ -337,6 +375,8 @@ class EnableNBExtensionApp(JupyterApp):
aliases = {'section': 'EnableNBExtensionApp.section',
}
python = Bool(False, config=True, help="Install from a Python package")
def _config_file_name_default(self):
return 'jupyter_notebook_config'
@ -368,6 +408,8 @@ class DisableNBExtensionApp(JupyterApp):
aliases = {'section': 'DisableNBExtensionApp.section',
}
python = Bool(False, config=True, help="Install from a Python package")
def _config_file_name_default(self):
return 'jupyter_notebook_config'
@ -411,6 +453,122 @@ class NBExtensionApp(JupyterApp):
main = NBExtensionApp.launch_instance
#------------------------------------------------------------------------------
# Private API
#------------------------------------------------------------------------------
def _should_copy(src, dest, verbose=1):
"""should a file be copied?"""
if not os.path.exists(dest):
return True
if os.stat(src).st_mtime - os.stat(dest).st_mtime > 1e-6:
# we add a fudge factor to work around a bug in python 2.x
# that was fixed in python 3.x: http://bugs.python.org/issue12904
if verbose >= 2:
print("%s is out of date" % dest)
return True
if verbose >= 2:
print("%s is up to date" % dest)
return False
def _maybe_copy(src, dest, verbose=1):
"""Copy a file if it needs updating."""
if _should_copy(src, dest, verbose):
if verbose >= 1:
print("copying %s -> %s" % (src, dest))
shutil.copy2(src, dest)
def _safe_is_tarfile(path):
"""Safe version of is_tarfile, return False on IOError."""
try:
return tarfile.is_tarfile(path)
except IOError:
return False
def _get_nbextension_dir(user=False, sys_prefix=False, prefix=None, nbextensions_dir=None):
"""Return the nbextension directory specified"""
if sum(map(bool, [user, prefix, nbextensions_dir, sys_prefix])) > 1:
raise ArgumentConflict("cannot specify more than one of user, prefix, or nbextensions_dir")
if user:
nbext = pjoin(jupyter_data_dir(), u'nbextensions')
elif sys_prefix:
nbext = pjoin(ENV_JUPYTER_PATH[0], u'nbextensions')
elif prefix:
nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
elif nbextensions_dir:
nbext = nbextensions_dir
else:
nbext = pjoin(SYSTEM_JUPYTER_PATH[0], 'nbextensions')
return nbext
def _get_config_dir(user=False, sys_prefix=False):
if sum(map(bool, [user, sys_prefix])) > 1:
raise ArgumentConflict("cannot specify more than one of user or sys_prefix")
if user:
nbext = jupyter_config_dir()
elif sys_prefix:
nbext = ENV_CONFIG_PATH[0]
else:
nbext = SYSTEM_CONFIG_PATH[0]
return nbext
def _get_nbextension_metadata(package):
m = __import__(package)
if not hasattr(m, '_jupyter_nbextension_paths'):
raise KeyError('the python package {} is not a valid nbextension'.format(package))
nbexts = m._jupyter_nbextension_paths()
return m, nbexts
def _read_config_data(user=False, sys_prefix=False):
config_dir = _get_config_dir(user=user, sys_prefix=sys_prefix)
config_file = os.path.join(config_dir, 'jupyter_notebook_config.json')
# Read existing config data
if os.path.isfile(config_file):
with io.open(config_file, encoding='utf-8') as f:
return json.load(f)
else:
return {}
def _write_config_data(data, user=False, sys_prefix=False):
config_dir = _get_config_dir(user=user, sys_prefix=sys_prefix)
config_file = os.path.join(config_dir, 'jupyter_notebook_config.json')
ensure_dir_exists(config_dir)
if PY3:
f = io.open(config_file, 'w', encoding='utf-8')
else:
f = open(config_file, 'wb')
with f:
json.dump(data, f, indent=2)
def _recursive_update(target, new):
"""Recursively update one dictionary using another.
None values will delete their keys.
"""
for k, v in new.items():
if isinstance(v, dict):
if k not in target:
target[k] = {}
_recursive_update(target[k], v)
if not target[k]:
# Prune empty subdicts
del target[k]
elif v is None:
target.pop(k, None)
else:
target[k] = v
if __name__ == '__main__':
main()