##// END OF EJS Templates
s/nbextensions/nbextensions_dir...
Min RK -
Show More
@@ -1,327 +1,327 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Utilities for installing Javascript extensions for the notebook"""
2 """Utilities for installing Javascript extensions for the notebook"""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 import os
9 import os
10 import shutil
10 import shutil
11 import sys
11 import sys
12 import tarfile
12 import tarfile
13 import zipfile
13 import zipfile
14 from os.path import basename, join as pjoin
14 from os.path import basename, join as pjoin
15
15
16 # Deferred imports
16 # Deferred imports
17 try:
17 try:
18 from urllib.parse import urlparse # Py3
18 from urllib.parse import urlparse # Py3
19 from urllib.request import urlretrieve
19 from urllib.request import urlretrieve
20 except ImportError:
20 except ImportError:
21 from urlparse import urlparse
21 from urlparse import urlparse
22 from urllib import urlretrieve
22 from urllib import urlretrieve
23
23
24 from IPython.utils.path import get_ipython_dir, ensure_dir_exists
24 from IPython.utils.path import get_ipython_dir, ensure_dir_exists
25 from IPython.utils.py3compat import string_types, cast_unicode_py2
25 from IPython.utils.py3compat import string_types, cast_unicode_py2
26 from IPython.utils.tempdir import TemporaryDirectory
26 from IPython.utils.tempdir import TemporaryDirectory
27
27
28
28
29 # Packagers: modify the next block if you store system-installed nbextensions elsewhere (unlikely)
29 # Packagers: modify the next block if you store system-installed nbextensions elsewhere (unlikely)
30 SYSTEM_NBEXTENSIONS_DIRS = []
30 SYSTEM_NBEXTENSIONS_DIRS = []
31
31
32 if os.name == 'nt':
32 if os.name == 'nt':
33 programdata = os.environ.get('PROGRAMDATA', None)
33 programdata = os.environ.get('PROGRAMDATA', None)
34 if programdata: # PROGRAMDATA is not defined by default on XP.
34 if programdata: # PROGRAMDATA is not defined by default on XP.
35 SYSTEM_NBEXTENSIONS_DIRS = [pjoin(programdata, 'jupyter', 'nbextensions')]
35 SYSTEM_NBEXTENSIONS_DIRS = [pjoin(programdata, 'jupyter', 'nbextensions')]
36 prefixes = []
36 prefixes = []
37 else:
37 else:
38 prefixes = ['/usr/local', '/usr']
38 prefixes = ['/usr/local', '/usr']
39
39
40 # add sys.prefix at the front
40 # add sys.prefix at the front
41 if sys.prefix not in prefixes:
41 if sys.prefix not in prefixes:
42 prefixes.insert(0, sys.prefix)
42 prefixes.insert(0, sys.prefix)
43
43
44 for prefix in prefixes:
44 for prefix in prefixes:
45 nbext = os.path.join(prefix, 'share', 'jupyter', 'nbextensions')
45 nbext = os.path.join(prefix, 'share', 'jupyter', 'nbextensions')
46 if nbext not in SYSTEM_NBEXTENSIONS_DIRS:
46 if nbext not in SYSTEM_NBEXTENSIONS_DIRS:
47 SYSTEM_NBEXTENSIONS_DIRS.append(nbext)
47 SYSTEM_NBEXTENSIONS_DIRS.append(nbext)
48
48
49 if os.name == 'nt':
49 if os.name == 'nt':
50 # PROGRAMDATA
50 # PROGRAMDATA
51 SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-1]
51 SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-1]
52 else:
52 else:
53 # /usr/local
53 # /usr/local
54 SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-2]
54 SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-2]
55
55
56
56
57 def _should_copy(src, dest, verbose=1):
57 def _should_copy(src, dest, verbose=1):
58 """should a file be copied?"""
58 """should a file be copied?"""
59 if not os.path.exists(dest):
59 if not os.path.exists(dest):
60 return True
60 return True
61 if os.stat(dest).st_mtime < os.stat(src).st_mtime:
61 if os.stat(dest).st_mtime < os.stat(src).st_mtime:
62 if verbose >= 2:
62 if verbose >= 2:
63 print("%s is out of date" % dest)
63 print("%s is out of date" % dest)
64 return True
64 return True
65 if verbose >= 2:
65 if verbose >= 2:
66 print("%s is up to date" % dest)
66 print("%s is up to date" % dest)
67 return False
67 return False
68
68
69
69
70 def _maybe_copy(src, dest, verbose=1):
70 def _maybe_copy(src, dest, verbose=1):
71 """copy a file if it needs updating"""
71 """copy a file if it needs updating"""
72 if _should_copy(src, dest, verbose):
72 if _should_copy(src, dest, verbose):
73 if verbose >= 1:
73 if verbose >= 1:
74 print("copying %s -> %s" % (src, dest))
74 print("copying %s -> %s" % (src, dest))
75 shutil.copy2(src, dest)
75 shutil.copy2(src, dest)
76
76
77
77
78 def _safe_is_tarfile(path):
78 def _safe_is_tarfile(path):
79 """safe version of is_tarfile, return False on IOError"""
79 """safe version of is_tarfile, return False on IOError"""
80 try:
80 try:
81 return tarfile.is_tarfile(path)
81 return tarfile.is_tarfile(path)
82 except IOError:
82 except IOError:
83 return False
83 return False
84
84
85
85
86 def check_nbextension(files, nbextensions=None):
86 def check_nbextension(files, nbextensions_dir=None):
87 """Check whether nbextension files have been installed
87 """Check whether nbextension files have been installed
88
88
89 files should be a list of relative paths within nbextensions.
89 files should be a list of relative paths within nbextensions.
90
90
91 Returns True if all files are found, False if any are missing.
91 Returns True if all files are found, False if any are missing.
92 """
92 """
93 if nbextensions:
93 if nbextensions_dir:
94 nbext = nbextensions
94 nbext = nbextensions_dir
95 else:
95 else:
96 nbext = pjoin(get_ipython_dir(), u'nbextensions')
96 nbext = pjoin(get_ipython_dir(), u'nbextensions')
97 # make sure nbextensions dir exists
97 # make sure nbextensions dir exists
98 if not os.path.exists(nbext):
98 if not os.path.exists(nbext):
99 return False
99 return False
100
100
101 if isinstance(files, string_types):
101 if isinstance(files, string_types):
102 # one file given, turn it into a list
102 # one file given, turn it into a list
103 files = [files]
103 files = [files]
104
104
105 return all(os.path.exists(pjoin(nbext, f)) for f in files)
105 return all(os.path.exists(pjoin(nbext, f)) for f in files)
106
106
107
107
108 def install_nbextension(files, overwrite=False, symlink=False, user=False, prefix=None, nbextensions=None, verbose=1):
108 def install_nbextension(files, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, verbose=1):
109 """Install a Javascript extension for the notebook
109 """Install a Javascript extension for the notebook
110
110
111 Stages files and/or directories into IPYTHONDIR/nbextensions.
111 Stages files and/or directories into the nbextensions directory.
112 By default, this compares modification time, and only stages files that need updating.
112 By default, this compares modification time, and only stages files that need updating.
113 If `overwrite` is specified, matching files are purged before proceeding.
113 If `overwrite` is specified, matching files are purged before proceeding.
114
114
115 Parameters
115 Parameters
116 ----------
116 ----------
117
117
118 files : list(paths or URLs)
118 files : list(paths or URLs)
119 One or more paths or URLs to existing files directories to install.
119 One or more paths or URLs to existing files directories to install.
120 These will be installed with their base name, so '/path/to/foo'
120 These will be installed with their base name, so '/path/to/foo'
121 will install to 'nbextensions/foo'.
121 will install to 'nbextensions/foo'.
122 Archives (zip or tarballs) will be extracted into the nbextensions directory.
122 Archives (zip or tarballs) will be extracted into the nbextensions directory.
123 overwrite : bool [default: False]
123 overwrite : bool [default: False]
124 If True, always install the files, regardless of what may already be installed.
124 If True, always install the files, regardless of what may already be installed.
125 symlink : bool [default: False]
125 symlink : bool [default: False]
126 If True, create a symlink in nbextensions, rather than copying files.
126 If True, create a symlink in nbextensions, rather than copying files.
127 Not allowed with URLs or archives. Windows support for symlinks requires
127 Not allowed with URLs or archives. Windows support for symlinks requires
128 Vista or above, Python 3, and a permission bit which only admin users
128 Vista or above, Python 3, and a permission bit which only admin users
129 have by default, so don't rely on it.
129 have by default, so don't rely on it.
130 user : bool [default: False]
130 user : bool [default: False]
131 Whether to install to the user's .ipython/nbextensions directory.
131 Whether to install to the user's .ipython/nbextensions directory.
132 Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
132 Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
133 prefix : str [optional]
133 prefix : str [optional]
134 Specify install prefix, if it should differ from default (e.g. /usr/local).
134 Specify install prefix, if it should differ from default (e.g. /usr/local).
135 Will install to prefix/share/jupyter/nbextensions
135 Will install to prefix/share/jupyter/nbextensions
136 nbextensions : str [optional]
136 nbextensions_dir : str [optional]
137 Specify absolute path of nbextensions directory explicitly.
137 Specify absolute path of nbextensions directory explicitly.
138 verbose : int [default: 1]
138 verbose : int [default: 1]
139 Set verbosity level. The default is 1, where file actions are printed.
139 Set verbosity level. The default is 1, where file actions are printed.
140 set verbose=2 for more output, or verbose=0 for silence.
140 set verbose=2 for more output, or verbose=0 for silence.
141 """
141 """
142 if sum(map(bool, [user, prefix, nbextensions])) > 1:
142 if sum(map(bool, [user, prefix, nbextensions_dir])) > 1:
143 raise ValueError("Cannot specify more than one of user, prefix, or nbextensions.")
143 raise ValueError("Cannot specify more than one of user, prefix, or nbextensions_dir.")
144 if user:
144 if user:
145 nbext = pjoin(get_ipython_dir(), u'nbextensions')
145 nbext = pjoin(get_ipython_dir(), u'nbextensions')
146 else:
146 else:
147 if prefix:
147 if prefix:
148 nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
148 nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
149 elif nbextensions:
149 elif nbextensions_dir:
150 nbext = nbextensions
150 nbext = nbextensions_dir
151 else:
151 else:
152 nbext = SYSTEM_NBEXTENSIONS_INSTALL_DIR
152 nbext = SYSTEM_NBEXTENSIONS_INSTALL_DIR
153 # make sure nbextensions dir exists
153 # make sure nbextensions dir exists
154 ensure_dir_exists(nbext)
154 ensure_dir_exists(nbext)
155
155
156 if isinstance(files, string_types):
156 if isinstance(files, string_types):
157 # one file given, turn it into a list
157 # one file given, turn it into a list
158 files = [files]
158 files = [files]
159
159
160 for path in map(cast_unicode_py2, files):
160 for path in map(cast_unicode_py2, files):
161
161
162 if path.startswith(('https://', 'http://')):
162 if path.startswith(('https://', 'http://')):
163 if symlink:
163 if symlink:
164 raise ValueError("Cannot symlink from URLs")
164 raise ValueError("Cannot symlink from URLs")
165 # Given a URL, download it
165 # Given a URL, download it
166 with TemporaryDirectory() as td:
166 with TemporaryDirectory() as td:
167 filename = urlparse(path).path.split('/')[-1]
167 filename = urlparse(path).path.split('/')[-1]
168 local_path = os.path.join(td, filename)
168 local_path = os.path.join(td, filename)
169 if verbose >= 1:
169 if verbose >= 1:
170 print("downloading %s to %s" % (path, local_path))
170 print("downloading %s to %s" % (path, local_path))
171 urlretrieve(path, local_path)
171 urlretrieve(path, local_path)
172 # now install from the local copy
172 # now install from the local copy
173 install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions=nbext, verbose=verbose)
173 install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, verbose=verbose)
174 continue
174 continue
175
175
176 # handle archives
176 # handle archives
177 archive = None
177 archive = None
178 if path.endswith('.zip'):
178 if path.endswith('.zip'):
179 archive = zipfile.ZipFile(path)
179 archive = zipfile.ZipFile(path)
180 elif _safe_is_tarfile(path):
180 elif _safe_is_tarfile(path):
181 archive = tarfile.open(path)
181 archive = tarfile.open(path)
182
182
183 if archive:
183 if archive:
184 if symlink:
184 if symlink:
185 raise ValueError("Cannot symlink from archives")
185 raise ValueError("Cannot symlink from archives")
186 if verbose >= 1:
186 if verbose >= 1:
187 print("extracting %s to %s" % (path, nbext))
187 print("extracting %s to %s" % (path, nbext))
188 archive.extractall(nbext)
188 archive.extractall(nbext)
189 archive.close()
189 archive.close()
190 continue
190 continue
191
191
192 dest = pjoin(nbext, basename(path))
192 dest = pjoin(nbext, basename(path))
193 if overwrite and os.path.exists(dest):
193 if overwrite and os.path.exists(dest):
194 if verbose >= 1:
194 if verbose >= 1:
195 print("removing %s" % dest)
195 print("removing %s" % dest)
196 if os.path.isdir(dest) and not os.path.islink(dest):
196 if os.path.isdir(dest) and not os.path.islink(dest):
197 shutil.rmtree(dest)
197 shutil.rmtree(dest)
198 else:
198 else:
199 os.remove(dest)
199 os.remove(dest)
200
200
201 if symlink:
201 if symlink:
202 path = os.path.abspath(path)
202 path = os.path.abspath(path)
203 if not os.path.exists(dest):
203 if not os.path.exists(dest):
204 if verbose >= 1:
204 if verbose >= 1:
205 print("symlink %s -> %s" % (dest, path))
205 print("symlink %s -> %s" % (dest, path))
206 os.symlink(path, dest)
206 os.symlink(path, dest)
207 continue
207 continue
208
208
209 if os.path.isdir(path):
209 if os.path.isdir(path):
210 strip_prefix_len = len(path) - len(basename(path))
210 strip_prefix_len = len(path) - len(basename(path))
211 for parent, dirs, files in os.walk(path):
211 for parent, dirs, files in os.walk(path):
212 dest_dir = pjoin(nbext, parent[strip_prefix_len:])
212 dest_dir = pjoin(nbext, parent[strip_prefix_len:])
213 if not os.path.exists(dest_dir):
213 if not os.path.exists(dest_dir):
214 if verbose >= 2:
214 if verbose >= 2:
215 print("making directory %s" % dest_dir)
215 print("making directory %s" % dest_dir)
216 os.makedirs(dest_dir)
216 os.makedirs(dest_dir)
217 for file in files:
217 for file in files:
218 src = pjoin(parent, file)
218 src = pjoin(parent, file)
219 # print("%r, %r" % (dest_dir, file))
219 # print("%r, %r" % (dest_dir, file))
220 dest = pjoin(dest_dir, file)
220 dest = pjoin(dest_dir, file)
221 _maybe_copy(src, dest, verbose)
221 _maybe_copy(src, dest, verbose)
222 else:
222 else:
223 src = path
223 src = path
224 _maybe_copy(src, dest, verbose)
224 _maybe_copy(src, dest, verbose)
225
225
226 #----------------------------------------------------------------------
226 #----------------------------------------------------------------------
227 # install nbextension app
227 # install nbextension app
228 #----------------------------------------------------------------------
228 #----------------------------------------------------------------------
229
229
230 from IPython.utils.traitlets import Bool, Enum, Unicode, TraitError
230 from IPython.utils.traitlets import Bool, Enum, Unicode, TraitError
231 from IPython.core.application import BaseIPythonApplication
231 from IPython.core.application import BaseIPythonApplication
232
232
233 flags = {
233 flags = {
234 "overwrite" : ({
234 "overwrite" : ({
235 "NBExtensionApp" : {
235 "NBExtensionApp" : {
236 "overwrite" : True,
236 "overwrite" : True,
237 }}, "Force overwrite of existing files"
237 }}, "Force overwrite of existing files"
238 ),
238 ),
239 "debug" : ({
239 "debug" : ({
240 "NBExtensionApp" : {
240 "NBExtensionApp" : {
241 "verbose" : 2,
241 "verbose" : 2,
242 }}, "Extra output"
242 }}, "Extra output"
243 ),
243 ),
244 "quiet" : ({
244 "quiet" : ({
245 "NBExtensionApp" : {
245 "NBExtensionApp" : {
246 "verbose" : 0,
246 "verbose" : 0,
247 }}, "Minimal output"
247 }}, "Minimal output"
248 ),
248 ),
249 "symlink" : ({
249 "symlink" : ({
250 "NBExtensionApp" : {
250 "NBExtensionApp" : {
251 "symlink" : True,
251 "symlink" : True,
252 }}, "Create symlinks instead of copying files"
252 }}, "Create symlinks instead of copying files"
253 ),
253 ),
254 "user" : ({
254 "user" : ({
255 "NBExtensionApp" : {
255 "NBExtensionApp" : {
256 "user" : True,
256 "user" : True,
257 }}, "Install to the user's IPython directory"
257 }}, "Install to the user's IPython directory"
258 ),
258 ),
259 }
259 }
260 flags['s'] = flags['symlink']
260 flags['s'] = flags['symlink']
261
261
262 aliases = {
262 aliases = {
263 "ipython-dir" : "NBExtensionApp.ipython_dir",
263 "ipython-dir" : "NBExtensionApp.ipython_dir",
264 "prefix" : "NBExtensionApp.prefix",
264 "prefix" : "NBExtensionApp.prefix",
265 "nbextensions" : "NBExtensionApp.nbextensions",
265 "nbextensions" : "NBExtensionApp.nbextensions_dir",
266 }
266 }
267
267
268 class NBExtensionApp(BaseIPythonApplication):
268 class NBExtensionApp(BaseIPythonApplication):
269 """Entry point for installing notebook extensions"""
269 """Entry point for installing notebook extensions"""
270
270
271 description = """Install IPython notebook extensions
271 description = """Install IPython notebook extensions
272
272
273 Usage
273 Usage
274
274
275 ipython install-nbextension file [more files, folders, archives or urls]
275 ipython install-nbextension file [more files, folders, archives or urls]
276
276
277 This copies files and/or folders into the IPython nbextensions directory.
277 This copies files and/or folders into the IPython nbextensions directory.
278 If a URL is given, it will be downloaded.
278 If a URL is given, it will be downloaded.
279 If an archive is given, it will be extracted into nbextensions.
279 If an archive is given, it will be extracted into nbextensions.
280 If the requested files are already up to date, no action is taken
280 If the requested files are already up to date, no action is taken
281 unless --overwrite is specified.
281 unless --overwrite is specified.
282 """
282 """
283
283
284 examples = """
284 examples = """
285 ipython install-nbextension /path/to/d3.js /path/to/myextension
285 ipython install-nbextension /path/to/d3.js /path/to/myextension
286 """
286 """
287 aliases = aliases
287 aliases = aliases
288 flags = flags
288 flags = flags
289
289
290 overwrite = Bool(False, config=True, help="Force overwrite of existing files")
290 overwrite = Bool(False, config=True, help="Force overwrite of existing files")
291 symlink = Bool(False, config=True, help="Create symlinks instead of copying files")
291 symlink = Bool(False, config=True, help="Create symlinks instead of copying files")
292 user = Bool(False, config=True, help="Whether to do a user install")
292 user = Bool(False, config=True, help="Whether to do a user install")
293 prefix = Unicode('', config=True, help="Installation prefix")
293 prefix = Unicode('', config=True, help="Installation prefix")
294 nbextensions = Unicode('', config=True, help="Full path to nbextensions (probably use prefix or user)")
294 nbextensions_dir = Unicode('', config=True, help="Full path to nbextensions dir (probably use prefix or user)")
295 verbose = Enum((0,1,2), default_value=1, config=True,
295 verbose = Enum((0,1,2), default_value=1, config=True,
296 help="Verbosity level"
296 help="Verbosity level"
297 )
297 )
298
298
299 def check_install():
299 def check_install():
300 if sum(map(bool, [user, prefix, nbextensions])) > 1:
300 if sum(map(bool, [user, prefix, nbextensions_dir])) > 1:
301 raise TraitError("Cannot specify more than one of user, prefix, or nbextensions.")
301 raise TraitError("Cannot specify more than one of user, prefix, or nbextensions_dir.")
302
302
303 def install_extensions(self):
303 def install_extensions(self):
304 install_nbextension(self.extra_args,
304 install_nbextension(self.extra_args,
305 overwrite=self.overwrite,
305 overwrite=self.overwrite,
306 symlink=self.symlink,
306 symlink=self.symlink,
307 verbose=self.verbose,
307 verbose=self.verbose,
308 user=self.user,
308 user=self.user,
309 prefix=self.prefix,
309 prefix=self.prefix,
310 nbextensions=self.nbextensions,
310 nbextensions_dir=self.nbextensions_dir,
311 )
311 )
312
312
313 def start(self):
313 def start(self):
314 if not self.extra_args:
314 if not self.extra_args:
315 for nbext in [pjoin(self.ipython_dir, u'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS:
315 for nbext in [pjoin(self.ipython_dir, u'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS:
316 if os.path.exists(nbext):
316 if os.path.exists(nbext):
317 print("Notebook extensions in %s:" % nbext)
317 print("Notebook extensions in %s:" % nbext)
318 for ext in os.listdir(nbext):
318 for ext in os.listdir(nbext):
319 print(u" %s" % ext)
319 print(u" %s" % ext)
320 else:
320 else:
321 self.check_install()
321 self.check_install()
322 self.install_extensions()
322 self.install_extensions()
323
323
324
324
325 if __name__ == '__main__':
325 if __name__ == '__main__':
326 NBExtensionApp.launch_instance()
326 NBExtensionApp.launch_instance()
327 No newline at end of file
327
General Comments 0
You need to be logged in to leave comments. Login now