nbextensions.py
264 lines
| 8.7 KiB
| text/x-python
|
PythonLexer
MinRK
|
r15220 | # coding: utf-8 | |
"""Utilities for installing Javascript extensions for the notebook""" | |||
MinRK
|
r16486 | # Copyright (c) IPython Development Team. | |
# Distributed under the terms of the Modified BSD License. | |||
MinRK
|
r15220 | ||
from __future__ import print_function | |||
import os | |||
import shutil | |||
MinRK
|
r15223 | import tarfile | |
import zipfile | |||
MinRK
|
r15220 | from os.path import basename, join as pjoin | |
MinRK
|
r15223 | # Deferred imports | |
try: | |||
from urllib.parse import urlparse # Py3 | |||
from urllib.request import urlretrieve | |||
except ImportError: | |||
from urlparse import urlparse | |||
from urllib import urlretrieve | |||
MinRK
|
r16486 | from IPython.utils.path import get_ipython_dir, ensure_dir_exists | |
MinRK
|
r15220 | from IPython.utils.py3compat import string_types, cast_unicode_py2 | |
MinRK
|
r15223 | from IPython.utils.tempdir import TemporaryDirectory | |
MinRK
|
r15220 | ||
def _should_copy(src, dest, verbose=1): | |||
"""should a file be copied?""" | |||
if not os.path.exists(dest): | |||
return True | |||
if os.stat(dest).st_mtime < os.stat(src).st_mtime: | |||
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) | |||
MinRK
|
r15223 | def _safe_is_tarfile(path): | |
"""safe version of is_tarfile, return False on IOError""" | |||
try: | |||
return tarfile.is_tarfile(path) | |||
except IOError: | |||
return False | |||
MinRK
|
r15227 | def check_nbextension(files, ipython_dir=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') | |||
# make sure nbextensions dir exists | |||
if not os.path.exists(nbext): | |||
return False | |||
if isinstance(files, string_types): | |||
# one file given, turn it into a list | |||
files = [files] | |||
MinRK
|
r15340 | return all(os.path.exists(pjoin(nbext, f)) for f in files) | |
MinRK
|
r15227 | ||
MinRK
|
r15226 | def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None, verbose=1): | |
MinRK
|
r15220 | """Install a Javascript extension for the notebook | |
Stages files and/or directories into IPYTHONDIR/nbextensions. | |||
MinRK
|
r15223 | By default, this compares modification time, and only stages files that need updating. | |
MinRK
|
r15220 | If `overwrite` is specified, matching files are purged before proceeding. | |
Parameters | |||
---------- | |||
MinRK
|
r15223 | files : list(paths or URLs) | |
One or more paths or URLs to existing files directories to install. | |||
MinRK
|
r15220 | These will be installed with their base name, so '/path/to/foo' | |
will install to 'nbextensions/foo'. | |||
MinRK
|
r15223 | Archives (zip or tarballs) will be extracted into the nbextensions directory. | |
MinRK
|
r15220 | overwrite : bool [default: False] | |
If True, always install the files, regardless of what may already be installed. | |||
MinRK
|
r15226 | symlink : bool [default: False] | |
If True, create a symlink in nbextensions, rather than copying files. | |||
Thomas Kluyver
|
r18725 | 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. | |||
MinRK
|
r15220 | ipython_dir : str [optional] | |
The path to an IPython directory, if the default value is not desired. | |||
get_ipython_dir() is used by default. | |||
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') | |||
# make sure nbextensions dir exists | |||
MinRK
|
r16486 | ensure_dir_exists(nbext) | |
MinRK
|
r15220 | ||
if isinstance(files, string_types): | |||
# one file given, turn it into a list | |||
files = [files] | |||
for path in map(cast_unicode_py2, files): | |||
MinRK
|
r15223 | ||
if path.startswith(('https://', 'http://')): | |||
MinRK
|
r15226 | if symlink: | |
raise ValueError("Cannot symlink from URLs") | |||
MinRK
|
r15223 | # Given a URL, download it | |
with TemporaryDirectory() as td: | |||
filename = urlparse(path).path.split('/')[-1] | |||
local_path = os.path.join(td, filename) | |||
if verbose >= 1: | |||
print("downloading %s to %s" % (path, local_path)) | |||
urlretrieve(path, local_path) | |||
# now install from the local copy | |||
MinRK
|
r15226 | install_nbextension(local_path, overwrite, symlink, ipython_dir, verbose) | |
MinRK
|
r15223 | continue | |
# handle archives | |||
archive = None | |||
if path.endswith('.zip'): | |||
archive = zipfile.ZipFile(path) | |||
elif _safe_is_tarfile(path): | |||
archive = tarfile.open(path) | |||
if archive: | |||
MinRK
|
r15226 | if symlink: | |
MinRK
|
r15339 | raise ValueError("Cannot symlink from archives") | |
MinRK
|
r15223 | if verbose >= 1: | |
print("extracting %s to %s" % (path, nbext)) | |||
archive.extractall(nbext) | |||
archive.close() | |||
continue | |||
MinRK
|
r15220 | dest = pjoin(nbext, basename(path)) | |
if overwrite and os.path.exists(dest): | |||
if verbose >= 1: | |||
print("removing %s" % dest) | |||
if os.path.isdir(dest): | |||
shutil.rmtree(dest) | |||
else: | |||
os.remove(dest) | |||
MinRK
|
r15226 | ||
if symlink: | |||
path = os.path.abspath(path) | |||
if not os.path.exists(dest): | |||
if verbose >= 1: | |||
print("symlink %s -> %s" % (dest, path)) | |||
os.symlink(path, dest) | |||
continue | |||
MinRK
|
r15220 | ||
if os.path.isdir(path): | |||
strip_prefix_len = len(path) - len(basename(path)) | |||
for parent, dirs, files in os.walk(path): | |||
dest_dir = pjoin(nbext, parent[strip_prefix_len:]) | |||
if not os.path.exists(dest_dir): | |||
if verbose >= 2: | |||
print("making directory %s" % dest_dir) | |||
os.makedirs(dest_dir) | |||
for file in files: | |||
src = pjoin(parent, file) | |||
# print("%r, %r" % (dest_dir, file)) | |||
dest = pjoin(dest_dir, file) | |||
_maybe_copy(src, dest, verbose) | |||
else: | |||
src = path | |||
_maybe_copy(src, dest, verbose) | |||
MinRK
|
r15221 | ||
#---------------------------------------------------------------------- | |||
# install nbextension app | |||
#---------------------------------------------------------------------- | |||
from IPython.utils.traitlets import Bool, Enum | |||
from IPython.core.application import BaseIPythonApplication | |||
flags = { | |||
"overwrite" : ({ | |||
"NBExtensionApp" : { | |||
"overwrite" : True, | |||
}}, "Force overwrite of existing files" | |||
), | |||
"debug" : ({ | |||
"NBExtensionApp" : { | |||
"verbose" : 2, | |||
}}, "Extra output" | |||
), | |||
"quiet" : ({ | |||
"NBExtensionApp" : { | |||
"verbose" : 0, | |||
}}, "Minimal output" | |||
), | |||
MinRK
|
r15226 | "symlink" : ({ | |
"NBExtensionApp" : { | |||
"symlink" : True, | |||
}}, "Create symlinks instead of copying files" | |||
), | |||
MinRK
|
r15221 | } | |
MinRK
|
r15226 | flags['s'] = flags['symlink'] | |
MinRK
|
r15221 | aliases = { | |
"ipython-dir" : "NBExtensionApp.ipython_dir" | |||
} | |||
class NBExtensionApp(BaseIPythonApplication): | |||
"""Entry point for installing notebook extensions""" | |||
description = """Install IPython notebook extensions | |||
Usage | |||
MinRK
|
r15223 | ipython install-nbextension file [more files, folders, archives or urls] | |
MinRK
|
r15221 | ||
This copies files and/or folders into the IPython nbextensions directory. | |||
MinRK
|
r15223 | If a URL is given, it will be downloaded. | |
If an archive is given, it will be extracted into nbextensions. | |||
MinRK
|
r15221 | If the requested files are already up to date, no action is taken | |
unless --overwrite is specified. | |||
""" | |||
examples = """ | |||
ipython install-nbextension /path/to/d3.js /path/to/myextension | |||
""" | |||
aliases = aliases | |||
flags = flags | |||
overwrite = Bool(False, config=True, help="Force overwrite of existing files") | |||
MinRK
|
r15226 | symlink = Bool(False, config=True, help="Create symlinks instead of copying files") | |
MinRK
|
r15221 | verbose = Enum((0,1,2), default_value=1, config=True, | |
help="Verbosity level" | |||
) | |||
def install_extensions(self): | |||
install_nbextension(self.extra_args, | |||
overwrite=self.overwrite, | |||
MinRK
|
r15226 | symlink=self.symlink, | |
MinRK
|
r15221 | verbose=self.verbose, | |
ipython_dir=self.ipython_dir, | |||
) | |||
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) | |||
else: | |||
self.install_extensions() | |||
if __name__ == '__main__': | |||
NBExtensionApp.launch_instance() | |||