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