##// 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 9 import os
10 10 import shutil
11 import sys
11 12 import tarfile
12 13 import zipfile
13 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 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 57 def _should_copy(src, dest, verbose=1):
29 58 """should a file be copied?"""
30 59 if not os.path.exists(dest):
@@ -54,15 +83,17 b' def _safe_is_tarfile(path):'
54 83 return False
55 84
56 85
57 def check_nbextension(files, ipython_dir=None):
86 def check_nbextension(files, nbextensions=None):
58 87 """Check whether nbextension files have been installed
59 88
60 89 files should be a list of relative paths within nbextensions.
61 90
62 91 Returns True if all files are found, False if any are missing.
63 92 """
64 ipython_dir = ipython_dir or get_ipython_dir()
65 nbext = pjoin(ipython_dir, u'nbextensions')
93 if nbextensions:
94 nbext = nbextensions
95 else:
96 nbext = pjoin(get_ipython_dir(), u'nbextensions')
66 97 # make sure nbextensions dir exists
67 98 if not os.path.exists(nbext):
68 99 return False
@@ -74,7 +105,7 b' def check_nbextension(files, ipython_dir=None):'
74 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 109 """Install a Javascript extension for the notebook
79 110
80 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 127 Not allowed with URLs or archives. Windows support for symlinks requires
97 128 Vista or above, Python 3, and a permission bit which only admin users
98 129 have by default, so don't rely on it.
99 ipython_dir : str [optional]
100 The path to an IPython directory, if the default value is not desired.
101 get_ipython_dir() is used by default.
130 user : bool [default: False]
131 Whether to install to the user's .ipython/nbextensions directory.
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 138 verbose : int [default: 1]
103 139 Set verbosity level. The default is 1, where file actions are printed.
104 140 set verbose=2 for more output, or verbose=0 for silence.
105 141 """
106
107 ipython_dir = ipython_dir or get_ipython_dir()
108 nbext = pjoin(ipython_dir, u'nbextensions')
142 if sum(map(bool, [user, prefix, nbextensions])) > 1:
143 raise ValueError("Cannot specify more than one of user, prefix, or 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 153 # make sure nbextensions dir exists
110 154 ensure_dir_exists(nbext)
111 155
@@ -126,7 +170,7 b' def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None,'
126 170 print("downloading %s to %s" % (path, local_path))
127 171 urlretrieve(path, local_path)
128 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 174 continue
131 175
132 176 # handle archives
@@ -183,7 +227,7 b' def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None,'
183 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 231 from IPython.core.application import BaseIPythonApplication
188 232
189 233 flags = {
@@ -207,11 +251,18 b' flags = {'
207 251 "symlink" : True,
208 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 260 flags['s'] = flags['symlink']
212 261
213 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 268 class NBExtensionApp(BaseIPythonApplication):
@@ -238,25 +289,36 b' class NBExtensionApp(BaseIPythonApplication):'
238 289
239 290 overwrite = Bool(False, config=True, help="Force overwrite of existing files")
240 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 295 verbose = Enum((0,1,2), default_value=1, config=True,
242 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 303 def install_extensions(self):
246 304 install_nbextension(self.extra_args,
247 305 overwrite=self.overwrite,
248 306 symlink=self.symlink,
249 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 313 def start(self):
254 314 if not self.extra_args:
255 nbext = pjoin(self.ipython_dir, u'nbextensions')
256 print("Notebook extensions in %s:" % nbext)
257 for ext in os.listdir(nbext):
258 print(u" %s" % ext)
315 for nbext in [pjoin(self.ipython_dir, u'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS:
316 if os.path.exists(nbext):
317 print("Notebook extensions in %s:" % nbext)
318 for ext in os.listdir(nbext):
319 print(u" %s" % ext)
259 320 else:
321 self.check_install()
260 322 self.install_extensions()
261 323
262 324
@@ -90,6 +90,7 b' from IPython.utils import py3compat'
90 90 from IPython.utils.path import filefind, get_ipython_dir
91 91 from IPython.utils.sysinfo import get_sys_info
92 92
93 from .nbextensions import SYSTEM_NBEXTENSIONS_DIRS
93 94 from .utils import url_path_join
94 95
95 96 #-----------------------------------------------------------------------------
@@ -566,11 +567,14 b' class NotebookApp(BaseIPythonApplication):'
566 567 """return extra paths + the default locations"""
567 568 return self.extra_template_paths + DEFAULT_TEMPLATE_PATH_LIST
568 569
569 nbextensions_path = List(Unicode, config=True,
570 help="""paths for Javascript extensions. By default, this is just IPYTHONDIR/nbextensions"""
570 extra_nbextensions_path = List(Unicode, config=True,
571 help="""extra paths to look for Javascript notebook extensions"""
571 572 )
572 def _nbextensions_path_default(self):
573 return [os.path.join(get_ipython_dir(), 'nbextensions')]
573
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 579 websocket_url = Unicode("", config=True,
576 580 help="""The base URL for websockets,
@@ -1,15 +1,8 b''
1 1 # coding: utf-8
2 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 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
13 6
14 7 import glob
15 8 import os
@@ -27,9 +20,6 b' from IPython.utils.tempdir import TemporaryDirectory'
27 20 from IPython.html import nbextensions
28 21 from IPython.html.nbextensions import install_nbextension, check_nbextension
29 22
30 #-----------------------------------------------------------------------------
31 # Test functions
32 #-----------------------------------------------------------------------------
33 23
34 24 def touch(file, mtime=None):
35 25 """ensure a file exists, and set its modification time
@@ -43,7 +33,6 b' def touch(file, mtime=None):'
43 33 os.utime(file, (atime, mtime))
44 34 return os.stat(file).st_mtime
45 35
46
47 36 class TestInstallNBExtension(TestCase):
48 37
49 38 def tempdir(self):
@@ -69,12 +58,15 b' class TestInstallNBExtension(TestCase):'
69 58 self.ipdir = self.tempdir()
70 59 self.save_get_ipython_dir = nbextensions.get_ipython_dir
71 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 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 67 for td in self.tempdirs:
75 68 td.cleanup()
76 nbextensions.get_ipython_dir = self.save_get_ipython_dir
77
69
78 70 def assert_dir_exists(self, path):
79 71 if not os.path.exists(path):
80 72 do_exist = os.listdir(os.path.dirname(path))
@@ -84,21 +76,29 b' class TestInstallNBExtension(TestCase):'
84 76 if os.path.exists(path):
85 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 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 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 97 def test_create_ipython_dir(self):
98 98 """install_nbextension when ipython_dir doesn't exist"""
99 99 with TemporaryDirectory() as td:
100 ipdir = pjoin(td, u'ipython')
101 install_nbextension(self.src, ipython_dir=ipdir)
100 self.ipdir = ipdir = pjoin(td, u'ipython')
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(
@@ -106,12 +106,22 b' class TestInstallNBExtension(TestCase):'
106 106 ipdir
107 107 )
108 108
109 def test_create_nbextensions(self):
110 with TemporaryDirectory() as ipdir:
111 install_nbextension(self.src, ipython_dir=ipdir)
109 def test_create_nbextensions_user(self):
110 with TemporaryDirectory() as td:
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 122 self.assert_installed(
113 123 pjoin(basename(self.src), u'ƒile'),
114 ipdir
124 user=False
115 125 )
116 126
117 127 def test_single_file(self):
@@ -136,7 +146,7 b' class TestInstallNBExtension(TestCase):'
136 146 with open(src, 'w') as f:
137 147 f.write('first')
138 148 mtime = touch(src)
139 dest = pjoin(self.ipdir, u'nbextensions', fname)
149 dest = pjoin(self.system_nbext, fname)
140 150 install_nbextension(src)
141 151 with open(src, 'w') as f:
142 152 f.write('overwrite')
@@ -147,7 +157,6 b' class TestInstallNBExtension(TestCase):'
147 157
148 158 def test_overwrite_dir(self):
149 159 with TemporaryDirectory() as src:
150 # src = py3compat.cast_unicode_py2(src)
151 160 base = basename(src)
152 161 fname = u'ƒ.js'
153 162 touch(pjoin(src, fname))
@@ -169,7 +178,7 b' class TestInstallNBExtension(TestCase):'
169 178 mtime = touch(src)
170 179 install_nbextension(src)
171 180 self.assert_installed(fname)
172 dest = pjoin(self.ipdir, u'nbextensions', fname)
181 dest = pjoin(self.system_nbext, fname)
173 182 old_mtime = os.stat(dest).st_mtime
174 183 with open(src, 'w') as f:
175 184 f.write('overwrite')
@@ -185,7 +194,7 b' class TestInstallNBExtension(TestCase):'
185 194 mtime = touch(src)
186 195 install_nbextension(src)
187 196 self.assert_installed(fname)
188 dest = pjoin(self.ipdir, u'nbextensions', fname)
197 dest = pjoin(self.system_nbext, fname)
189 198 old_mtime = os.stat(dest).st_mtime
190 199
191 200 mtime = touch(src, mtime - 100)
@@ -239,11 +248,12 b' class TestInstallNBExtension(TestCase):'
239 248 f = u'ƒ.js'
240 249 src = pjoin(d, f)
241 250 touch(src)
242 install_nbextension(src)
251 install_nbextension(src, user=True)
243 252
244 assert check_nbextension(f, self.ipdir)
245 assert check_nbextension([f], self.ipdir)
246 assert not check_nbextension([f, pjoin('dne', f)], self.ipdir)
253 nbext = pjoin(self.ipdir, u'nbextensions')
254 assert check_nbextension(f, nbext)
255 assert check_nbextension([f], nbext)
256 assert not check_nbextension([f, pjoin('dne', f)], nbext)
247 257
248 258 @dec.skip_win32
249 259 def test_install_symlink(self):
@@ -252,7 +262,7 b' class TestInstallNBExtension(TestCase):'
252 262 src = pjoin(d, f)
253 263 touch(src)
254 264 install_nbextension(src, symlink=True)
255 dest = pjoin(self.ipdir, u'nbextensions', f)
265 dest = pjoin(self.system_nbext, f)
256 266 assert os.path.islink(dest)
257 267 link = os.readlink(dest)
258 268 self.assertEqual(link, src)
General Comments 0
You need to be logged in to leave comments. Login now