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