##// END OF EJS Templates
Merge pull request #7609 from jasongrout/install-single-nbextension...
Thomas Kluyver -
r20244:a346be78 merge
parent child Browse files
Show More
@@ -1,358 +1,350 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(src).st_mtime - os.stat(dest).st_mtime > 1e-6:
65 65 # we add a fudge factor to work around a bug in python 2.x
66 66 # that was fixed in python 3.x: http://bugs.python.org/issue12904
67 67 if verbose >= 2:
68 68 print("%s is out of date" % dest)
69 69 return True
70 70 if verbose >= 2:
71 71 print("%s is up to date" % dest)
72 72 return False
73 73
74 74
75 75 def _maybe_copy(src, dest, verbose=1):
76 76 """copy a file if it needs updating"""
77 77 if _should_copy(src, dest, verbose):
78 78 if verbose >= 1:
79 79 print("copying %s -> %s" % (src, dest))
80 80 shutil.copy2(src, dest)
81 81
82 82 def _safe_is_tarfile(path):
83 83 """safe version of is_tarfile, return False on IOError"""
84 84 try:
85 85 return tarfile.is_tarfile(path)
86 86 except IOError:
87 87 return False
88 88
89 89
90 90 def _get_nbext_dir(nbextensions_dir=None, user=False, prefix=None):
91 91 """Return the nbextension directory specified"""
92 92 if sum(map(bool, [user, prefix, nbextensions_dir])) > 1:
93 93 raise ArgumentConflict("Cannot specify more than one of user, prefix, or nbextensions_dir.")
94 94 if user:
95 95 nbext = pjoin(get_ipython_dir(), u'nbextensions')
96 96 else:
97 97 if prefix:
98 98 nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
99 99 elif nbextensions_dir:
100 100 nbext = nbextensions_dir
101 101 else:
102 102 nbext = SYSTEM_NBEXTENSIONS_INSTALL_DIR
103 103 return nbext
104 104
105 105
106 106 def check_nbextension(files, user=False, prefix=None, nbextensions_dir=None):
107 107 """Check whether nbextension files have been installed
108 108
109 109 Returns True if all files are found, False if any are missing.
110 110
111 111 Parameters
112 112 ----------
113 113
114 114 files : list(paths)
115 115 a list of relative paths within nbextensions.
116 116 user : bool [default: False]
117 117 Whether to check the user's .ipython/nbextensions directory.
118 118 Otherwise check a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
119 119 prefix : str [optional]
120 120 Specify install prefix, if it should differ from default (e.g. /usr/local).
121 121 Will check prefix/share/jupyter/nbextensions
122 122 nbextensions_dir : str [optional]
123 123 Specify absolute path of nbextensions directory explicitly.
124 124 """
125 125 nbext = _get_nbext_dir(nbextensions_dir, user, prefix)
126 126 # make sure nbextensions dir exists
127 127 if not os.path.exists(nbext):
128 128 return False
129 129
130 130 if isinstance(files, string_types):
131 131 # one file given, turn it into a list
132 132 files = [files]
133 133
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.
141 141 By default, this compares modification time, and only stages files that need updating.
142 142 If `overwrite` is specified, matching files are purged before proceeding.
143 143
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.
155 153 symlink : bool [default: False]
156 154 If True, create a symlink in nbextensions, rather than copying files.
157 155 Not allowed with URLs or archives. Windows support for symlinks requires
158 156 Vista or above, Python 3, and a permission bit which only admin users
159 157 have by default, so don't rely on it.
160 158 user : bool [default: False]
161 159 Whether to install to the user's .ipython/nbextensions directory.
162 160 Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
163 161 prefix : str [optional]
164 162 Specify install prefix, if it should differ from default (e.g. /usr/local).
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.
171 173 """
172 174 nbext = _get_nbext_dir(nbextensions_dir, user, prefix)
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 archive.extractall(nbext)
208 archive.close()
209 else:
210 if not destination:
211 destination = basename(path)
212 destination = cast_unicode_py2(destination)
213 full_dest = pjoin(nbext, destination)
214 if overwrite and os.path.exists(full_dest):
226 215 if verbose >= 1:
227 print("removing %s" % dest)
228 if os.path.isdir(dest) and not os.path.islink(dest):
229 shutil.rmtree(dest)
216 print("removing %s" % full_dest)
217 if os.path.isdir(full_dest) and not os.path.islink(full_dest):
218 shutil.rmtree(full_dest)
230 219 else:
231 os.remove(dest)
232
220 os.remove(full_dest)
221
233 222 if symlink:
234 223 path = os.path.abspath(path)
235 if not os.path.exists(dest):
224 if not os.path.exists(full_dest):
236 225 if verbose >= 1:
237 print("symlink %s -> %s" % (dest, path))
238 os.symlink(path, dest)
239 continue
240
241 if os.path.isdir(path):
226 print("symlink %s -> %s" % (full_dest, path))
227 os.symlink(path, full_dest)
228 elif os.path.isdir(path):
242 229 path = pjoin(os.path.abspath(path), '') # end in path separator
243 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 232 if not os.path.exists(dest_dir):
246 233 if verbose >= 2:
247 234 print("making directory %s" % dest_dir)
248 235 os.makedirs(dest_dir)
249 236 for file in files:
250 237 src = pjoin(parent, file)
251 238 # print("%r, %r" % (dest_dir, file))
252 239 dest_file = pjoin(dest_dir, file)
253 240 _maybe_copy(src, dest_file, verbose)
254 241 else:
255 242 src = path
256 _maybe_copy(src, dest, verbose)
243 _maybe_copy(src, full_dest, verbose)
257 244
258 245 #----------------------------------------------------------------------
259 246 # install nbextension app
260 247 #----------------------------------------------------------------------
261 248
262 249 from IPython.utils.traitlets import Bool, Enum, Unicode, TraitError
263 250 from IPython.core.application import BaseIPythonApplication
264 251
265 252 flags = {
266 253 "overwrite" : ({
267 254 "NBExtensionApp" : {
268 255 "overwrite" : True,
269 256 }}, "Force overwrite of existing files"
270 257 ),
271 258 "debug" : ({
272 259 "NBExtensionApp" : {
273 260 "verbose" : 2,
274 261 }}, "Extra output"
275 262 ),
276 263 "quiet" : ({
277 264 "NBExtensionApp" : {
278 265 "verbose" : 0,
279 266 }}, "Minimal output"
280 267 ),
281 268 "symlink" : ({
282 269 "NBExtensionApp" : {
283 270 "symlink" : True,
284 }}, "Create symlinks instead of copying files"
271 }}, "Create symlink instead of copying files"
285 272 ),
286 273 "user" : ({
287 274 "NBExtensionApp" : {
288 275 "user" : True,
289 276 }}, "Install to the user's IPython directory"
290 277 ),
291 278 }
292 279 flags['s'] = flags['symlink']
293 280
294 281 aliases = {
295 282 "ipython-dir" : "NBExtensionApp.ipython_dir",
296 283 "prefix" : "NBExtensionApp.prefix",
297 284 "nbextensions" : "NBExtensionApp.nbextensions_dir",
285 "destination" : "NBExtensionApp.destination",
298 286 }
299 287
300 288 class NBExtensionApp(BaseIPythonApplication):
301 289 """Entry point for installing notebook extensions"""
302 290
303 291 description = """Install IPython notebook extensions
304 292
305 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 298 If a URL is given, it will be downloaded.
311 299 If an archive is given, it will be extracted into nbextensions.
312 300 If the requested files are already up to date, no action is taken
313 301 unless --overwrite is specified.
314 302 """
315 303
316 304 examples = """
317 ipython install-nbextension /path/to/d3.js /path/to/myextension
305 ipython install-nbextension /path/to/myextension
318 306 """
319 307 aliases = aliases
320 308 flags = flags
321 309
322 310 overwrite = Bool(False, config=True, help="Force overwrite of existing files")
323 311 symlink = Bool(False, config=True, help="Create symlinks instead of copying files")
324 312 user = Bool(False, config=True, help="Whether to do a user install")
325 313 prefix = Unicode('', config=True, help="Installation prefix")
326 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 316 verbose = Enum((0,1,2), default_value=1, config=True,
328 317 help="Verbosity level"
329 318 )
330 319
331 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 324 overwrite=self.overwrite,
334 325 symlink=self.symlink,
335 326 verbose=self.verbose,
336 327 user=self.user,
337 328 prefix=self.prefix,
329 destination=self.destination,
338 330 nbextensions_dir=self.nbextensions_dir,
339 331 )
340 332
341 333 def start(self):
342 334 if not self.extra_args:
343 335 for nbext in [pjoin(self.ipython_dir, u'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS:
344 336 if os.path.exists(nbext):
345 337 print("Notebook extensions in %s:" % nbext)
346 338 for ext in os.listdir(nbext):
347 339 print(u" %s" % ext)
348 340 else:
349 341 try:
350 342 self.install_extensions()
351 343 except ArgumentConflict as e:
352 344 print(str(e), file=sys.stderr)
353 345 self.exit(1)
354 346
355 347
356 348 if __name__ == '__main__':
357 349 NBExtensionApp.launch_instance()
358 350
@@ -1,293 +1,316 b''
1 1 # coding: utf-8
2 2 """Test installation of notebook extensions"""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import glob
8 8 import os
9 9 import re
10 10 import tarfile
11 11 import zipfile
12 12 from io import BytesIO
13 13 from os.path import basename, join as pjoin
14 14 from unittest import TestCase
15 15
16 16 import IPython.testing.tools as tt
17 17 import IPython.testing.decorators as dec
18 18 from IPython.utils import py3compat
19 19 from IPython.utils.tempdir import TemporaryDirectory
20 20 from IPython.html import nbextensions
21 21 from IPython.html.nbextensions import install_nbextension, check_nbextension
22 22
23 23
24 24 def touch(file, mtime=None):
25 25 """ensure a file exists, and set its modification time
26 26
27 27 returns the modification time of the file
28 28 """
29 29 open(file, 'a').close()
30 30 # set explicit mtime
31 31 if mtime:
32 32 atime = os.stat(file).st_atime
33 33 os.utime(file, (atime, mtime))
34 34 return os.stat(file).st_mtime
35 35
36 36 class TestInstallNBExtension(TestCase):
37 37
38 38 def tempdir(self):
39 39 td = TemporaryDirectory()
40 40 self.tempdirs.append(td)
41 41 return py3compat.cast_unicode(td.name)
42 42
43 43 def setUp(self):
44 44 self.tempdirs = []
45 45 src = self.src = self.tempdir()
46 46 self.files = files = [
47 47 pjoin(u'ƒile'),
48 48 pjoin(u'∂ir', u'ƒile1'),
49 49 pjoin(u'∂ir', u'∂ir2', u'ƒile2'),
50 50 ]
51 51 for file in files:
52 52 fullpath = os.path.join(self.src, file)
53 53 parent = os.path.dirname(fullpath)
54 54 if not os.path.exists(parent):
55 55 os.makedirs(parent)
56 56 touch(fullpath)
57 57
58 58 self.ipdir = self.tempdir()
59 59 self.save_get_ipython_dir = nbextensions.get_ipython_dir
60 60 nbextensions.get_ipython_dir = lambda : self.ipdir
61 61 self.save_system_dir = nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR
62 62 nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = self.tempdir()
63 63
64 64 def tearDown(self):
65 65 nbextensions.get_ipython_dir = self.save_get_ipython_dir
66 66 nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.save_system_dir
67 67 for td in self.tempdirs:
68 68 td.cleanup()
69 69
70 70 def assert_dir_exists(self, path):
71 71 if not os.path.exists(path):
72 72 do_exist = os.listdir(os.path.dirname(path))
73 73 self.fail(u"%s should exist (found %s)" % (path, do_exist))
74 74
75 75 def assert_not_dir_exists(self, path):
76 76 if os.path.exists(path):
77 77 self.fail(u"%s should not exist" % path)
78 78
79 79 def assert_installed(self, relative_path, user=False):
80 80 if user:
81 81 nbext = pjoin(self.ipdir, u'nbextensions')
82 82 else:
83 83 nbext = self.system_nbext
84 84 self.assert_dir_exists(
85 85 pjoin(nbext, relative_path)
86 86 )
87 87
88 88 def assert_not_installed(self, relative_path, user=False):
89 89 if user:
90 90 nbext = pjoin(self.ipdir, u'nbextensions')
91 91 else:
92 92 nbext = self.system_nbext
93 93 self.assert_not_dir_exists(
94 94 pjoin(nbext, relative_path)
95 95 )
96 96
97 97 def test_create_ipython_dir(self):
98 98 """install_nbextension when ipython_dir doesn't exist"""
99 99 with TemporaryDirectory() as td:
100 100 self.ipdir = ipdir = pjoin(td, u'ipython')
101 101 install_nbextension(self.src, user=True)
102 102 self.assert_dir_exists(ipdir)
103 103 for file in self.files:
104 104 self.assert_installed(
105 105 pjoin(basename(self.src), file),
106 106 user=bool(ipdir)
107 107 )
108 108
109 109 def test_create_nbextensions_user(self):
110 110 with TemporaryDirectory() as td:
111 111 self.ipdir = ipdir = pjoin(td, u'ipython')
112 112 install_nbextension(self.src, user=True)
113 113 self.assert_installed(
114 114 pjoin(basename(self.src), u'ƒile'),
115 115 user=True
116 116 )
117 117
118 118 def test_create_nbextensions_system(self):
119 119 with TemporaryDirectory() as td:
120 120 nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = pjoin(td, u'nbextensions')
121 121 install_nbextension(self.src, user=False)
122 122 self.assert_installed(
123 123 pjoin(basename(self.src), u'ƒile'),
124 124 user=False
125 125 )
126 126
127 127 def test_single_file(self):
128 128 file = self.files[0]
129 129 install_nbextension(pjoin(self.src, file))
130 130 self.assert_installed(file)
131 131
132 132 def test_single_dir(self):
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:
146 154 fname = u'ƒ.js'
147 155 src = pjoin(d, fname)
148 156 with open(src, 'w') as f:
149 157 f.write('first')
150 158 mtime = touch(src)
151 159 dest = pjoin(self.system_nbext, fname)
152 160 install_nbextension(src)
153 161 with open(src, 'w') as f:
154 162 f.write('overwrite')
155 163 mtime = touch(src, mtime - 100)
156 164 install_nbextension(src, overwrite=True)
157 165 with open(dest) as f:
158 166 self.assertEqual(f.read(), 'overwrite')
159 167
160 168 def test_overwrite_dir(self):
161 169 with TemporaryDirectory() as src:
162 170 base = basename(src)
163 171 fname = u'ƒ.js'
164 172 touch(pjoin(src, fname))
165 173 install_nbextension(src)
166 174 self.assert_installed(pjoin(base, fname))
167 175 os.remove(pjoin(src, fname))
168 176 fname2 = u'∂.js'
169 177 touch(pjoin(src, fname2))
170 178 install_nbextension(src, overwrite=True)
171 179 self.assert_installed(pjoin(base, fname2))
172 180 self.assert_not_installed(pjoin(base, fname))
173 181
174 182 def test_update_file(self):
175 183 with TemporaryDirectory() as d:
176 184 fname = u'ƒ.js'
177 185 src = pjoin(d, fname)
178 186 with open(src, 'w') as f:
179 187 f.write('first')
180 188 mtime = touch(src)
181 189 install_nbextension(src)
182 190 self.assert_installed(fname)
183 191 dest = pjoin(self.system_nbext, fname)
184 192 old_mtime = os.stat(dest).st_mtime
185 193 with open(src, 'w') as f:
186 194 f.write('overwrite')
187 195 touch(src, mtime + 10)
188 196 install_nbextension(src)
189 197 with open(dest) as f:
190 198 self.assertEqual(f.read(), 'overwrite')
191 199
192 200 def test_skip_old_file(self):
193 201 with TemporaryDirectory() as d:
194 202 fname = u'ƒ.js'
195 203 src = pjoin(d, fname)
196 204 mtime = touch(src)
197 205 install_nbextension(src)
198 206 self.assert_installed(fname)
199 207 dest = pjoin(self.system_nbext, fname)
200 208 old_mtime = os.stat(dest).st_mtime
201 209
202 210 mtime = touch(src, mtime - 100)
203 211 install_nbextension(src)
204 212 new_mtime = os.stat(dest).st_mtime
205 213 self.assertEqual(new_mtime, old_mtime)
206 214
207 215 def test_quiet(self):
208 216 with tt.AssertNotPrints(re.compile(r'.+')):
209 217 install_nbextension(self.src, verbose=0)
210 218
211 219 def test_install_zip(self):
212 220 path = pjoin(self.src, "myjsext.zip")
213 221 with zipfile.ZipFile(path, 'w') as f:
214 222 f.writestr("a.js", b"b();")
215 223 f.writestr("foo/a.js", b"foo();")
216 224 install_nbextension(path)
217 225 self.assert_installed("a.js")
218 226 self.assert_installed(pjoin("foo", "a.js"))
219 227
220 228 def test_install_tar(self):
221 229 def _add_file(f, fname, buf):
222 230 info = tarfile.TarInfo(fname)
223 231 info.size = len(buf)
224 232 f.addfile(info, BytesIO(buf))
225 233
226 234 for i,ext in enumerate((".tar.gz", ".tgz", ".tar.bz2")):
227 235 path = pjoin(self.src, "myjsext" + ext)
228 236 with tarfile.open(path, 'w') as f:
229 237 _add_file(f, "b%i.js" % i, b"b();")
230 238 _add_file(f, "foo/b%i.js" % i, b"foo();")
231 239 install_nbextension(path)
232 240 self.assert_installed("b%i.js" % i)
233 241 self.assert_installed(pjoin("foo", "b%i.js" % i))
234 242
235 243 def test_install_url(self):
236 244 def fake_urlretrieve(url, dest):
237 245 touch(dest)
238 246 save_urlretrieve = nbextensions.urlretrieve
239 247 nbextensions.urlretrieve = fake_urlretrieve
240 248 try:
241 249 install_nbextension("http://example.com/path/to/foo.js")
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
249 258
250 259 def test_check_nbextension(self):
251 260 with TemporaryDirectory() as d:
252 261 f = u'ƒ.js'
253 262 src = pjoin(d, f)
254 263 touch(src)
255 264 install_nbextension(src, user=True)
256 265
257 266 assert check_nbextension(f, user=True)
258 267 assert check_nbextension([f], user=True)
259 268 assert not check_nbextension([f, pjoin('dne', f)], user=True)
260 269
261 270 @dec.skip_win32
262 271 def test_install_symlink(self):
263 272 with TemporaryDirectory() as d:
264 273 f = u'ƒ.js'
265 274 src = pjoin(d, f)
266 275 touch(src)
267 276 install_nbextension(src, symlink=True)
268 277 dest = pjoin(self.system_nbext, f)
269 278 assert os.path.islink(dest)
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)
276 298
277 299 with TemporaryDirectory() as d:
278 300 zf = u'ƒ.zip'
279 301 zsrc = pjoin(d, zf)
280 302 with zipfile.ZipFile(zsrc, 'w') as z:
281 303 z.writestr("a.js", b"b();")
282 304
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