allow system-wide paths for nbextensions

like kernel specs, default install is `/usr/local/share/jupyter/nbextensions`

add `--user` for .ipython/nbextensions path
Min RK 11 years ago
parent 632b423752
commit f3da69c38d

@ -8,6 +8,7 @@ from __future__ import print_function
import os
import shutil
import sys
import tarfile
import zipfile
from os.path import basename, join as pjoin
@ -25,6 +26,34 @@ from IPython.utils.py3compat import string_types, cast_unicode_py2
from IPython.utils.tempdir import TemporaryDirectory
# Packagers: modify the next block if you store system-installed nbextensions elsewhere (unlikely)
SYSTEM_NBEXTENSIONS_DIRS = []
if os.name == 'nt':
programdata = os.environ.get('PROGRAMDATA', None)
if programdata: # PROGRAMDATA is not defined by default on XP.
SYSTEM_NBEXTENSIONS_DIRS = [pjoin(programdata, 'jupyter', 'nbextensions')]
prefixes = []
else:
prefixes = ['/usr/local', '/usr']
# add sys.prefix at the front
if sys.prefix not in prefixes:
prefixes.insert(0, sys.prefix)
for prefix in prefixes:
nbext = os.path.join(prefix, 'share', 'jupyter', 'nbextensions')
if nbext not in SYSTEM_NBEXTENSIONS_DIRS:
SYSTEM_NBEXTENSIONS_DIRS.append(nbext)
if os.name == 'nt':
# PROGRAMDATA
SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-1]
else:
# /usr/local
SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-2]
def _should_copy(src, dest, verbose=1):
"""should a file be copied?"""
if not os.path.exists(dest):
@ -54,15 +83,17 @@ def _safe_is_tarfile(path):
return False
def check_nbextension(files, ipython_dir=None):
def check_nbextension(files, nbextensions=None):
"""Check whether nbextension files have been installed
files should be a list of relative paths within nbextensions.
Returns True if all files are found, False if any are missing.
"""
ipython_dir = ipython_dir or get_ipython_dir()
nbext = pjoin(ipython_dir, u'nbextensions')
if nbextensions:
nbext = nbextensions
else:
nbext = pjoin(get_ipython_dir(), u'nbextensions')
# make sure nbextensions dir exists
if not os.path.exists(nbext):
return False
@ -74,7 +105,7 @@ def check_nbextension(files, ipython_dir=None):
return all(os.path.exists(pjoin(nbext, f)) for f in files)
def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None, verbose=1):
def install_nbextension(files, overwrite=False, symlink=False, user=False, prefix=None, nbextensions=None, verbose=1):
"""Install a Javascript extension for the notebook
Stages files and/or directories into IPYTHONDIR/nbextensions.
@ -96,16 +127,29 @@ def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None,
Not allowed with URLs or archives. Windows support for symlinks requires
Vista or above, Python 3, and a permission bit which only admin users
have by default, so don't rely on it.
ipython_dir : str [optional]
The path to an IPython directory, if the default value is not desired.
get_ipython_dir() is used by default.
user : bool [default: False]
Whether to install to the user's .ipython/nbextensions directory.
Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
prefix : str [optional]
Specify install prefix, if it should differ from default (e.g. /usr/local).
Will install to prefix/share/jupyter/nbextensions
nbextensions : str [optional]
Specify absolute path of nbextensions directory explicitly.
verbose : int [default: 1]
Set verbosity level. The default is 1, where file actions are printed.
set verbose=2 for more output, or verbose=0 for silence.
"""
ipython_dir = ipython_dir or get_ipython_dir()
nbext = pjoin(ipython_dir, u'nbextensions')
if sum(map(bool, [user, prefix, nbextensions])) > 1:
raise ValueError("Cannot specify more than one of user, prefix, or nbextensions.")
if user:
nbext = pjoin(get_ipython_dir(), u'nbextensions')
else:
if prefix:
nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
elif nbextensions:
nbext = nbextensions
else:
nbext = SYSTEM_NBEXTENSIONS_INSTALL_DIR
# make sure nbextensions dir exists
ensure_dir_exists(nbext)
@ -126,7 +170,7 @@ def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None,
print("downloading %s to %s" % (path, local_path))
urlretrieve(path, local_path)
# now install from the local copy
install_nbextension(local_path, overwrite, symlink, ipython_dir, verbose)
install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions=nbext, verbose=verbose)
continue
# handle archives
@ -183,7 +227,7 @@ def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None,
# install nbextension app
#----------------------------------------------------------------------
from IPython.utils.traitlets import Bool, Enum
from IPython.utils.traitlets import Bool, Enum, Unicode, TraitError
from IPython.core.application import BaseIPythonApplication
flags = {
@ -207,11 +251,18 @@ flags = {
"symlink" : True,
}}, "Create symlinks instead of copying files"
),
"user" : ({
"NBExtensionApp" : {
"user" : True,
}}, "Install to the user's IPython directory"
),
}
flags['s'] = flags['symlink']
aliases = {
"ipython-dir" : "NBExtensionApp.ipython_dir"
"ipython-dir" : "NBExtensionApp.ipython_dir",
"prefix" : "NBExtensionApp.prefix",
"nbextensions" : "NBExtensionApp.nbextensions",
}
class NBExtensionApp(BaseIPythonApplication):
@ -238,25 +289,36 @@ class NBExtensionApp(BaseIPythonApplication):
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")
prefix = Unicode('', config=True, help="Installation prefix")
nbextensions = Unicode('', config=True, help="Full path to nbextensions (probably use prefix or user)")
verbose = Enum((0,1,2), default_value=1, config=True,
help="Verbosity level"
)
def check_install():
if sum(map(bool, [user, prefix, nbextensions])) > 1:
raise TraitError("Cannot specify more than one of user, prefix, or nbextensions.")
def install_extensions(self):
install_nbextension(self.extra_args,
overwrite=self.overwrite,
symlink=self.symlink,
verbose=self.verbose,
ipython_dir=self.ipython_dir,
user=self.user,
prefix=self.prefix,
nbextensions=self.nbextensions,
)
def start(self):
if not self.extra_args:
nbext = pjoin(self.ipython_dir, u'nbextensions')
print("Notebook extensions in %s:" % nbext)
for ext in os.listdir(nbext):
print(u" %s" % ext)
for nbext in [pjoin(self.ipython_dir, u'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS:
if os.path.exists(nbext):
print("Notebook extensions in %s:" % nbext)
for ext in os.listdir(nbext):
print(u" %s" % ext)
else:
self.check_install()
self.install_extensions()

@ -90,6 +90,7 @@ from IPython.utils import py3compat
from IPython.utils.path import filefind, get_ipython_dir
from IPython.utils.sysinfo import get_sys_info
from .nbextensions import SYSTEM_NBEXTENSIONS_DIRS
from .utils import url_path_join
#-----------------------------------------------------------------------------
@ -566,11 +567,14 @@ class NotebookApp(BaseIPythonApplication):
"""return extra paths + the default locations"""
return self.extra_template_paths + DEFAULT_TEMPLATE_PATH_LIST
nbextensions_path = List(Unicode, config=True,
help="""paths for Javascript extensions. By default, this is just IPYTHONDIR/nbextensions"""
extra_nbextensions_path = List(Unicode, config=True,
help="""extra paths to look for Javascript notebook extensions"""
)
def _nbextensions_path_default(self):
return [os.path.join(get_ipython_dir(), 'nbextensions')]
@property
def nbextensions_path(self):
"""The path to look for Javascript notebook extensions"""
return self.extra_nbextensions_path + [os.path.join(get_ipython_dir(), 'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS
websocket_url = Unicode("", config=True,
help="""The base URL for websockets,

@ -1,15 +1,8 @@
# coding: utf-8
"""Test installation of notebook extensions"""
#-----------------------------------------------------------------------------
# Copyright (C) 2014 The IPython Development Team
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import glob
import os
@ -27,9 +20,6 @@ from IPython.utils.tempdir import TemporaryDirectory
from IPython.html import nbextensions
from IPython.html.nbextensions import install_nbextension, check_nbextension
#-----------------------------------------------------------------------------
# Test functions
#-----------------------------------------------------------------------------
def touch(file, mtime=None):
"""ensure a file exists, and set its modification time
@ -43,7 +33,6 @@ def touch(file, mtime=None):
os.utime(file, (atime, mtime))
return os.stat(file).st_mtime
class TestInstallNBExtension(TestCase):
def tempdir(self):
@ -69,12 +58,15 @@ class TestInstallNBExtension(TestCase):
self.ipdir = self.tempdir()
self.save_get_ipython_dir = nbextensions.get_ipython_dir
nbextensions.get_ipython_dir = lambda : self.ipdir
self.save_system_dir = nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR
nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = self.tempdir()
def tearDown(self):
nbextensions.get_ipython_dir = self.save_get_ipython_dir
nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.save_system_dir
for td in self.tempdirs:
td.cleanup()
nbextensions.get_ipython_dir = self.save_get_ipython_dir
def assert_dir_exists(self, path):
if not os.path.exists(path):
do_exist = os.listdir(os.path.dirname(path))
@ -84,21 +76,29 @@ class TestInstallNBExtension(TestCase):
if os.path.exists(path):
self.fail(u"%s should not exist" % path)
def assert_installed(self, relative_path, ipdir=None):
def assert_installed(self, relative_path, user=False):
if user:
nbext = pjoin(self.ipdir, u'nbextensions')
else:
nbext = self.system_nbext
self.assert_dir_exists(
pjoin(ipdir or self.ipdir, u'nbextensions', relative_path)
pjoin(nbext, relative_path)
)
def assert_not_installed(self, relative_path, ipdir=None):
def assert_not_installed(self, relative_path, user=False):
if user:
nbext = pjoin(self.ipdir, u'nbextensions')
else:
nbext = self.system_nbext
self.assert_not_dir_exists(
pjoin(ipdir or self.ipdir, u'nbextensions', relative_path)
pjoin(nbext, relative_path)
)
def test_create_ipython_dir(self):
"""install_nbextension when ipython_dir doesn't exist"""
with TemporaryDirectory() as td:
ipdir = pjoin(td, u'ipython')
install_nbextension(self.src, ipython_dir=ipdir)
self.ipdir = ipdir = pjoin(td, u'ipython')
install_nbextension(self.src, user=True)
self.assert_dir_exists(ipdir)
for file in self.files:
self.assert_installed(
@ -106,12 +106,22 @@ class TestInstallNBExtension(TestCase):
ipdir
)
def test_create_nbextensions(self):
with TemporaryDirectory() as ipdir:
install_nbextension(self.src, ipython_dir=ipdir)
def test_create_nbextensions_user(self):
with TemporaryDirectory() as td:
self.ipdir = ipdir = pjoin(td, u'ipython')
install_nbextension(self.src, user=True)
self.assert_installed(
pjoin(basename(self.src), u'ƒile'),
user=True
)
def test_create_nbextensions_system(self):
with TemporaryDirectory() as td:
nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = pjoin(td, u'nbextensions')
install_nbextension(self.src, user=False)
self.assert_installed(
pjoin(basename(self.src), u'ƒile'),
ipdir
user=False
)
def test_single_file(self):
@ -136,7 +146,7 @@ class TestInstallNBExtension(TestCase):
with open(src, 'w') as f:
f.write('first')
mtime = touch(src)
dest = pjoin(self.ipdir, u'nbextensions', fname)
dest = pjoin(self.system_nbext, fname)
install_nbextension(src)
with open(src, 'w') as f:
f.write('overwrite')
@ -147,7 +157,6 @@ class TestInstallNBExtension(TestCase):
def test_overwrite_dir(self):
with TemporaryDirectory() as src:
# src = py3compat.cast_unicode_py2(src)
base = basename(src)
fname = u'ƒ.js'
touch(pjoin(src, fname))
@ -169,7 +178,7 @@ class TestInstallNBExtension(TestCase):
mtime = touch(src)
install_nbextension(src)
self.assert_installed(fname)
dest = pjoin(self.ipdir, u'nbextensions', fname)
dest = pjoin(self.system_nbext, fname)
old_mtime = os.stat(dest).st_mtime
with open(src, 'w') as f:
f.write('overwrite')
@ -185,7 +194,7 @@ class TestInstallNBExtension(TestCase):
mtime = touch(src)
install_nbextension(src)
self.assert_installed(fname)
dest = pjoin(self.ipdir, u'nbextensions', fname)
dest = pjoin(self.system_nbext, fname)
old_mtime = os.stat(dest).st_mtime
mtime = touch(src, mtime - 100)
@ -239,11 +248,12 @@ class TestInstallNBExtension(TestCase):
f = u'ƒ.js'
src = pjoin(d, f)
touch(src)
install_nbextension(src)
install_nbextension(src, user=True)
assert check_nbextension(f, self.ipdir)
assert check_nbextension([f], self.ipdir)
assert not check_nbextension([f, pjoin('dne', f)], self.ipdir)
nbext = pjoin(self.ipdir, u'nbextensions')
assert check_nbextension(f, nbext)
assert check_nbextension([f], nbext)
assert not check_nbextension([f, pjoin('dne', f)], nbext)
@dec.skip_win32
def test_install_symlink(self):
@ -252,7 +262,7 @@ class TestInstallNBExtension(TestCase):
src = pjoin(d, f)
touch(src)
install_nbextension(src, symlink=True)
dest = pjoin(self.ipdir, u'nbextensions', f)
dest = pjoin(self.system_nbext, f)
assert os.path.islink(dest)
link = os.readlink(dest)
self.assertEqual(link, src)

Loading…
Cancel
Save