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