mirror of
https://github.com/jupyter/notebook.git
synced 2025-01-24 12:05:22 +08:00
Merge pull request #7609 from jasongrout/install-single-nbextension
Change install_nbextension to take install only a single nbextension, with an optional destination
This commit is contained in:
commit
c571fa545d
@ -134,7 +134,7 @@ def check_nbextension(files, user=False, prefix=None, nbextensions_dir=None):
|
|||||||
return all(os.path.exists(pjoin(nbext, f)) for f in files)
|
return all(os.path.exists(pjoin(nbext, f)) for f in files)
|
||||||
|
|
||||||
|
|
||||||
def install_nbextension(files, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, verbose=1):
|
def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, verbose=1):
|
||||||
"""Install a Javascript extension for the notebook
|
"""Install a Javascript extension for the notebook
|
||||||
|
|
||||||
Stages files and/or directories into the nbextensions directory.
|
Stages files and/or directories into the nbextensions directory.
|
||||||
@ -144,11 +144,9 @@ def install_nbextension(files, overwrite=False, symlink=False, user=False, prefi
|
|||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|
||||||
files : list(paths or URLs) or dict(install_name: path or URL)
|
path : path to file, directory, zip or tarball archive, or URL to install
|
||||||
One or more paths or URLs to existing files directories to install.
|
By default, the file will be installed with its base name, so '/path/to/foo'
|
||||||
If given as a list, these will be installed with their base name, so '/path/to/foo'
|
will install to 'nbextensions/foo'. See the destination argument below to change this.
|
||||||
will install to 'nbextensions/foo'. If given as a dict, such as {'bar': '/path/to/foo'},
|
|
||||||
then '/path/to/foo' will install to 'nbextensions/bar'.
|
|
||||||
Archives (zip or tarballs) will be extracted into the nbextensions directory.
|
Archives (zip or tarballs) will be extracted into the nbextensions directory.
|
||||||
overwrite : bool [default: False]
|
overwrite : bool [default: False]
|
||||||
If True, always install the files, regardless of what may already be installed.
|
If True, always install the files, regardless of what may already be installed.
|
||||||
@ -165,6 +163,10 @@ def install_nbextension(files, overwrite=False, symlink=False, user=False, prefi
|
|||||||
Will install to prefix/share/jupyter/nbextensions
|
Will install to prefix/share/jupyter/nbextensions
|
||||||
nbextensions_dir : str [optional]
|
nbextensions_dir : str [optional]
|
||||||
Specify absolute path of nbextensions directory explicitly.
|
Specify absolute path of nbextensions directory explicitly.
|
||||||
|
destination : str [optional]
|
||||||
|
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.
|
||||||
verbose : int [default: 1]
|
verbose : int [default: 1]
|
||||||
Set verbosity level. The default is 1, where file actions are printed.
|
Set verbosity level. The default is 1, where file actions are printed.
|
||||||
set verbose=2 for more output, or verbose=0 for silence.
|
set verbose=2 for more output, or verbose=0 for silence.
|
||||||
@ -173,75 +175,60 @@ def install_nbextension(files, overwrite=False, symlink=False, user=False, prefi
|
|||||||
# make sure nbextensions dir exists
|
# make sure nbextensions dir exists
|
||||||
ensure_dir_exists(nbext)
|
ensure_dir_exists(nbext)
|
||||||
|
|
||||||
if isinstance(files, string_types):
|
if isinstance(path, (list, tuple)):
|
||||||
# one file given, turn it into a list
|
raise TypeError("path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions")
|
||||||
files = [files]
|
|
||||||
if isinstance(files, (list,tuple)):
|
|
||||||
# list given, turn into dict
|
|
||||||
_files = {}
|
|
||||||
for path in map(cast_unicode_py2, files):
|
|
||||||
if path.startswith(('https://', 'http://')):
|
|
||||||
destination = urlparse(path).path.split('/')[-1]
|
|
||||||
elif path.endswith('.zip') or _safe_is_tarfile(path):
|
|
||||||
destination = str(uuid.uuid4()) # ignored for archives
|
|
||||||
else:
|
|
||||||
destination = basename(path)
|
|
||||||
_files[destination] = path
|
|
||||||
files = _files
|
|
||||||
|
|
||||||
for dest_basename,path in (map(cast_unicode_py2, item) for item in files.items()):
|
path = cast_unicode_py2(path)
|
||||||
|
|
||||||
if path.startswith(('https://', 'http://')):
|
if path.startswith(('https://', 'http://')):
|
||||||
if symlink:
|
if symlink:
|
||||||
raise ValueError("Cannot symlink from URLs")
|
raise ValueError("Cannot symlink from URLs")
|
||||||
# Given a URL, download it
|
# Given a URL, download it
|
||||||
with TemporaryDirectory() as td:
|
with TemporaryDirectory() as td:
|
||||||
filename = urlparse(path).path.split('/')[-1]
|
filename = urlparse(path).path.split('/')[-1]
|
||||||
local_path = os.path.join(td, filename)
|
local_path = os.path.join(td, filename)
|
||||||
if verbose >= 1:
|
if verbose >= 1:
|
||||||
print("downloading %s to %s" % (path, local_path))
|
print("downloading %s to %s" % (path, local_path))
|
||||||
urlretrieve(path, local_path)
|
urlretrieve(path, local_path)
|
||||||
# now install from the local copy
|
# now install from the local copy
|
||||||
install_nbextension({dest_basename: local_path}, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, verbose=verbose)
|
install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, verbose=verbose)
|
||||||
continue
|
elif path.endswith('.zip') or _safe_is_tarfile(path):
|
||||||
|
if symlink:
|
||||||
# handle archives
|
raise ValueError("Cannot symlink from archives")
|
||||||
archive = None
|
if destination:
|
||||||
|
raise ValueError("Cannot give destination for archives")
|
||||||
|
if verbose >= 1:
|
||||||
|
print("extracting %s to %s" % (path, nbext))
|
||||||
|
|
||||||
if path.endswith('.zip'):
|
if path.endswith('.zip'):
|
||||||
archive = zipfile.ZipFile(path)
|
archive = zipfile.ZipFile(path)
|
||||||
elif _safe_is_tarfile(path):
|
elif _safe_is_tarfile(path):
|
||||||
archive = tarfile.open(path)
|
archive = tarfile.open(path)
|
||||||
|
archive.extractall(nbext)
|
||||||
if archive:
|
archive.close()
|
||||||
if symlink:
|
else:
|
||||||
raise ValueError("Cannot symlink from archives")
|
if not destination:
|
||||||
|
destination = basename(path)
|
||||||
|
destination = cast_unicode_py2(destination)
|
||||||
|
full_dest = pjoin(nbext, destination)
|
||||||
|
if overwrite and os.path.exists(full_dest):
|
||||||
if verbose >= 1:
|
if verbose >= 1:
|
||||||
print("extracting %s to %s" % (path, nbext))
|
print("removing %s" % full_dest)
|
||||||
archive.extractall(nbext)
|
if os.path.isdir(full_dest) and not os.path.islink(full_dest):
|
||||||
archive.close()
|
shutil.rmtree(full_dest)
|
||||||
continue
|
|
||||||
|
|
||||||
dest = pjoin(nbext, dest_basename)
|
|
||||||
if overwrite and os.path.exists(dest):
|
|
||||||
if verbose >= 1:
|
|
||||||
print("removing %s" % dest)
|
|
||||||
if os.path.isdir(dest) and not os.path.islink(dest):
|
|
||||||
shutil.rmtree(dest)
|
|
||||||
else:
|
else:
|
||||||
os.remove(dest)
|
os.remove(full_dest)
|
||||||
|
|
||||||
if symlink:
|
if symlink:
|
||||||
path = os.path.abspath(path)
|
path = os.path.abspath(path)
|
||||||
if not os.path.exists(dest):
|
if not os.path.exists(full_dest):
|
||||||
if verbose >= 1:
|
if verbose >= 1:
|
||||||
print("symlink %s -> %s" % (dest, path))
|
print("symlink %s -> %s" % (full_dest, path))
|
||||||
os.symlink(path, dest)
|
os.symlink(path, full_dest)
|
||||||
continue
|
elif os.path.isdir(path):
|
||||||
|
|
||||||
if os.path.isdir(path):
|
|
||||||
path = pjoin(os.path.abspath(path), '') # end in path separator
|
path = pjoin(os.path.abspath(path), '') # end in path separator
|
||||||
for parent, dirs, files in os.walk(path):
|
for parent, dirs, files in os.walk(path):
|
||||||
dest_dir = pjoin(dest, parent[len(path):])
|
dest_dir = pjoin(full_dest, parent[len(path):])
|
||||||
if not os.path.exists(dest_dir):
|
if not os.path.exists(dest_dir):
|
||||||
if verbose >= 2:
|
if verbose >= 2:
|
||||||
print("making directory %s" % dest_dir)
|
print("making directory %s" % dest_dir)
|
||||||
@ -253,7 +240,7 @@ def install_nbextension(files, overwrite=False, symlink=False, user=False, prefi
|
|||||||
_maybe_copy(src, dest_file, verbose)
|
_maybe_copy(src, dest_file, verbose)
|
||||||
else:
|
else:
|
||||||
src = path
|
src = path
|
||||||
_maybe_copy(src, dest, verbose)
|
_maybe_copy(src, full_dest, verbose)
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# install nbextension app
|
# install nbextension app
|
||||||
@ -281,7 +268,7 @@ flags = {
|
|||||||
"symlink" : ({
|
"symlink" : ({
|
||||||
"NBExtensionApp" : {
|
"NBExtensionApp" : {
|
||||||
"symlink" : True,
|
"symlink" : True,
|
||||||
}}, "Create symlinks instead of copying files"
|
}}, "Create symlink instead of copying files"
|
||||||
),
|
),
|
||||||
"user" : ({
|
"user" : ({
|
||||||
"NBExtensionApp" : {
|
"NBExtensionApp" : {
|
||||||
@ -295,6 +282,7 @@ aliases = {
|
|||||||
"ipython-dir" : "NBExtensionApp.ipython_dir",
|
"ipython-dir" : "NBExtensionApp.ipython_dir",
|
||||||
"prefix" : "NBExtensionApp.prefix",
|
"prefix" : "NBExtensionApp.prefix",
|
||||||
"nbextensions" : "NBExtensionApp.nbextensions_dir",
|
"nbextensions" : "NBExtensionApp.nbextensions_dir",
|
||||||
|
"destination" : "NBExtensionApp.destination",
|
||||||
}
|
}
|
||||||
|
|
||||||
class NBExtensionApp(BaseIPythonApplication):
|
class NBExtensionApp(BaseIPythonApplication):
|
||||||
@ -304,9 +292,9 @@ class NBExtensionApp(BaseIPythonApplication):
|
|||||||
|
|
||||||
Usage
|
Usage
|
||||||
|
|
||||||
ipython install-nbextension file [more files, folders, archives or urls]
|
ipython install-nbextension path/url
|
||||||
|
|
||||||
This copies files and/or folders into the IPython nbextensions directory.
|
This copies a file or a folder into the IPython nbextensions directory.
|
||||||
If a URL is given, it will be downloaded.
|
If a URL is given, it will be downloaded.
|
||||||
If an archive is given, it will be extracted into nbextensions.
|
If an archive is given, it will be extracted into nbextensions.
|
||||||
If the requested files are already up to date, no action is taken
|
If the requested files are already up to date, no action is taken
|
||||||
@ -314,7 +302,7 @@ class NBExtensionApp(BaseIPythonApplication):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
examples = """
|
examples = """
|
||||||
ipython install-nbextension /path/to/d3.js /path/to/myextension
|
ipython install-nbextension /path/to/myextension
|
||||||
"""
|
"""
|
||||||
aliases = aliases
|
aliases = aliases
|
||||||
flags = flags
|
flags = flags
|
||||||
@ -324,17 +312,21 @@ class NBExtensionApp(BaseIPythonApplication):
|
|||||||
user = Bool(False, config=True, help="Whether to do a user install")
|
user = Bool(False, config=True, help="Whether to do a user install")
|
||||||
prefix = Unicode('', config=True, help="Installation prefix")
|
prefix = Unicode('', config=True, help="Installation prefix")
|
||||||
nbextensions_dir = Unicode('', config=True, help="Full path to nbextensions dir (probably use prefix or user)")
|
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")
|
||||||
verbose = Enum((0,1,2), default_value=1, config=True,
|
verbose = Enum((0,1,2), default_value=1, config=True,
|
||||||
help="Verbosity level"
|
help="Verbosity level"
|
||||||
)
|
)
|
||||||
|
|
||||||
def install_extensions(self):
|
def install_extensions(self):
|
||||||
install_nbextension(self.extra_args,
|
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,
|
overwrite=self.overwrite,
|
||||||
symlink=self.symlink,
|
symlink=self.symlink,
|
||||||
verbose=self.verbose,
|
verbose=self.verbose,
|
||||||
user=self.user,
|
user=self.user,
|
||||||
prefix=self.prefix,
|
prefix=self.prefix,
|
||||||
|
destination=self.destination,
|
||||||
nbextensions_dir=self.nbextensions_dir,
|
nbextensions_dir=self.nbextensions_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -133,13 +133,21 @@ class TestInstallNBExtension(TestCase):
|
|||||||
d = u'∂ir'
|
d = u'∂ir'
|
||||||
install_nbextension(pjoin(self.src, d))
|
install_nbextension(pjoin(self.src, d))
|
||||||
self.assert_installed(self.files[-1])
|
self.assert_installed(self.files[-1])
|
||||||
install_nbextension({'test': pjoin(self.src, d)})
|
|
||||||
self.assert_installed(pjoin('test', u'∂ir2', u'ƒile2'))
|
|
||||||
|
def test_destination_file(self):
|
||||||
|
file = self.files[0]
|
||||||
|
install_nbextension(pjoin(self.src, file), destination = u'ƒiledest')
|
||||||
|
self.assert_installed(u'ƒiledest')
|
||||||
|
|
||||||
|
def test_destination_dir(self):
|
||||||
|
d = u'∂ir'
|
||||||
|
install_nbextension(pjoin(self.src, d), destination = u'ƒiledest2')
|
||||||
|
self.assert_installed(pjoin(u'ƒiledest2', u'∂ir2', u'ƒile2'))
|
||||||
|
|
||||||
def test_install_nbextension(self):
|
def test_install_nbextension(self):
|
||||||
install_nbextension(glob.glob(pjoin(self.src, '*')))
|
with self.assertRaises(TypeError):
|
||||||
for file in self.files:
|
install_nbextension(glob.glob(pjoin(self.src, '*')))
|
||||||
self.assert_installed(file)
|
|
||||||
|
|
||||||
def test_overwrite_file(self):
|
def test_overwrite_file(self):
|
||||||
with TemporaryDirectory() as d:
|
with TemporaryDirectory() as d:
|
||||||
@ -242,7 +250,8 @@ class TestInstallNBExtension(TestCase):
|
|||||||
self.assert_installed("foo.js")
|
self.assert_installed("foo.js")
|
||||||
install_nbextension("https://example.com/path/to/another/bar.js")
|
install_nbextension("https://example.com/path/to/another/bar.js")
|
||||||
self.assert_installed("bar.js")
|
self.assert_installed("bar.js")
|
||||||
install_nbextension({'foobar.js': "https://example.com/path/to/another/bar.js"})
|
install_nbextension("https://example.com/path/to/another/bar.js",
|
||||||
|
destination = 'foobar.js')
|
||||||
self.assert_installed("foobar.js")
|
self.assert_installed("foobar.js")
|
||||||
finally:
|
finally:
|
||||||
nbextensions.urlretrieve = save_urlretrieve
|
nbextensions.urlretrieve = save_urlretrieve
|
||||||
@ -270,6 +279,19 @@ class TestInstallNBExtension(TestCase):
|
|||||||
link = os.readlink(dest)
|
link = os.readlink(dest)
|
||||||
self.assertEqual(link, src)
|
self.assertEqual(link, src)
|
||||||
|
|
||||||
|
@dec.skip_win32
|
||||||
|
def test_install_symlink_destination(self):
|
||||||
|
with TemporaryDirectory() as d:
|
||||||
|
f = u'ƒ.js'
|
||||||
|
flink = u'ƒlink.js'
|
||||||
|
src = pjoin(d, f)
|
||||||
|
touch(src)
|
||||||
|
install_nbextension(src, symlink=True, destination=flink)
|
||||||
|
dest = pjoin(self.system_nbext, flink)
|
||||||
|
assert os.path.islink(dest)
|
||||||
|
link = os.readlink(dest)
|
||||||
|
self.assertEqual(link, src)
|
||||||
|
|
||||||
def test_install_symlink_bad(self):
|
def test_install_symlink_bad(self):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
install_nbextension("http://example.com/foo.js", symlink=True)
|
install_nbextension("http://example.com/foo.js", symlink=True)
|
||||||
@ -283,11 +305,12 @@ class TestInstallNBExtension(TestCase):
|
|||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
install_nbextension(zsrc, symlink=True)
|
install_nbextension(zsrc, symlink=True)
|
||||||
|
|
||||||
def test_install_different_name(self):
|
def test_install_destination_bad(self):
|
||||||
with TemporaryDirectory() as d:
|
with TemporaryDirectory() as d:
|
||||||
f = u'ƒ.js'
|
zf = u'ƒ.zip'
|
||||||
src = pjoin(d, f)
|
zsrc = pjoin(d, zf)
|
||||||
dest_f = u'ƒile.js'
|
with zipfile.ZipFile(zsrc, 'w') as z:
|
||||||
touch(src)
|
z.writestr("a.js", b"b();")
|
||||||
install_nbextension({dest_f: src})
|
|
||||||
self.assert_installed(dest_f)
|
with self.assertRaises(ValueError):
|
||||||
|
install_nbextension(zsrc, destination='foo')
|
||||||
|
Loading…
Reference in New Issue
Block a user