##// END OF EJS Templates
Change install_nbextension to take install only a single nbextension (file, folder, archive, url), with an optional destination argument
Jason Grout -
Show More
@@ -134,7 +134,7 b' def check_nbextension(files, user=False, prefix=None, nbextensions_dir=None):'
134 134 return all(os.path.exists(pjoin(nbext, f)) for f in files)
135 135
136 136
137 def install_nbextension(files, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, verbose=1):
137 def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, verbose=1):
138 138 """Install a Javascript extension for the notebook
139 139
140 140 Stages files and/or directories into the nbextensions directory.
@@ -144,11 +144,9 b' def install_nbextension(files, overwrite=False, symlink=False, user=False, prefi'
144 144 Parameters
145 145 ----------
146 146
147 files : list(paths or URLs) or dict(install_name: path or URL)
148 One or more paths or URLs to existing files directories to install.
149 If given as a list, these will be installed with their base name, so '/path/to/foo'
150 will install to 'nbextensions/foo'. If given as a dict, such as {'bar': '/path/to/foo'},
151 then '/path/to/foo' will install to 'nbextensions/bar'.
147 path : path to file, directory, zip or tarball archive, or URL to install
148 By default, the file will be installed with its base name, so '/path/to/foo'
149 will install to 'nbextensions/foo'. See the destination argument below to change this.
152 150 Archives (zip or tarballs) will be extracted into the nbextensions directory.
153 151 overwrite : bool [default: False]
154 152 If True, always install the files, regardless of what may already be installed.
@@ -165,6 +163,10 b' def install_nbextension(files, overwrite=False, symlink=False, user=False, prefi'
165 163 Will install to prefix/share/jupyter/nbextensions
166 164 nbextensions_dir : str [optional]
167 165 Specify absolute path of nbextensions directory explicitly.
166 destination : str [optional]
167 name the nbextension is installed to. For example, if destination is 'foo', then
168 the source file will be installed to 'nbextensions/foo', regardless of the source name.
169 This cannot be specified if an archive is given as the source.
168 170 verbose : int [default: 1]
169 171 Set verbosity level. The default is 1, where file actions are printed.
170 172 set verbose=2 for more output, or verbose=0 for silence.
@@ -173,75 +175,61 b' def install_nbextension(files, overwrite=False, symlink=False, user=False, prefi'
173 175 # make sure nbextensions dir exists
174 176 ensure_dir_exists(nbext)
175 177
176 if isinstance(files, string_types):
177 # one file given, turn it into a list
178 files = [files]
179 if isinstance(files, (list,tuple)):
180 # list given, turn into dict
181 _files = {}
182 for path in map(cast_unicode_py2, files):
183 if path.startswith(('https://', 'http://')):
184 destination = urlparse(path).path.split('/')[-1]
185 elif path.endswith('.zip') or _safe_is_tarfile(path):
186 destination = str(uuid.uuid4()) # ignored for archives
187 else:
188 destination = basename(path)
189 _files[destination] = path
190 files = _files
178 if isinstance(path, (list, tuple)):
179 raise TypeError("path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions")
191 180
192 for dest_basename,path in (map(cast_unicode_py2, item) for item in files.items()):
193
194 if path.startswith(('https://', 'http://')):
195 if symlink:
196 raise ValueError("Cannot symlink from URLs")
197 # Given a URL, download it
198 with TemporaryDirectory() as td:
199 filename = urlparse(path).path.split('/')[-1]
200 local_path = os.path.join(td, filename)
201 if verbose >= 1:
202 print("downloading %s to %s" % (path, local_path))
203 urlretrieve(path, local_path)
204 # now install from the local copy
205 install_nbextension({dest_basename: local_path}, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, verbose=verbose)
206 continue
207
208 # handle archives
209 archive = None
181 path = cast_unicode_py2(path)
182
183 if path.startswith(('https://', 'http://')):
184 if symlink:
185 raise ValueError("Cannot symlink from URLs")
186 # Given a URL, download it
187 with TemporaryDirectory() as td:
188 filename = urlparse(path).path.split('/')[-1]
189 local_path = os.path.join(td, filename)
190 if verbose >= 1:
191 print("downloading %s to %s" % (path, local_path))
192 urlretrieve(path, local_path)
193 # now install from the local copy
194 install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, verbose=verbose)
195 elif path.endswith('.zip') or _safe_is_tarfile(path):
196 if symlink:
197 raise ValueError("Cannot symlink from archives")
198 if destination:
199 raise ValueError("Cannot give destination for archives")
200 if verbose >= 1:
201 print("extracting %s to %s" % (path, nbext))
202
210 203 if path.endswith('.zip'):
211 204 archive = zipfile.ZipFile(path)
212 205 elif _safe_is_tarfile(path):
213 206 archive = tarfile.open(path)
214
215 if archive:
216 if symlink:
217 raise ValueError("Cannot symlink from archives")
218 if verbose >= 1:
219 print("extracting %s to %s" % (path, nbext))
220 archive.extractall(nbext)
221 archive.close()
222 continue
223
224 dest = pjoin(nbext, dest_basename)
225 if overwrite and os.path.exists(dest):
207 else:
208 raise ValueError("Could not extract archive")
209 archive.extractall(nbext)
210 archive.close()
211 else:
212 if not destination:
213 destination = basename(path)
214 full_dest = pjoin(nbext, destination)
215 if overwrite and os.path.exists(full_dest):
226 216 if verbose >= 1:
227 print("removing %s" % dest)
228 if os.path.isdir(dest) and not os.path.islink(dest):
229 shutil.rmtree(dest)
217 print("removing %s" % full_dest)
218 if os.path.isdir(full_dest) and not os.path.islink(full_dest):
219 shutil.rmtree(full_dest)
230 220 else:
231 os.remove(dest)
232
221 os.remove(full_dest)
222
233 223 if symlink:
234 224 path = os.path.abspath(path)
235 if not os.path.exists(dest):
225 if not os.path.exists(full_dest):
236 226 if verbose >= 1:
237 print("symlink %s -> %s" % (dest, path))
238 os.symlink(path, dest)
239 continue
240
241 if os.path.isdir(path):
227 print("symlink %s -> %s" % (full_dest, path))
228 os.symlink(path, full_dest)
229 elif os.path.isdir(path):
242 230 path = pjoin(os.path.abspath(path), '') # end in path separator
243 231 for parent, dirs, files in os.walk(path):
244 dest_dir = pjoin(dest, parent[len(path):])
232 dest_dir = pjoin(full_dest, parent[len(path):])
245 233 if not os.path.exists(dest_dir):
246 234 if verbose >= 2:
247 235 print("making directory %s" % dest_dir)
@@ -281,7 +269,7 b' flags = {'
281 269 "symlink" : ({
282 270 "NBExtensionApp" : {
283 271 "symlink" : True,
284 }}, "Create symlinks instead of copying files"
272 }}, "Create symlink instead of copying files"
285 273 ),
286 274 "user" : ({
287 275 "NBExtensionApp" : {
@@ -295,6 +283,7 b' aliases = {'
295 283 "ipython-dir" : "NBExtensionApp.ipython_dir",
296 284 "prefix" : "NBExtensionApp.prefix",
297 285 "nbextensions" : "NBExtensionApp.nbextensions_dir",
286 "destination" : "NBExtensionApp.destination",
298 287 }
299 288
300 289 class NBExtensionApp(BaseIPythonApplication):
@@ -304,9 +293,9 b' class NBExtensionApp(BaseIPythonApplication):'
304 293
305 294 Usage
306 295
307 ipython install-nbextension file [more files, folders, archives or urls]
296 ipython install-nbextension [file, folder, archive, or url]
308 297
309 This copies files and/or folders into the IPython nbextensions directory.
298 This copies a file and/or a folder into the IPython nbextensions directory.
310 299 If a URL is given, it will be downloaded.
311 300 If an archive is given, it will be extracted into nbextensions.
312 301 If the requested files are already up to date, no action is taken
@@ -314,7 +303,7 b' class NBExtensionApp(BaseIPythonApplication):'
314 303 """
315 304
316 305 examples = """
317 ipython install-nbextension /path/to/d3.js /path/to/myextension
306 ipython install-nbextension /path/to/myextension
318 307 """
319 308 aliases = aliases
320 309 flags = flags
@@ -324,6 +313,7 b' class NBExtensionApp(BaseIPythonApplication):'
324 313 user = Bool(False, config=True, help="Whether to do a user install")
325 314 prefix = Unicode('', config=True, help="Installation prefix")
326 315 nbextensions_dir = Unicode('', config=True, help="Full path to nbextensions dir (probably use prefix or user)")
316 destination = Unicode('', config=True, help="Destination for the copy or symlink")
327 317 verbose = Enum((0,1,2), default_value=1, config=True,
328 318 help="Verbosity level"
329 319 )
@@ -335,6 +325,7 b' class NBExtensionApp(BaseIPythonApplication):'
335 325 verbose=self.verbose,
336 326 user=self.user,
337 327 prefix=self.prefix,
328 destination=self.destination,
338 329 nbextensions_dir=self.nbextensions_dir,
339 330 )
340 331
@@ -133,13 +133,21 b' class TestInstallNBExtension(TestCase):'
133 133 d = u'∂ir'
134 134 install_nbextension(pjoin(self.src, d))
135 135 self.assert_installed(self.files[-1])
136 install_nbextension({'test': pjoin(self.src, d)})
137 self.assert_installed(pjoin('test', u'∂ir2', u'ƒile2'))
136
137
138 def test_destination_file(self):
139 file = self.files[0]
140 install_nbextension(pjoin(self.src, file), destination = u'ƒiledest')
141 self.assert_installed(u'ƒiledest')
142
143 def test_destination_dir(self):
144 d = u'∂ir'
145 install_nbextension(pjoin(self.src, d), destination = u'ƒiledest2')
146 self.assert_installed(pjoin(u'ƒiledest2', u'∂ir2', u'ƒile2'))
138 147
139 148 def test_install_nbextension(self):
140 install_nbextension(glob.glob(pjoin(self.src, '*')))
141 for file in self.files:
142 self.assert_installed(file)
149 with self.assertRaises(TypeError):
150 install_nbextension(glob.glob(pjoin(self.src, '*')))
143 151
144 152 def test_overwrite_file(self):
145 153 with TemporaryDirectory() as d:
@@ -242,7 +250,8 b' class TestInstallNBExtension(TestCase):'
242 250 self.assert_installed("foo.js")
243 251 install_nbextension("https://example.com/path/to/another/bar.js")
244 252 self.assert_installed("bar.js")
245 install_nbextension({'foobar.js': "https://example.com/path/to/another/bar.js"})
253 install_nbextension("https://example.com/path/to/another/bar.js",
254 destination = 'foobar.js')
246 255 self.assert_installed("foobar.js")
247 256 finally:
248 257 nbextensions.urlretrieve = save_urlretrieve
@@ -270,6 +279,19 b' class TestInstallNBExtension(TestCase):'
270 279 link = os.readlink(dest)
271 280 self.assertEqual(link, src)
272 281
282 @dec.skip_win32
283 def test_install_symlink_destination(self):
284 with TemporaryDirectory() as d:
285 f = u'ƒ.js'
286 flink = u'ƒlink.js'
287 src = pjoin(d, f)
288 touch(src)
289 install_nbextension(src, symlink=True, destination=flink)
290 dest = pjoin(self.system_nbext, flink)
291 assert os.path.islink(dest)
292 link = os.readlink(dest)
293 self.assertEqual(link, src)
294
273 295 def test_install_symlink_bad(self):
274 296 with self.assertRaises(ValueError):
275 297 install_nbextension("http://example.com/foo.js", symlink=True)
@@ -283,11 +305,12 b' class TestInstallNBExtension(TestCase):'
283 305 with self.assertRaises(ValueError):
284 306 install_nbextension(zsrc, symlink=True)
285 307
286 def test_install_different_name(self):
308 def test_install_destination_bad(self):
287 309 with TemporaryDirectory() as d:
288 f = u'ƒ.js'
289 src = pjoin(d, f)
290 dest_f = u'ƒile.js'
291 touch(src)
292 install_nbextension({dest_f: src})
293 self.assert_installed(dest_f)
310 zf = u'ƒ.zip'
311 zsrc = pjoin(d, zf)
312 with zipfile.ZipFile(zsrc, 'w') as z:
313 z.writestr("a.js", b"b();")
314
315 with self.assertRaises(ValueError):
316 install_nbextension(zsrc, destination='foo')
General Comments 0
You need to be logged in to leave comments. Login now