##// END OF EJS Templates
add IPython.html.nbextensions.install_nbextension...
MinRK -
Show More
@@ -0,0 +1,101
1 # coding: utf-8
2 """Utilities for installing Javascript extensions for the notebook"""
3
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2014 The IPython Development Team
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 from __future__ import print_function
12
13 import os
14 import shutil
15 from os.path import basename, join as pjoin
16
17 from IPython.utils.path import get_ipython_dir
18 from IPython.utils.py3compat import string_types, cast_unicode_py2
19
20
21 def _should_copy(src, dest, verbose=1):
22 """should a file be copied?"""
23 if not os.path.exists(dest):
24 return True
25 if os.stat(dest).st_mtime < os.stat(src).st_mtime:
26 if verbose >= 2:
27 print("%s is out of date" % dest)
28 return True
29 if verbose >= 2:
30 print("%s is up to date" % dest)
31 return False
32
33
34 def _maybe_copy(src, dest, verbose=1):
35 """copy a file if it needs updating"""
36 if _should_copy(src, dest, verbose):
37 if verbose >= 1:
38 print("copying %s -> %s" % (src, dest))
39 shutil.copy2(src, dest)
40
41
42 def install_nbextension(files, overwrite=False, ipython_dir=None, verbose=1):
43 """Install a Javascript extension for the notebook
44
45 Stages files and/or directories into IPYTHONDIR/nbextensions.
46 By default, this comparse modification time, and only stages files that need updating.
47 If `overwrite` is specified, matching files are purged before proceeding.
48
49 Parameters
50 ----------
51
52 files : list(paths)
53 One or more paths to existing files or directories to install.
54 These will be installed with their base name, so '/path/to/foo'
55 will install to 'nbextensions/foo'.
56 overwrite : bool [default: False]
57 If True, always install the files, regardless of what may already be installed.
58 ipython_dir : str [optional]
59 The path to an IPython directory, if the default value is not desired.
60 get_ipython_dir() is used by default.
61 verbose : int [default: 1]
62 Set verbosity level. The default is 1, where file actions are printed.
63 set verbose=2 for more output, or verbose=0 for silence.
64 """
65
66 ipython_dir = ipython_dir or get_ipython_dir()
67 nbext = pjoin(ipython_dir, u'nbextensions')
68 # make sure nbextensions dir exists
69 if not os.path.exists(nbext):
70 os.makedirs(nbext)
71
72 if isinstance(files, string_types):
73 # one file given, turn it into a list
74 files = [files]
75
76 for path in map(cast_unicode_py2, files):
77 dest = pjoin(nbext, basename(path))
78 if overwrite and os.path.exists(dest):
79 if verbose >= 1:
80 print("removing %s" % dest)
81 if os.path.isdir(dest):
82 shutil.rmtree(dest)
83 else:
84 os.remove(dest)
85
86 if os.path.isdir(path):
87 strip_prefix_len = len(path) - len(basename(path))
88 for parent, dirs, files in os.walk(path):
89 dest_dir = pjoin(nbext, parent[strip_prefix_len:])
90 if not os.path.exists(dest_dir):
91 if verbose >= 2:
92 print("making directory %s" % dest_dir)
93 os.makedirs(dest_dir)
94 for file in files:
95 src = pjoin(parent, file)
96 # print("%r, %r" % (dest_dir, file))
97 dest = pjoin(dest_dir, file)
98 _maybe_copy(src, dest, verbose)
99 else:
100 src = path
101 _maybe_copy(src, dest, verbose)
@@ -0,0 +1,201
1 # coding: utf-8
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
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 import glob
15 import os
16 import re
17 import time
18 from contextlib import contextmanager
19 from os.path import basename, join as pjoin
20 from unittest import TestCase
21
22 import nose.tools as nt
23
24 from IPython.external.decorator import decorator
25
26 import IPython.testing.tools as tt
27 import IPython.utils.path
28 from IPython.utils import py3compat
29 from IPython.utils.tempdir import TemporaryDirectory
30 from IPython.html import nbextensions
31 from IPython.html.nbextensions import install_nbextension
32
33 #-----------------------------------------------------------------------------
34 # Test functions
35 #-----------------------------------------------------------------------------
36
37 def touch(file, mtime=None):
38 """ensure a file exists, and set its modification time
39
40 returns the modification time of the file
41 """
42 open(file, 'a').close()
43 # set explicit mtime
44 if mtime:
45 atime = os.stat(file).st_atime
46 os.utime(file, (atime, mtime))
47 return os.stat(file).st_mtime
48
49
50 class TestInstallNBExtension(TestCase):
51
52 def tempdir(self):
53 td = TemporaryDirectory()
54 self.tempdirs.append(td)
55 return py3compat.cast_unicode(td.name)
56
57 def setUp(self):
58 self.tempdirs = []
59 src = self.src = self.tempdir()
60 self.files = files = [
61 pjoin(u'ƒile'),
62 pjoin(u'∂ir', u'ƒile1'),
63 pjoin(u'∂ir', u'∂ir2', u'ƒile2'),
64 ]
65 for file in files:
66 fullpath = os.path.join(self.src, file)
67 parent = os.path.dirname(fullpath)
68 if not os.path.exists(parent):
69 os.makedirs(parent)
70 touch(fullpath)
71
72 self.ipdir = self.tempdir()
73 self.save_get_ipython_dir = nbextensions.get_ipython_dir
74 nbextensions.get_ipython_dir = lambda : self.ipdir
75
76 def tearDown(self):
77 for td in self.tempdirs:
78 td.cleanup()
79 nbextensions.get_ipython_dir = self.save_get_ipython_dir
80
81 def assert_path_exists(self, path):
82 if not os.path.exists(path):
83 self.fail(u"%s should exist" % path)
84
85 def assert_not_path_exists(self, path):
86 if os.path.exists(path):
87 self.fail(u"%s should not exist" % path)
88
89 def assert_installed(self, relative_path, ipdir=None):
90 self.assert_path_exists(
91 pjoin(ipdir or self.ipdir, u'nbextensions', relative_path)
92 )
93
94 def assert_not_installed(self, relative_path, ipdir=None):
95 self.assert_not_path_exists(
96 pjoin(ipdir or self.ipdir, u'nbextensions', relative_path)
97 )
98
99 def test_create_ipython_dir(self):
100 """install_nbextension when ipython_dir doesn't exist"""
101 with TemporaryDirectory() as td:
102 ipdir = pjoin(td, u'ipython')
103 install_nbextension(self.src, ipython_dir=ipdir)
104 self.assert_path_exists(ipdir)
105 for file in self.files:
106 self.assert_installed(
107 pjoin(basename(self.src), file),
108 ipdir
109 )
110
111 def test_create_nbextensions(self):
112 with TemporaryDirectory() as ipdir:
113 install_nbextension(self.src, ipython_dir=ipdir)
114 self.assert_installed(
115 pjoin(basename(self.src), u'ƒile'),
116 ipdir
117 )
118
119 def test_single_file(self):
120 file = self.files[0]
121 install_nbextension(pjoin(self.src, file))
122 self.assert_installed(file)
123
124 def test_single_dir(self):
125 d = u'∂ir'
126 install_nbextension(pjoin(self.src, d))
127 self.assert_installed(self.files[-1])
128
129 def test_install_nbextension(self):
130 install_nbextension(glob.glob(pjoin(self.src, '*')))
131 for file in self.files:
132 self.assert_installed(file)
133
134 def test_overwrite_file(self):
135 with TemporaryDirectory() as d:
136 fname = u'ƒ.js'
137 src = pjoin(d, fname)
138 with open(src, 'w') as f:
139 f.write('first')
140 mtime = touch(src)
141 dest = pjoin(self.ipdir, u'nbextensions', fname)
142 install_nbextension(src)
143 with open(src, 'w') as f:
144 f.write('overwrite')
145 mtime = touch(src, mtime - 100)
146 install_nbextension(src, overwrite=True)
147 with open(dest) as f:
148 self.assertEqual(f.read(), 'overwrite')
149
150 def test_overwrite_dir(self):
151 with TemporaryDirectory() as src:
152 # src = py3compat.cast_unicode_py2(src)
153 base = basename(src)
154 fname = u'ƒ.js'
155 touch(pjoin(src, fname))
156 install_nbextension(src)
157 self.assert_installed(pjoin(base, fname))
158 os.remove(pjoin(src, fname))
159 fname2 = u'∂.js'
160 touch(pjoin(src, fname2))
161 install_nbextension(src, overwrite=True)
162 self.assert_installed(pjoin(base, fname2))
163 self.assert_not_installed(pjoin(base, fname))
164
165 def test_update_file(self):
166 with TemporaryDirectory() as d:
167 fname = u'ƒ.js'
168 src = pjoin(d, fname)
169 with open(src, 'w') as f:
170 f.write('first')
171 mtime = touch(src)
172 install_nbextension(src)
173 self.assert_installed(fname)
174 dest = pjoin(self.ipdir, u'nbextensions', fname)
175 old_mtime = os.stat(dest).st_mtime
176 with open(src, 'w') as f:
177 f.write('overwrite')
178 touch(src, mtime + 10)
179 install_nbextension(src)
180 with open(dest) as f:
181 self.assertEqual(f.read(), 'overwrite')
182
183 def test_skip_old_file(self):
184 with TemporaryDirectory() as d:
185 fname = u'ƒ.js'
186 src = pjoin(d, fname)
187 mtime = touch(src)
188 install_nbextension(src)
189 self.assert_installed(fname)
190 dest = pjoin(self.ipdir, u'nbextensions', fname)
191 old_mtime = os.stat(dest).st_mtime
192
193 mtime = touch(src, mtime - 100)
194 install_nbextension(src)
195 new_mtime = os.stat(dest).st_mtime
196 self.assertEqual(new_mtime, old_mtime)
197
198 def test_quiet(self):
199 with tt.AssertNotPrints(re.compile(r'.+')):
200 install_nbextension(self.src, verbose=0)
201
General Comments 0
You need to be logged in to leave comments. Login now