##// END OF EJS Templates
allow system-wide paths for nbextensions...
Min RK -
Show More
@@ -8,6 +8,7 b' from __future__ import print_function'
8
8
9 import os
9 import os
10 import shutil
10 import shutil
11 import sys
11 import tarfile
12 import tarfile
12 import zipfile
13 import zipfile
13 from os.path import basename, join as pjoin
14 from os.path import basename, join as pjoin
@@ -25,6 +26,34 b' from IPython.utils.py3compat import string_types, cast_unicode_py2'
25 from IPython.utils.tempdir import TemporaryDirectory
26 from IPython.utils.tempdir import TemporaryDirectory
26
27
27
28
29 # Packagers: modify the next block if you store system-installed nbextensions elsewhere (unlikely)
30 SYSTEM_NBEXTENSIONS_DIRS = []
31
32 if os.name == 'nt':
33 programdata = os.environ.get('PROGRAMDATA', None)
34 if programdata: # PROGRAMDATA is not defined by default on XP.
35 SYSTEM_NBEXTENSIONS_DIRS = [pjoin(programdata, 'jupyter', 'nbextensions')]
36 prefixes = []
37 else:
38 prefixes = ['/usr/local', '/usr']
39
40 # add sys.prefix at the front
41 if sys.prefix not in prefixes:
42 prefixes.insert(0, sys.prefix)
43
44 for prefix in prefixes:
45 nbext = os.path.join(prefix, 'share', 'jupyter', 'nbextensions')
46 if nbext not in SYSTEM_NBEXTENSIONS_DIRS:
47 SYSTEM_NBEXTENSIONS_DIRS.append(nbext)
48
49 if os.name == 'nt':
50 # PROGRAMDATA
51 SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-1]
52 else:
53 # /usr/local
54 SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-2]
55
56
28 def _should_copy(src, dest, verbose=1):
57 def _should_copy(src, dest, verbose=1):
29 """should a file be copied?"""
58 """should a file be copied?"""
30 if not os.path.exists(dest):
59 if not os.path.exists(dest):
@@ -54,15 +83,17 b' def _safe_is_tarfile(path):'
54 return False
83 return False
55
84
56
85
57 def check_nbextension(files, ipython_dir=None):
86 def check_nbextension(files, nbextensions=None):
58 """Check whether nbextension files have been installed
87 """Check whether nbextension files have been installed
59
88
60 files should be a list of relative paths within nbextensions.
89 files should be a list of relative paths within nbextensions.
61
90
62 Returns True if all files are found, False if any are missing.
91 Returns True if all files are found, False if any are missing.
63 """
92 """
64 ipython_dir = ipython_dir or get_ipython_dir()
93 if nbextensions:
65 nbext = pjoin(ipython_dir, u'nbextensions')
94 nbext = nbextensions
95 else:
96 nbext = pjoin(get_ipython_dir(), u'nbextensions')
66 # make sure nbextensions dir exists
97 # make sure nbextensions dir exists
67 if not os.path.exists(nbext):
98 if not os.path.exists(nbext):
68 return False
99 return False
@@ -74,7 +105,7 b' def check_nbextension(files, ipython_dir=None):'
74 return all(os.path.exists(pjoin(nbext, f)) for f in files)
105 return all(os.path.exists(pjoin(nbext, f)) for f in files)
75
106
76
107
77 def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None, verbose=1):
108 def install_nbextension(files, overwrite=False, symlink=False, user=False, prefix=None, nbextensions=None, verbose=1):
78 """Install a Javascript extension for the notebook
109 """Install a Javascript extension for the notebook
79
110
80 Stages files and/or directories into IPYTHONDIR/nbextensions.
111 Stages files and/or directories into IPYTHONDIR/nbextensions.
@@ -96,16 +127,29 b' def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None,'
96 Not allowed with URLs or archives. Windows support for symlinks requires
127 Not allowed with URLs or archives. Windows support for symlinks requires
97 Vista or above, Python 3, and a permission bit which only admin users
128 Vista or above, Python 3, and a permission bit which only admin users
98 have by default, so don't rely on it.
129 have by default, so don't rely on it.
99 ipython_dir : str [optional]
130 user : bool [default: False]
100 The path to an IPython directory, if the default value is not desired.
131 Whether to install to the user's .ipython/nbextensions directory.
101 get_ipython_dir() is used by default.
132 Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
133 prefix : str [optional]
134 Specify install prefix, if it should differ from default (e.g. /usr/local).
135 Will install to prefix/share/jupyter/nbextensions
136 nbextensions : str [optional]
137 Specify absolute path of nbextensions directory explicitly.
102 verbose : int [default: 1]
138 verbose : int [default: 1]
103 Set verbosity level. The default is 1, where file actions are printed.
139 Set verbosity level. The default is 1, where file actions are printed.
104 set verbose=2 for more output, or verbose=0 for silence.
140 set verbose=2 for more output, or verbose=0 for silence.
105 """
141 """
106
142 if sum(map(bool, [user, prefix, nbextensions])) > 1:
107 ipython_dir = ipython_dir or get_ipython_dir()
143 raise ValueError("Cannot specify more than one of user, prefix, or nbextensions.")
108 nbext = pjoin(ipython_dir, u'nbextensions')
144 if user:
145 nbext = pjoin(get_ipython_dir(), u'nbextensions')
146 else:
147 if prefix:
148 nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
149 elif nbextensions:
150 nbext = nbextensions
151 else:
152 nbext = SYSTEM_NBEXTENSIONS_INSTALL_DIR
109 # make sure nbextensions dir exists
153 # make sure nbextensions dir exists
110 ensure_dir_exists(nbext)
154 ensure_dir_exists(nbext)
111
155
@@ -126,7 +170,7 b' def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None,'
126 print("downloading %s to %s" % (path, local_path))
170 print("downloading %s to %s" % (path, local_path))
127 urlretrieve(path, local_path)
171 urlretrieve(path, local_path)
128 # now install from the local copy
172 # now install from the local copy
129 install_nbextension(local_path, overwrite, symlink, ipython_dir, verbose)
173 install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions=nbext, verbose=verbose)
130 continue
174 continue
131
175
132 # handle archives
176 # handle archives
@@ -183,7 +227,7 b' def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None,'
183 # install nbextension app
227 # install nbextension app
184 #----------------------------------------------------------------------
228 #----------------------------------------------------------------------
185
229
186 from IPython.utils.traitlets import Bool, Enum
230 from IPython.utils.traitlets import Bool, Enum, Unicode, TraitError
187 from IPython.core.application import BaseIPythonApplication
231 from IPython.core.application import BaseIPythonApplication
188
232
189 flags = {
233 flags = {
@@ -207,11 +251,18 b' flags = {'
207 "symlink" : True,
251 "symlink" : True,
208 }}, "Create symlinks instead of copying files"
252 }}, "Create symlinks instead of copying files"
209 ),
253 ),
254 "user" : ({
255 "NBExtensionApp" : {
256 "user" : True,
257 }}, "Install to the user's IPython directory"
258 ),
210 }
259 }
211 flags['s'] = flags['symlink']
260 flags['s'] = flags['symlink']
212
261
213 aliases = {
262 aliases = {
214 "ipython-dir" : "NBExtensionApp.ipython_dir"
263 "ipython-dir" : "NBExtensionApp.ipython_dir",
264 "prefix" : "NBExtensionApp.prefix",
265 "nbextensions" : "NBExtensionApp.nbextensions",
215 }
266 }
216
267
217 class NBExtensionApp(BaseIPythonApplication):
268 class NBExtensionApp(BaseIPythonApplication):
@@ -238,25 +289,36 b' class NBExtensionApp(BaseIPythonApplication):'
238
289
239 overwrite = Bool(False, config=True, help="Force overwrite of existing files")
290 overwrite = Bool(False, config=True, help="Force overwrite of existing files")
240 symlink = Bool(False, config=True, help="Create symlinks instead of copying files")
291 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")
293 prefix = Unicode('', config=True, help="Installation prefix")
294 nbextensions = Unicode('', config=True, help="Full path to nbextensions (probably use prefix or user)")
241 verbose = Enum((0,1,2), default_value=1, config=True,
295 verbose = Enum((0,1,2), default_value=1, config=True,
242 help="Verbosity level"
296 help="Verbosity level"
243 )
297 )
244
298
299 def check_install():
300 if sum(map(bool, [user, prefix, nbextensions])) > 1:
301 raise TraitError("Cannot specify more than one of user, prefix, or nbextensions.")
302
245 def install_extensions(self):
303 def install_extensions(self):
246 install_nbextension(self.extra_args,
304 install_nbextension(self.extra_args,
247 overwrite=self.overwrite,
305 overwrite=self.overwrite,
248 symlink=self.symlink,
306 symlink=self.symlink,
249 verbose=self.verbose,
307 verbose=self.verbose,
250 ipython_dir=self.ipython_dir,
308 user=self.user,
309 prefix=self.prefix,
310 nbextensions=self.nbextensions,
251 )
311 )
252
312
253 def start(self):
313 def start(self):
254 if not self.extra_args:
314 if not self.extra_args:
255 nbext = pjoin(self.ipython_dir, u'nbextensions')
315 for nbext in [pjoin(self.ipython_dir, u'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS:
256 print("Notebook extensions in %s:" % nbext)
316 if os.path.exists(nbext):
257 for ext in os.listdir(nbext):
317 print("Notebook extensions in %s:" % nbext)
258 print(u" %s" % ext)
318 for ext in os.listdir(nbext):
319 print(u" %s" % ext)
259 else:
320 else:
321 self.check_install()
260 self.install_extensions()
322 self.install_extensions()
261
323
262
324
@@ -90,6 +90,7 b' from IPython.utils import py3compat'
90 from IPython.utils.path import filefind, get_ipython_dir
90 from IPython.utils.path import filefind, get_ipython_dir
91 from IPython.utils.sysinfo import get_sys_info
91 from IPython.utils.sysinfo import get_sys_info
92
92
93 from .nbextensions import SYSTEM_NBEXTENSIONS_DIRS
93 from .utils import url_path_join
94 from .utils import url_path_join
94
95
95 #-----------------------------------------------------------------------------
96 #-----------------------------------------------------------------------------
@@ -566,11 +567,14 b' class NotebookApp(BaseIPythonApplication):'
566 """return extra paths + the default locations"""
567 """return extra paths + the default locations"""
567 return self.extra_template_paths + DEFAULT_TEMPLATE_PATH_LIST
568 return self.extra_template_paths + DEFAULT_TEMPLATE_PATH_LIST
568
569
569 nbextensions_path = List(Unicode, config=True,
570 extra_nbextensions_path = List(Unicode, config=True,
570 help="""paths for Javascript extensions. By default, this is just IPYTHONDIR/nbextensions"""
571 help="""extra paths to look for Javascript notebook extensions"""
571 )
572 )
572 def _nbextensions_path_default(self):
573
573 return [os.path.join(get_ipython_dir(), 'nbextensions')]
574 @property
575 def nbextensions_path(self):
576 """The path to look for Javascript notebook extensions"""
577 return self.extra_nbextensions_path + [os.path.join(get_ipython_dir(), 'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS
574
578
575 websocket_url = Unicode("", config=True,
579 websocket_url = Unicode("", config=True,
576 help="""The base URL for websockets,
580 help="""The base URL for websockets,
@@ -1,15 +1,8 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Test installation of notebook extensions"""
2 """Test installation of notebook extensions"""
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2014 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
3
10 #-----------------------------------------------------------------------------
4 # Copyright (c) IPython Development Team.
11 # Imports
5 # Distributed under the terms of the Modified BSD License.
12 #-----------------------------------------------------------------------------
13
6
14 import glob
7 import glob
15 import os
8 import os
@@ -27,9 +20,6 b' from IPython.utils.tempdir import TemporaryDirectory'
27 from IPython.html import nbextensions
20 from IPython.html import nbextensions
28 from IPython.html.nbextensions import install_nbextension, check_nbextension
21 from IPython.html.nbextensions import install_nbextension, check_nbextension
29
22
30 #-----------------------------------------------------------------------------
31 # Test functions
32 #-----------------------------------------------------------------------------
33
23
34 def touch(file, mtime=None):
24 def touch(file, mtime=None):
35 """ensure a file exists, and set its modification time
25 """ensure a file exists, and set its modification time
@@ -43,7 +33,6 b' def touch(file, mtime=None):'
43 os.utime(file, (atime, mtime))
33 os.utime(file, (atime, mtime))
44 return os.stat(file).st_mtime
34 return os.stat(file).st_mtime
45
35
46
47 class TestInstallNBExtension(TestCase):
36 class TestInstallNBExtension(TestCase):
48
37
49 def tempdir(self):
38 def tempdir(self):
@@ -69,12 +58,15 b' class TestInstallNBExtension(TestCase):'
69 self.ipdir = self.tempdir()
58 self.ipdir = self.tempdir()
70 self.save_get_ipython_dir = nbextensions.get_ipython_dir
59 self.save_get_ipython_dir = nbextensions.get_ipython_dir
71 nbextensions.get_ipython_dir = lambda : self.ipdir
60 nbextensions.get_ipython_dir = lambda : self.ipdir
61 self.save_system_dir = nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR
62 nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = self.tempdir()
72
63
73 def tearDown(self):
64 def tearDown(self):
65 nbextensions.get_ipython_dir = self.save_get_ipython_dir
66 nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.save_system_dir
74 for td in self.tempdirs:
67 for td in self.tempdirs:
75 td.cleanup()
68 td.cleanup()
76 nbextensions.get_ipython_dir = self.save_get_ipython_dir
69
77
78 def assert_dir_exists(self, path):
70 def assert_dir_exists(self, path):
79 if not os.path.exists(path):
71 if not os.path.exists(path):
80 do_exist = os.listdir(os.path.dirname(path))
72 do_exist = os.listdir(os.path.dirname(path))
@@ -84,21 +76,29 b' class TestInstallNBExtension(TestCase):'
84 if os.path.exists(path):
76 if os.path.exists(path):
85 self.fail(u"%s should not exist" % path)
77 self.fail(u"%s should not exist" % path)
86
78
87 def assert_installed(self, relative_path, ipdir=None):
79 def assert_installed(self, relative_path, user=False):
80 if user:
81 nbext = pjoin(self.ipdir, u'nbextensions')
82 else:
83 nbext = self.system_nbext
88 self.assert_dir_exists(
84 self.assert_dir_exists(
89 pjoin(ipdir or self.ipdir, u'nbextensions', relative_path)
85 pjoin(nbext, relative_path)
90 )
86 )
91
87
92 def assert_not_installed(self, relative_path, ipdir=None):
88 def assert_not_installed(self, relative_path, user=False):
89 if user:
90 nbext = pjoin(self.ipdir, u'nbextensions')
91 else:
92 nbext = self.system_nbext
93 self.assert_not_dir_exists(
93 self.assert_not_dir_exists(
94 pjoin(ipdir or self.ipdir, u'nbextensions', relative_path)
94 pjoin(nbext, relative_path)
95 )
95 )
96
96
97 def test_create_ipython_dir(self):
97 def test_create_ipython_dir(self):
98 """install_nbextension when ipython_dir doesn't exist"""
98 """install_nbextension when ipython_dir doesn't exist"""
99 with TemporaryDirectory() as td:
99 with TemporaryDirectory() as td:
100 ipdir = pjoin(td, u'ipython')
100 self.ipdir = ipdir = pjoin(td, u'ipython')
101 install_nbextension(self.src, ipython_dir=ipdir)
101 install_nbextension(self.src, user=True)
102 self.assert_dir_exists(ipdir)
102 self.assert_dir_exists(ipdir)
103 for file in self.files:
103 for file in self.files:
104 self.assert_installed(
104 self.assert_installed(
@@ -106,12 +106,22 b' class TestInstallNBExtension(TestCase):'
106 ipdir
106 ipdir
107 )
107 )
108
108
109 def test_create_nbextensions(self):
109 def test_create_nbextensions_user(self):
110 with TemporaryDirectory() as ipdir:
110 with TemporaryDirectory() as td:
111 install_nbextension(self.src, ipython_dir=ipdir)
111 self.ipdir = ipdir = pjoin(td, u'ipython')
112 install_nbextension(self.src, user=True)
113 self.assert_installed(
114 pjoin(basename(self.src), u'ƒile'),
115 user=True
116 )
117
118 def test_create_nbextensions_system(self):
119 with TemporaryDirectory() as td:
120 nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = pjoin(td, u'nbextensions')
121 install_nbextension(self.src, user=False)
112 self.assert_installed(
122 self.assert_installed(
113 pjoin(basename(self.src), u'ƒile'),
123 pjoin(basename(self.src), u'ƒile'),
114 ipdir
124 user=False
115 )
125 )
116
126
117 def test_single_file(self):
127 def test_single_file(self):
@@ -136,7 +146,7 b' class TestInstallNBExtension(TestCase):'
136 with open(src, 'w') as f:
146 with open(src, 'w') as f:
137 f.write('first')
147 f.write('first')
138 mtime = touch(src)
148 mtime = touch(src)
139 dest = pjoin(self.ipdir, u'nbextensions', fname)
149 dest = pjoin(self.system_nbext, fname)
140 install_nbextension(src)
150 install_nbextension(src)
141 with open(src, 'w') as f:
151 with open(src, 'w') as f:
142 f.write('overwrite')
152 f.write('overwrite')
@@ -147,7 +157,6 b' class TestInstallNBExtension(TestCase):'
147
157
148 def test_overwrite_dir(self):
158 def test_overwrite_dir(self):
149 with TemporaryDirectory() as src:
159 with TemporaryDirectory() as src:
150 # src = py3compat.cast_unicode_py2(src)
151 base = basename(src)
160 base = basename(src)
152 fname = u'ƒ.js'
161 fname = u'ƒ.js'
153 touch(pjoin(src, fname))
162 touch(pjoin(src, fname))
@@ -169,7 +178,7 b' class TestInstallNBExtension(TestCase):'
169 mtime = touch(src)
178 mtime = touch(src)
170 install_nbextension(src)
179 install_nbextension(src)
171 self.assert_installed(fname)
180 self.assert_installed(fname)
172 dest = pjoin(self.ipdir, u'nbextensions', fname)
181 dest = pjoin(self.system_nbext, fname)
173 old_mtime = os.stat(dest).st_mtime
182 old_mtime = os.stat(dest).st_mtime
174 with open(src, 'w') as f:
183 with open(src, 'w') as f:
175 f.write('overwrite')
184 f.write('overwrite')
@@ -185,7 +194,7 b' class TestInstallNBExtension(TestCase):'
185 mtime = touch(src)
194 mtime = touch(src)
186 install_nbextension(src)
195 install_nbextension(src)
187 self.assert_installed(fname)
196 self.assert_installed(fname)
188 dest = pjoin(self.ipdir, u'nbextensions', fname)
197 dest = pjoin(self.system_nbext, fname)
189 old_mtime = os.stat(dest).st_mtime
198 old_mtime = os.stat(dest).st_mtime
190
199
191 mtime = touch(src, mtime - 100)
200 mtime = touch(src, mtime - 100)
@@ -239,11 +248,12 b' class TestInstallNBExtension(TestCase):'
239 f = u'ƒ.js'
248 f = u'ƒ.js'
240 src = pjoin(d, f)
249 src = pjoin(d, f)
241 touch(src)
250 touch(src)
242 install_nbextension(src)
251 install_nbextension(src, user=True)
243
252
244 assert check_nbextension(f, self.ipdir)
253 nbext = pjoin(self.ipdir, u'nbextensions')
245 assert check_nbextension([f], self.ipdir)
254 assert check_nbextension(f, nbext)
246 assert not check_nbextension([f, pjoin('dne', f)], self.ipdir)
255 assert check_nbextension([f], nbext)
256 assert not check_nbextension([f, pjoin('dne', f)], nbext)
247
257
248 @dec.skip_win32
258 @dec.skip_win32
249 def test_install_symlink(self):
259 def test_install_symlink(self):
@@ -252,7 +262,7 b' class TestInstallNBExtension(TestCase):'
252 src = pjoin(d, f)
262 src = pjoin(d, f)
253 touch(src)
263 touch(src)
254 install_nbextension(src, symlink=True)
264 install_nbextension(src, symlink=True)
255 dest = pjoin(self.ipdir, u'nbextensions', f)
265 dest = pjoin(self.system_nbext, f)
256 assert os.path.islink(dest)
266 assert os.path.islink(dest)
257 link = os.readlink(dest)
267 link = os.readlink(dest)
258 self.assertEqual(link, src)
268 self.assertEqual(link, src)
General Comments 0
You need to be logged in to leave comments. Login now