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:
Thomas Kluyver 2015-01-28 14:31:33 -08:00
commit c571fa545d
2 changed files with 97 additions and 82 deletions

View File

@ -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)
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
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
----------
files : list(paths or URLs) or dict(install_name: path or URL)
One or more paths or URLs to existing files directories to install.
If given as a list, these will be installed with their base name, so '/path/to/foo'
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'.
path : path to file, directory, zip or tarball archive, or URL to install
By default, the file will be installed with its base name, so '/path/to/foo'
will install to 'nbextensions/foo'. See the destination argument below to change this.
Archives (zip or tarballs) will be extracted into the nbextensions directory.
overwrite : bool [default: False]
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
nbextensions_dir : str [optional]
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]
Set verbosity level. The default is 1, where file actions are printed.
set verbose=2 for more output, or verbose=0 for silence.
@ -173,23 +175,10 @@ def install_nbextension(files, overwrite=False, symlink=False, user=False, prefi
# make sure nbextensions dir exists
ensure_dir_exists(nbext)
if isinstance(files, string_types):
# one file given, turn it into a list
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
if isinstance(path, (list, tuple)):
raise TypeError("path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions")
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 symlink:
@ -202,46 +191,44 @@ def install_nbextension(files, overwrite=False, symlink=False, user=False, prefi
print("downloading %s to %s" % (path, local_path))
urlretrieve(path, local_path)
# now install from the local copy
install_nbextension({dest_basename: local_path}, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, verbose=verbose)
continue
install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, verbose=verbose)
elif path.endswith('.zip') or _safe_is_tarfile(path):
if symlink:
raise ValueError("Cannot symlink from archives")
if destination:
raise ValueError("Cannot give destination for archives")
if verbose >= 1:
print("extracting %s to %s" % (path, nbext))
# handle archives
archive = None
if path.endswith('.zip'):
archive = zipfile.ZipFile(path)
elif _safe_is_tarfile(path):
archive = tarfile.open(path)
if archive:
if symlink:
raise ValueError("Cannot symlink from archives")
if verbose >= 1:
print("extracting %s to %s" % (path, nbext))
archive.extractall(nbext)
archive.close()
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:
os.remove(dest)
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:
print("removing %s" % full_dest)
if os.path.isdir(full_dest) and not os.path.islink(full_dest):
shutil.rmtree(full_dest)
else:
os.remove(full_dest)
if symlink:
path = os.path.abspath(path)
if not os.path.exists(dest):
if not os.path.exists(full_dest):
if verbose >= 1:
print("symlink %s -> %s" % (dest, path))
os.symlink(path, dest)
continue
if os.path.isdir(path):
print("symlink %s -> %s" % (full_dest, path))
os.symlink(path, full_dest)
elif os.path.isdir(path):
path = pjoin(os.path.abspath(path), '') # end in path separator
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 verbose >= 2:
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)
else:
src = path
_maybe_copy(src, dest, verbose)
_maybe_copy(src, full_dest, verbose)
#----------------------------------------------------------------------
# install nbextension app
@ -281,7 +268,7 @@ flags = {
"symlink" : ({
"NBExtensionApp" : {
"symlink" : True,
}}, "Create symlinks instead of copying files"
}}, "Create symlink instead of copying files"
),
"user" : ({
"NBExtensionApp" : {
@ -295,6 +282,7 @@ aliases = {
"ipython-dir" : "NBExtensionApp.ipython_dir",
"prefix" : "NBExtensionApp.prefix",
"nbextensions" : "NBExtensionApp.nbextensions_dir",
"destination" : "NBExtensionApp.destination",
}
class NBExtensionApp(BaseIPythonApplication):
@ -304,9 +292,9 @@ class NBExtensionApp(BaseIPythonApplication):
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 an archive is given, it will be extracted into nbextensions.
If the requested files are already up to date, no action is taken
@ -314,7 +302,7 @@ class NBExtensionApp(BaseIPythonApplication):
"""
examples = """
ipython install-nbextension /path/to/d3.js /path/to/myextension
ipython install-nbextension /path/to/myextension
"""
aliases = aliases
flags = flags
@ -324,17 +312,21 @@ class NBExtensionApp(BaseIPythonApplication):
user = Bool(False, config=True, help="Whether to do a user install")
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")
verbose = Enum((0,1,2), default_value=1, config=True,
help="Verbosity level"
)
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,
symlink=self.symlink,
verbose=self.verbose,
user=self.user,
prefix=self.prefix,
destination=self.destination,
nbextensions_dir=self.nbextensions_dir,
)

View File

@ -133,13 +133,21 @@ class TestInstallNBExtension(TestCase):
d = u'∂ir'
install_nbextension(pjoin(self.src, d))
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):
with self.assertRaises(TypeError):
install_nbextension(glob.glob(pjoin(self.src, '*')))
for file in self.files:
self.assert_installed(file)
def test_overwrite_file(self):
with TemporaryDirectory() as d:
@ -242,7 +250,8 @@ class TestInstallNBExtension(TestCase):
self.assert_installed("foo.js")
install_nbextension("https://example.com/path/to/another/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")
finally:
nbextensions.urlretrieve = save_urlretrieve
@ -270,6 +279,19 @@ class TestInstallNBExtension(TestCase):
link = os.readlink(dest)
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):
with self.assertRaises(ValueError):
install_nbextension("http://example.com/foo.js", symlink=True)
@ -283,11 +305,12 @@ class TestInstallNBExtension(TestCase):
with self.assertRaises(ValueError):
install_nbextension(zsrc, symlink=True)
def test_install_different_name(self):
def test_install_destination_bad(self):
with TemporaryDirectory() as d:
f = u'ƒ.js'
src = pjoin(d, f)
dest_f = u'ƒile.js'
touch(src)
install_nbextension({dest_f: src})
self.assert_installed(dest_f)
zf = u'ƒ.zip'
zsrc = pjoin(d, zf)
with zipfile.ZipFile(zsrc, 'w') as z:
z.writestr("a.js", b"b();")
with self.assertRaises(ValueError):
install_nbextension(zsrc, destination='foo')