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