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