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, |
|
|
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 = |
|
|
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, |
|
|
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, |
|
|
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 |
|
|
|
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 |
|
|
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 |
|
|
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, |
|
|
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( |
|
|
85 | pjoin(nbext, relative_path) | |
|
90 | 86 | ) |
|
91 | 87 | |
|
92 |
def assert_not_installed(self, relative_path, |
|
|
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( |
|
|
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, |
|
|
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 |
|
|
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 |
|
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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( |
|
|
246 |
assert |
|
|
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. |
|
|
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