Show More
@@ -0,0 +1,40 b'' | |||||
|
1 | import sys | |||
|
2 | import nose.tools as nt | |||
|
3 | ||||
|
4 | from subprocess import Popen, PIPE | |||
|
5 | ||||
|
6 | def get_output_error_code(cmd): | |||
|
7 | """Get stdout, stderr, and exit code from running a command""" | |||
|
8 | p = Popen(cmd, stdout=PIPE, stderr=PIPE) | |||
|
9 | out, err = p.communicate() | |||
|
10 | out = out.decode('utf8', 'replace') | |||
|
11 | err = err.decode('utf8', 'replace') | |||
|
12 | return out, err, p.returncode | |||
|
13 | ||||
|
14 | ||||
|
15 | def check_help_output(pkg, subcommand=None): | |||
|
16 | """test that `python -m PKG [subcommand] -h` works""" | |||
|
17 | cmd = [sys.executable, '-m', pkg] | |||
|
18 | if subcommand: | |||
|
19 | cmd.extend(subcommand) | |||
|
20 | cmd.append('-h') | |||
|
21 | out, err, rc = get_output_error_code(cmd) | |||
|
22 | nt.assert_equal(rc, 0, err) | |||
|
23 | nt.assert_not_in("Traceback", err) | |||
|
24 | nt.assert_in("Options", out) | |||
|
25 | nt.assert_in("--help-all", out) | |||
|
26 | return out, err | |||
|
27 | ||||
|
28 | ||||
|
29 | def check_help_all_output(pkg, subcommand=None): | |||
|
30 | """test that `python -m PKG --help-all` works""" | |||
|
31 | cmd = [sys.executable, '-m', pkg] | |||
|
32 | if subcommand: | |||
|
33 | cmd.extend(subcommand) | |||
|
34 | cmd.append('--help-all') | |||
|
35 | out, err, rc = get_output_error_code(cmd) | |||
|
36 | nt.assert_equal(rc, 0, err) | |||
|
37 | nt.assert_not_in("Traceback", err) | |||
|
38 | nt.assert_in("Options", out) | |||
|
39 | nt.assert_in("Class parameters", out) | |||
|
40 | return out, err |
@@ -1,332 +1,342 b'' | |||||
1 | # coding: utf-8 |
|
1 | # coding: utf-8 | |
2 | """Test installation of notebook extensions""" |
|
2 | """Test installation of notebook extensions""" | |
3 |
|
3 | |||
4 | # Copyright (c) IPython Development Team. |
|
4 | # Copyright (c) IPython Development Team. | |
5 | # Distributed under the terms of the Modified BSD License. |
|
5 | # Distributed under the terms of the Modified BSD License. | |
6 |
|
6 | |||
7 | import glob |
|
7 | import glob | |
8 | import os |
|
8 | import os | |
9 | import re |
|
9 | import re | |
|
10 | import sys | |||
10 | import tarfile |
|
11 | import tarfile | |
11 | import zipfile |
|
12 | import zipfile | |
12 | from io import BytesIO |
|
13 | from io import BytesIO, StringIO | |
13 | from os.path import basename, join as pjoin |
|
14 | from os.path import basename, join as pjoin | |
14 | from unittest import TestCase |
|
15 | from unittest import TestCase | |
15 |
|
16 | |||
16 | import IPython.testing.tools as tt |
|
17 | try: | |
|
18 | from unittest import mock | |||
|
19 | except ImportError: | |||
|
20 | import mock # py2 | |||
|
21 | ||||
17 | import IPython.testing.decorators as dec |
|
22 | import IPython.testing.decorators as dec | |
18 | from IPython.utils import py3compat |
|
23 | from IPython.utils import py3compat | |
19 | from IPython.utils.tempdir import TemporaryDirectory |
|
24 | from IPython.utils.tempdir import TemporaryDirectory | |
20 | from IPython.html import nbextensions |
|
25 | from IPython.html import nbextensions | |
21 | from IPython.html.nbextensions import install_nbextension, check_nbextension |
|
26 | from IPython.html.nbextensions import install_nbextension, check_nbextension | |
22 |
|
27 | |||
23 |
|
28 | |||
24 | def touch(file, mtime=None): |
|
29 | def touch(file, mtime=None): | |
25 | """ensure a file exists, and set its modification time |
|
30 | """ensure a file exists, and set its modification time | |
26 |
|
31 | |||
27 | returns the modification time of the file |
|
32 | returns the modification time of the file | |
28 | """ |
|
33 | """ | |
29 | open(file, 'a').close() |
|
34 | open(file, 'a').close() | |
30 | # set explicit mtime |
|
35 | # set explicit mtime | |
31 | if mtime: |
|
36 | if mtime: | |
32 | atime = os.stat(file).st_atime |
|
37 | atime = os.stat(file).st_atime | |
33 | os.utime(file, (atime, mtime)) |
|
38 | os.utime(file, (atime, mtime)) | |
34 | return os.stat(file).st_mtime |
|
39 | return os.stat(file).st_mtime | |
35 |
|
40 | |||
36 | class TestInstallNBExtension(TestCase): |
|
41 | class TestInstallNBExtension(TestCase): | |
37 |
|
42 | |||
38 | def tempdir(self): |
|
43 | def tempdir(self): | |
39 | td = TemporaryDirectory() |
|
44 | td = TemporaryDirectory() | |
40 | self.tempdirs.append(td) |
|
45 | self.tempdirs.append(td) | |
41 | return py3compat.cast_unicode(td.name) |
|
46 | return py3compat.cast_unicode(td.name) | |
42 |
|
47 | |||
43 | def setUp(self): |
|
48 | def setUp(self): | |
44 | self.tempdirs = [] |
|
49 | self.tempdirs = [] | |
45 | src = self.src = self.tempdir() |
|
50 | src = self.src = self.tempdir() | |
46 | self.files = files = [ |
|
51 | self.files = files = [ | |
47 | pjoin(u'ƒile'), |
|
52 | pjoin(u'ƒile'), | |
48 | pjoin(u'∂ir', u'ƒile1'), |
|
53 | pjoin(u'∂ir', u'ƒile1'), | |
49 | pjoin(u'∂ir', u'∂ir2', u'ƒile2'), |
|
54 | pjoin(u'∂ir', u'∂ir2', u'ƒile2'), | |
50 | ] |
|
55 | ] | |
51 | for file in files: |
|
56 | for file in files: | |
52 | fullpath = os.path.join(self.src, file) |
|
57 | fullpath = os.path.join(self.src, file) | |
53 | parent = os.path.dirname(fullpath) |
|
58 | parent = os.path.dirname(fullpath) | |
54 | if not os.path.exists(parent): |
|
59 | if not os.path.exists(parent): | |
55 | os.makedirs(parent) |
|
60 | os.makedirs(parent) | |
56 | touch(fullpath) |
|
61 | touch(fullpath) | |
57 |
|
62 | |||
58 | self.ipdir = self.tempdir() |
|
63 | self.ipdir = self.tempdir() | |
59 | self.save_get_ipython_dir = nbextensions.get_ipython_dir |
|
64 | self.save_get_ipython_dir = nbextensions.get_ipython_dir | |
60 | nbextensions.get_ipython_dir = lambda : self.ipdir |
|
65 | nbextensions.get_ipython_dir = lambda : self.ipdir | |
61 | self.save_system_dir = nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR |
|
66 | self.save_system_dir = nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR | |
62 | nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = self.tempdir() |
|
67 | nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = self.tempdir() | |
63 |
|
68 | |||
64 | def tearDown(self): |
|
69 | def tearDown(self): | |
65 | nbextensions.get_ipython_dir = self.save_get_ipython_dir |
|
70 | nbextensions.get_ipython_dir = self.save_get_ipython_dir | |
66 | nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.save_system_dir |
|
71 | nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.save_system_dir | |
67 | for td in self.tempdirs: |
|
72 | for td in self.tempdirs: | |
68 | td.cleanup() |
|
73 | td.cleanup() | |
69 |
|
74 | |||
70 | def assert_dir_exists(self, path): |
|
75 | def assert_dir_exists(self, path): | |
71 | if not os.path.exists(path): |
|
76 | if not os.path.exists(path): | |
72 | do_exist = os.listdir(os.path.dirname(path)) |
|
77 | do_exist = os.listdir(os.path.dirname(path)) | |
73 | self.fail(u"%s should exist (found %s)" % (path, do_exist)) |
|
78 | self.fail(u"%s should exist (found %s)" % (path, do_exist)) | |
74 |
|
79 | |||
75 | def assert_not_dir_exists(self, path): |
|
80 | def assert_not_dir_exists(self, path): | |
76 | if os.path.exists(path): |
|
81 | if os.path.exists(path): | |
77 | self.fail(u"%s should not exist" % path) |
|
82 | self.fail(u"%s should not exist" % path) | |
78 |
|
83 | |||
79 | def assert_installed(self, relative_path, user=False): |
|
84 | def assert_installed(self, relative_path, user=False): | |
80 | if user: |
|
85 | if user: | |
81 | nbext = pjoin(self.ipdir, u'nbextensions') |
|
86 | nbext = pjoin(self.ipdir, u'nbextensions') | |
82 | else: |
|
87 | else: | |
83 | nbext = self.system_nbext |
|
88 | nbext = self.system_nbext | |
84 | self.assert_dir_exists( |
|
89 | self.assert_dir_exists( | |
85 | pjoin(nbext, relative_path) |
|
90 | pjoin(nbext, relative_path) | |
86 | ) |
|
91 | ) | |
87 |
|
92 | |||
88 | def assert_not_installed(self, relative_path, user=False): |
|
93 | def assert_not_installed(self, relative_path, user=False): | |
89 | if user: |
|
94 | if user: | |
90 | nbext = pjoin(self.ipdir, u'nbextensions') |
|
95 | nbext = pjoin(self.ipdir, u'nbextensions') | |
91 | else: |
|
96 | else: | |
92 | nbext = self.system_nbext |
|
97 | nbext = self.system_nbext | |
93 | self.assert_not_dir_exists( |
|
98 | self.assert_not_dir_exists( | |
94 | pjoin(nbext, relative_path) |
|
99 | pjoin(nbext, relative_path) | |
95 | ) |
|
100 | ) | |
96 |
|
101 | |||
97 | def test_create_ipython_dir(self): |
|
102 | def test_create_ipython_dir(self): | |
98 | """install_nbextension when ipython_dir doesn't exist""" |
|
103 | """install_nbextension when ipython_dir doesn't exist""" | |
99 | with TemporaryDirectory() as td: |
|
104 | with TemporaryDirectory() as td: | |
100 | self.ipdir = ipdir = pjoin(td, u'ipython') |
|
105 | self.ipdir = ipdir = pjoin(td, u'ipython') | |
101 | install_nbextension(self.src, user=True) |
|
106 | install_nbextension(self.src, user=True) | |
102 | self.assert_dir_exists(ipdir) |
|
107 | self.assert_dir_exists(ipdir) | |
103 | for file in self.files: |
|
108 | for file in self.files: | |
104 | self.assert_installed( |
|
109 | self.assert_installed( | |
105 | pjoin(basename(self.src), file), |
|
110 | pjoin(basename(self.src), file), | |
106 | user=bool(ipdir) |
|
111 | user=bool(ipdir) | |
107 | ) |
|
112 | ) | |
108 |
|
113 | |||
109 | def test_create_nbextensions_user(self): |
|
114 | def test_create_nbextensions_user(self): | |
110 | with TemporaryDirectory() as td: |
|
115 | with TemporaryDirectory() as td: | |
111 | self.ipdir = ipdir = pjoin(td, u'ipython') |
|
116 | self.ipdir = ipdir = pjoin(td, u'ipython') | |
112 | install_nbextension(self.src, user=True) |
|
117 | install_nbextension(self.src, user=True) | |
113 | self.assert_installed( |
|
118 | self.assert_installed( | |
114 | pjoin(basename(self.src), u'ƒile'), |
|
119 | pjoin(basename(self.src), u'ƒile'), | |
115 | user=True |
|
120 | user=True | |
116 | ) |
|
121 | ) | |
117 |
|
122 | |||
118 | def test_create_nbextensions_system(self): |
|
123 | def test_create_nbextensions_system(self): | |
119 | with TemporaryDirectory() as td: |
|
124 | with TemporaryDirectory() as td: | |
120 | nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = pjoin(td, u'nbextensions') |
|
125 | nbextensions.SYSTEM_NBEXTENSIONS_INSTALL_DIR = self.system_nbext = pjoin(td, u'nbextensions') | |
121 | install_nbextension(self.src, user=False) |
|
126 | install_nbextension(self.src, user=False) | |
122 | self.assert_installed( |
|
127 | self.assert_installed( | |
123 | pjoin(basename(self.src), u'ƒile'), |
|
128 | pjoin(basename(self.src), u'ƒile'), | |
124 | user=False |
|
129 | user=False | |
125 | ) |
|
130 | ) | |
126 |
|
131 | |||
127 | def test_single_file(self): |
|
132 | def test_single_file(self): | |
128 | file = self.files[0] |
|
133 | file = self.files[0] | |
129 | install_nbextension(pjoin(self.src, file)) |
|
134 | install_nbextension(pjoin(self.src, file)) | |
130 | self.assert_installed(file) |
|
135 | self.assert_installed(file) | |
131 |
|
136 | |||
132 | def test_single_dir(self): |
|
137 | def test_single_dir(self): | |
133 | d = u'∂ir' |
|
138 | d = u'∂ir' | |
134 | install_nbextension(pjoin(self.src, d)) |
|
139 | install_nbextension(pjoin(self.src, d)) | |
135 | self.assert_installed(self.files[-1]) |
|
140 | self.assert_installed(self.files[-1]) | |
136 |
|
141 | |||
137 |
|
142 | |||
138 | def test_destination_file(self): |
|
143 | def test_destination_file(self): | |
139 | file = self.files[0] |
|
144 | file = self.files[0] | |
140 | install_nbextension(pjoin(self.src, file), destination = u'ƒiledest') |
|
145 | install_nbextension(pjoin(self.src, file), destination = u'ƒiledest') | |
141 | self.assert_installed(u'ƒiledest') |
|
146 | self.assert_installed(u'ƒiledest') | |
142 |
|
147 | |||
143 | def test_destination_dir(self): |
|
148 | def test_destination_dir(self): | |
144 | d = u'∂ir' |
|
149 | d = u'∂ir' | |
145 | install_nbextension(pjoin(self.src, d), destination = u'ƒiledest2') |
|
150 | install_nbextension(pjoin(self.src, d), destination = u'ƒiledest2') | |
146 | self.assert_installed(pjoin(u'ƒiledest2', u'∂ir2', u'ƒile2')) |
|
151 | self.assert_installed(pjoin(u'ƒiledest2', u'∂ir2', u'ƒile2')) | |
147 |
|
152 | |||
148 | def test_install_nbextension(self): |
|
153 | def test_install_nbextension(self): | |
149 | with self.assertRaises(TypeError): |
|
154 | with self.assertRaises(TypeError): | |
150 | install_nbextension(glob.glob(pjoin(self.src, '*'))) |
|
155 | install_nbextension(glob.glob(pjoin(self.src, '*'))) | |
151 |
|
156 | |||
152 | def test_overwrite_file(self): |
|
157 | def test_overwrite_file(self): | |
153 | with TemporaryDirectory() as d: |
|
158 | with TemporaryDirectory() as d: | |
154 | fname = u'ƒ.js' |
|
159 | fname = u'ƒ.js' | |
155 | src = pjoin(d, fname) |
|
160 | src = pjoin(d, fname) | |
156 | with open(src, 'w') as f: |
|
161 | with open(src, 'w') as f: | |
157 | f.write('first') |
|
162 | f.write('first') | |
158 | mtime = touch(src) |
|
163 | mtime = touch(src) | |
159 | dest = pjoin(self.system_nbext, fname) |
|
164 | dest = pjoin(self.system_nbext, fname) | |
160 | install_nbextension(src) |
|
165 | install_nbextension(src) | |
161 | with open(src, 'w') as f: |
|
166 | with open(src, 'w') as f: | |
162 | f.write('overwrite') |
|
167 | f.write('overwrite') | |
163 | mtime = touch(src, mtime - 100) |
|
168 | mtime = touch(src, mtime - 100) | |
164 | install_nbextension(src, overwrite=True) |
|
169 | install_nbextension(src, overwrite=True) | |
165 | with open(dest) as f: |
|
170 | with open(dest) as f: | |
166 | self.assertEqual(f.read(), 'overwrite') |
|
171 | self.assertEqual(f.read(), 'overwrite') | |
167 |
|
172 | |||
168 | def test_overwrite_dir(self): |
|
173 | def test_overwrite_dir(self): | |
169 | with TemporaryDirectory() as src: |
|
174 | with TemporaryDirectory() as src: | |
170 | base = basename(src) |
|
175 | base = basename(src) | |
171 | fname = u'ƒ.js' |
|
176 | fname = u'ƒ.js' | |
172 | touch(pjoin(src, fname)) |
|
177 | touch(pjoin(src, fname)) | |
173 | install_nbextension(src) |
|
178 | install_nbextension(src) | |
174 | self.assert_installed(pjoin(base, fname)) |
|
179 | self.assert_installed(pjoin(base, fname)) | |
175 | os.remove(pjoin(src, fname)) |
|
180 | os.remove(pjoin(src, fname)) | |
176 | fname2 = u'∂.js' |
|
181 | fname2 = u'∂.js' | |
177 | touch(pjoin(src, fname2)) |
|
182 | touch(pjoin(src, fname2)) | |
178 | install_nbextension(src, overwrite=True) |
|
183 | install_nbextension(src, overwrite=True) | |
179 | self.assert_installed(pjoin(base, fname2)) |
|
184 | self.assert_installed(pjoin(base, fname2)) | |
180 | self.assert_not_installed(pjoin(base, fname)) |
|
185 | self.assert_not_installed(pjoin(base, fname)) | |
181 |
|
186 | |||
182 | def test_update_file(self): |
|
187 | def test_update_file(self): | |
183 | with TemporaryDirectory() as d: |
|
188 | with TemporaryDirectory() as d: | |
184 | fname = u'ƒ.js' |
|
189 | fname = u'ƒ.js' | |
185 | src = pjoin(d, fname) |
|
190 | src = pjoin(d, fname) | |
186 | with open(src, 'w') as f: |
|
191 | with open(src, 'w') as f: | |
187 | f.write('first') |
|
192 | f.write('first') | |
188 | mtime = touch(src) |
|
193 | mtime = touch(src) | |
189 | install_nbextension(src) |
|
194 | install_nbextension(src) | |
190 | self.assert_installed(fname) |
|
195 | self.assert_installed(fname) | |
191 | dest = pjoin(self.system_nbext, fname) |
|
196 | dest = pjoin(self.system_nbext, fname) | |
192 | old_mtime = os.stat(dest).st_mtime |
|
197 | old_mtime = os.stat(dest).st_mtime | |
193 | with open(src, 'w') as f: |
|
198 | with open(src, 'w') as f: | |
194 | f.write('overwrite') |
|
199 | f.write('overwrite') | |
195 | touch(src, mtime + 10) |
|
200 | touch(src, mtime + 10) | |
196 | install_nbextension(src) |
|
201 | install_nbextension(src) | |
197 | with open(dest) as f: |
|
202 | with open(dest) as f: | |
198 | self.assertEqual(f.read(), 'overwrite') |
|
203 | self.assertEqual(f.read(), 'overwrite') | |
199 |
|
204 | |||
200 | def test_skip_old_file(self): |
|
205 | def test_skip_old_file(self): | |
201 | with TemporaryDirectory() as d: |
|
206 | with TemporaryDirectory() as d: | |
202 | fname = u'ƒ.js' |
|
207 | fname = u'ƒ.js' | |
203 | src = pjoin(d, fname) |
|
208 | src = pjoin(d, fname) | |
204 | mtime = touch(src) |
|
209 | mtime = touch(src) | |
205 | install_nbextension(src) |
|
210 | install_nbextension(src) | |
206 | self.assert_installed(fname) |
|
211 | self.assert_installed(fname) | |
207 | dest = pjoin(self.system_nbext, fname) |
|
212 | dest = pjoin(self.system_nbext, fname) | |
208 | old_mtime = os.stat(dest).st_mtime |
|
213 | old_mtime = os.stat(dest).st_mtime | |
209 |
|
214 | |||
210 | mtime = touch(src, mtime - 100) |
|
215 | mtime = touch(src, mtime - 100) | |
211 | install_nbextension(src) |
|
216 | install_nbextension(src) | |
212 | new_mtime = os.stat(dest).st_mtime |
|
217 | new_mtime = os.stat(dest).st_mtime | |
213 | self.assertEqual(new_mtime, old_mtime) |
|
218 | self.assertEqual(new_mtime, old_mtime) | |
214 |
|
219 | |||
215 | def test_quiet(self): |
|
220 | def test_quiet(self): | |
216 | with tt.AssertNotPrints(re.compile(r'.+')): |
|
221 | stdout = StringIO() | |
|
222 | stderr = StringIO() | |||
|
223 | with mock.patch.object(sys, 'stdout', stdout), \ | |||
|
224 | mock.patch.object(sys, 'stderr', stderr): | |||
217 | install_nbextension(self.src, verbose=0) |
|
225 | install_nbextension(self.src, verbose=0) | |
|
226 | self.assertEqual(stdout.getvalue(), '') | |||
|
227 | self.assertEqual(stderr.getvalue(), '') | |||
218 |
|
228 | |||
219 | def test_install_zip(self): |
|
229 | def test_install_zip(self): | |
220 | path = pjoin(self.src, "myjsext.zip") |
|
230 | path = pjoin(self.src, "myjsext.zip") | |
221 | with zipfile.ZipFile(path, 'w') as f: |
|
231 | with zipfile.ZipFile(path, 'w') as f: | |
222 | f.writestr("a.js", b"b();") |
|
232 | f.writestr("a.js", b"b();") | |
223 | f.writestr("foo/a.js", b"foo();") |
|
233 | f.writestr("foo/a.js", b"foo();") | |
224 | install_nbextension(path) |
|
234 | install_nbextension(path) | |
225 | self.assert_installed("a.js") |
|
235 | self.assert_installed("a.js") | |
226 | self.assert_installed(pjoin("foo", "a.js")) |
|
236 | self.assert_installed(pjoin("foo", "a.js")) | |
227 |
|
237 | |||
228 | def test_install_tar(self): |
|
238 | def test_install_tar(self): | |
229 | def _add_file(f, fname, buf): |
|
239 | def _add_file(f, fname, buf): | |
230 | info = tarfile.TarInfo(fname) |
|
240 | info = tarfile.TarInfo(fname) | |
231 | info.size = len(buf) |
|
241 | info.size = len(buf) | |
232 | f.addfile(info, BytesIO(buf)) |
|
242 | f.addfile(info, BytesIO(buf)) | |
233 |
|
243 | |||
234 | for i,ext in enumerate((".tar.gz", ".tgz", ".tar.bz2")): |
|
244 | for i,ext in enumerate((".tar.gz", ".tgz", ".tar.bz2")): | |
235 | path = pjoin(self.src, "myjsext" + ext) |
|
245 | path = pjoin(self.src, "myjsext" + ext) | |
236 | with tarfile.open(path, 'w') as f: |
|
246 | with tarfile.open(path, 'w') as f: | |
237 | _add_file(f, "b%i.js" % i, b"b();") |
|
247 | _add_file(f, "b%i.js" % i, b"b();") | |
238 | _add_file(f, "foo/b%i.js" % i, b"foo();") |
|
248 | _add_file(f, "foo/b%i.js" % i, b"foo();") | |
239 | install_nbextension(path) |
|
249 | install_nbextension(path) | |
240 | self.assert_installed("b%i.js" % i) |
|
250 | self.assert_installed("b%i.js" % i) | |
241 | self.assert_installed(pjoin("foo", "b%i.js" % i)) |
|
251 | self.assert_installed(pjoin("foo", "b%i.js" % i)) | |
242 |
|
252 | |||
243 | def test_install_url(self): |
|
253 | def test_install_url(self): | |
244 | def fake_urlretrieve(url, dest): |
|
254 | def fake_urlretrieve(url, dest): | |
245 | touch(dest) |
|
255 | touch(dest) | |
246 | save_urlretrieve = nbextensions.urlretrieve |
|
256 | save_urlretrieve = nbextensions.urlretrieve | |
247 | nbextensions.urlretrieve = fake_urlretrieve |
|
257 | nbextensions.urlretrieve = fake_urlretrieve | |
248 | try: |
|
258 | try: | |
249 | install_nbextension("http://example.com/path/to/foo.js") |
|
259 | install_nbextension("http://example.com/path/to/foo.js") | |
250 | self.assert_installed("foo.js") |
|
260 | self.assert_installed("foo.js") | |
251 | install_nbextension("https://example.com/path/to/another/bar.js") |
|
261 | install_nbextension("https://example.com/path/to/another/bar.js") | |
252 | self.assert_installed("bar.js") |
|
262 | self.assert_installed("bar.js") | |
253 | install_nbextension("https://example.com/path/to/another/bar.js", |
|
263 | install_nbextension("https://example.com/path/to/another/bar.js", | |
254 | destination = 'foobar.js') |
|
264 | destination = 'foobar.js') | |
255 | self.assert_installed("foobar.js") |
|
265 | self.assert_installed("foobar.js") | |
256 | finally: |
|
266 | finally: | |
257 | nbextensions.urlretrieve = save_urlretrieve |
|
267 | nbextensions.urlretrieve = save_urlretrieve | |
258 |
|
268 | |||
259 | def test_check_nbextension(self): |
|
269 | def test_check_nbextension(self): | |
260 | with TemporaryDirectory() as d: |
|
270 | with TemporaryDirectory() as d: | |
261 | f = u'ƒ.js' |
|
271 | f = u'ƒ.js' | |
262 | src = pjoin(d, f) |
|
272 | src = pjoin(d, f) | |
263 | touch(src) |
|
273 | touch(src) | |
264 | install_nbextension(src, user=True) |
|
274 | install_nbextension(src, user=True) | |
265 |
|
275 | |||
266 | assert check_nbextension(f, user=True) |
|
276 | assert check_nbextension(f, user=True) | |
267 | assert check_nbextension([f], user=True) |
|
277 | assert check_nbextension([f], user=True) | |
268 | assert not check_nbextension([f, pjoin('dne', f)], user=True) |
|
278 | assert not check_nbextension([f, pjoin('dne', f)], user=True) | |
269 |
|
279 | |||
270 | @dec.skip_win32 |
|
280 | @dec.skip_win32 | |
271 | def test_install_symlink(self): |
|
281 | def test_install_symlink(self): | |
272 | with TemporaryDirectory() as d: |
|
282 | with TemporaryDirectory() as d: | |
273 | f = u'ƒ.js' |
|
283 | f = u'ƒ.js' | |
274 | src = pjoin(d, f) |
|
284 | src = pjoin(d, f) | |
275 | touch(src) |
|
285 | touch(src) | |
276 | install_nbextension(src, symlink=True) |
|
286 | install_nbextension(src, symlink=True) | |
277 | dest = pjoin(self.system_nbext, f) |
|
287 | dest = pjoin(self.system_nbext, f) | |
278 | assert os.path.islink(dest) |
|
288 | assert os.path.islink(dest) | |
279 | link = os.readlink(dest) |
|
289 | link = os.readlink(dest) | |
280 | self.assertEqual(link, src) |
|
290 | self.assertEqual(link, src) | |
281 |
|
291 | |||
282 | @dec.skip_win32 |
|
292 | @dec.skip_win32 | |
283 | def test_overwrite_broken_symlink(self): |
|
293 | def test_overwrite_broken_symlink(self): | |
284 | with TemporaryDirectory() as d: |
|
294 | with TemporaryDirectory() as d: | |
285 | f = u'ƒ.js' |
|
295 | f = u'ƒ.js' | |
286 | f2 = u'ƒ2.js' |
|
296 | f2 = u'ƒ2.js' | |
287 | src = pjoin(d, f) |
|
297 | src = pjoin(d, f) | |
288 | src2 = pjoin(d, f2) |
|
298 | src2 = pjoin(d, f2) | |
289 | touch(src) |
|
299 | touch(src) | |
290 | install_nbextension(src, symlink=True) |
|
300 | install_nbextension(src, symlink=True) | |
291 | os.rename(src, src2) |
|
301 | os.rename(src, src2) | |
292 | install_nbextension(src2, symlink=True, overwrite=True, destination=f) |
|
302 | install_nbextension(src2, symlink=True, overwrite=True, destination=f) | |
293 | dest = pjoin(self.system_nbext, f) |
|
303 | dest = pjoin(self.system_nbext, f) | |
294 | assert os.path.islink(dest) |
|
304 | assert os.path.islink(dest) | |
295 | link = os.readlink(dest) |
|
305 | link = os.readlink(dest) | |
296 | self.assertEqual(link, src2) |
|
306 | self.assertEqual(link, src2) | |
297 |
|
307 | |||
298 | @dec.skip_win32 |
|
308 | @dec.skip_win32 | |
299 | def test_install_symlink_destination(self): |
|
309 | def test_install_symlink_destination(self): | |
300 | with TemporaryDirectory() as d: |
|
310 | with TemporaryDirectory() as d: | |
301 | f = u'ƒ.js' |
|
311 | f = u'ƒ.js' | |
302 | flink = u'ƒlink.js' |
|
312 | flink = u'ƒlink.js' | |
303 | src = pjoin(d, f) |
|
313 | src = pjoin(d, f) | |
304 | touch(src) |
|
314 | touch(src) | |
305 | install_nbextension(src, symlink=True, destination=flink) |
|
315 | install_nbextension(src, symlink=True, destination=flink) | |
306 | dest = pjoin(self.system_nbext, flink) |
|
316 | dest = pjoin(self.system_nbext, flink) | |
307 | assert os.path.islink(dest) |
|
317 | assert os.path.islink(dest) | |
308 | link = os.readlink(dest) |
|
318 | link = os.readlink(dest) | |
309 | self.assertEqual(link, src) |
|
319 | self.assertEqual(link, src) | |
310 |
|
320 | |||
311 | def test_install_symlink_bad(self): |
|
321 | def test_install_symlink_bad(self): | |
312 | with self.assertRaises(ValueError): |
|
322 | with self.assertRaises(ValueError): | |
313 | install_nbextension("http://example.com/foo.js", symlink=True) |
|
323 | install_nbextension("http://example.com/foo.js", symlink=True) | |
314 |
|
324 | |||
315 | with TemporaryDirectory() as d: |
|
325 | with TemporaryDirectory() as d: | |
316 | zf = u'ƒ.zip' |
|
326 | zf = u'ƒ.zip' | |
317 | zsrc = pjoin(d, zf) |
|
327 | zsrc = pjoin(d, zf) | |
318 | with zipfile.ZipFile(zsrc, 'w') as z: |
|
328 | with zipfile.ZipFile(zsrc, 'w') as z: | |
319 | z.writestr("a.js", b"b();") |
|
329 | z.writestr("a.js", b"b();") | |
320 |
|
330 | |||
321 | with self.assertRaises(ValueError): |
|
331 | with self.assertRaises(ValueError): | |
322 | install_nbextension(zsrc, symlink=True) |
|
332 | install_nbextension(zsrc, symlink=True) | |
323 |
|
333 | |||
324 | def test_install_destination_bad(self): |
|
334 | def test_install_destination_bad(self): | |
325 | with TemporaryDirectory() as d: |
|
335 | with TemporaryDirectory() as d: | |
326 | zf = u'ƒ.zip' |
|
336 | zf = u'ƒ.zip' | |
327 | zsrc = pjoin(d, zf) |
|
337 | zsrc = pjoin(d, zf) | |
328 | with zipfile.ZipFile(zsrc, 'w') as z: |
|
338 | with zipfile.ZipFile(zsrc, 'w') as z: | |
329 | z.writestr("a.js", b"b();") |
|
339 | z.writestr("a.js", b"b();") | |
330 |
|
340 | |||
331 | with self.assertRaises(ValueError): |
|
341 | with self.assertRaises(ValueError): | |
332 | install_nbextension(zsrc, destination='foo') |
|
342 | install_nbextension(zsrc, destination='foo') |
@@ -1,74 +1,62 b'' | |||||
1 | """Test NotebookApp""" |
|
1 | """Test NotebookApp""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
|||
4 | # Copyright (C) 2013 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 |
|
3 | |||
14 | import logging |
|
4 | import logging | |
15 | import os |
|
5 | import os | |
16 | from tempfile import NamedTemporaryFile |
|
6 | from tempfile import NamedTemporaryFile | |
17 |
|
7 | |||
18 | import nose.tools as nt |
|
8 | import nose.tools as nt | |
19 |
|
9 | |||
|
10 | from traitlets.tests.utils import check_help_all_output | |||
|
11 | ||||
20 | from IPython.utils.tempdir import TemporaryDirectory |
|
12 | from IPython.utils.tempdir import TemporaryDirectory | |
21 | from IPython.utils.traitlets import TraitError |
|
13 | from IPython.utils.traitlets import TraitError | |
22 | import IPython.testing.tools as tt |
|
|||
23 | from IPython.html import notebookapp |
|
14 | from IPython.html import notebookapp | |
24 | NotebookApp = notebookapp.NotebookApp |
|
15 | NotebookApp = notebookapp.NotebookApp | |
25 |
|
16 | |||
26 | #----------------------------------------------------------------------------- |
|
|||
27 | # Test functions |
|
|||
28 | #----------------------------------------------------------------------------- |
|
|||
29 |
|
17 | |||
30 | def test_help_output(): |
|
18 | def test_help_output(): | |
31 | """ipython notebook --help-all works""" |
|
19 | """ipython notebook --help-all works""" | |
32 |
|
|
20 | check_help_all_output('IPython.html') | |
33 |
|
21 | |||
34 | def test_server_info_file(): |
|
22 | def test_server_info_file(): | |
35 | nbapp = NotebookApp(profile='nbserver_file_test', log=logging.getLogger()) |
|
23 | nbapp = NotebookApp(profile='nbserver_file_test', log=logging.getLogger()) | |
36 | def get_servers(): |
|
24 | def get_servers(): | |
37 | return list(notebookapp.list_running_servers(profile='nbserver_file_test')) |
|
25 | return list(notebookapp.list_running_servers(profile='nbserver_file_test')) | |
38 | nbapp.initialize(argv=[]) |
|
26 | nbapp.initialize(argv=[]) | |
39 | nbapp.write_server_info_file() |
|
27 | nbapp.write_server_info_file() | |
40 | servers = get_servers() |
|
28 | servers = get_servers() | |
41 | nt.assert_equal(len(servers), 1) |
|
29 | nt.assert_equal(len(servers), 1) | |
42 | nt.assert_equal(servers[0]['port'], nbapp.port) |
|
30 | nt.assert_equal(servers[0]['port'], nbapp.port) | |
43 | nt.assert_equal(servers[0]['url'], nbapp.connection_url) |
|
31 | nt.assert_equal(servers[0]['url'], nbapp.connection_url) | |
44 | nbapp.remove_server_info_file() |
|
32 | nbapp.remove_server_info_file() | |
45 | nt.assert_equal(get_servers(), []) |
|
33 | nt.assert_equal(get_servers(), []) | |
46 |
|
34 | |||
47 | # The ENOENT error should be silenced. |
|
35 | # The ENOENT error should be silenced. | |
48 | nbapp.remove_server_info_file() |
|
36 | nbapp.remove_server_info_file() | |
49 |
|
37 | |||
50 | def test_nb_dir(): |
|
38 | def test_nb_dir(): | |
51 | with TemporaryDirectory() as td: |
|
39 | with TemporaryDirectory() as td: | |
52 | app = NotebookApp(notebook_dir=td) |
|
40 | app = NotebookApp(notebook_dir=td) | |
53 | nt.assert_equal(app.notebook_dir, td) |
|
41 | nt.assert_equal(app.notebook_dir, td) | |
54 |
|
42 | |||
55 | def test_no_create_nb_dir(): |
|
43 | def test_no_create_nb_dir(): | |
56 | with TemporaryDirectory() as td: |
|
44 | with TemporaryDirectory() as td: | |
57 | nbdir = os.path.join(td, 'notebooks') |
|
45 | nbdir = os.path.join(td, 'notebooks') | |
58 | app = NotebookApp() |
|
46 | app = NotebookApp() | |
59 | with nt.assert_raises(TraitError): |
|
47 | with nt.assert_raises(TraitError): | |
60 | app.notebook_dir = nbdir |
|
48 | app.notebook_dir = nbdir | |
61 |
|
49 | |||
62 | def test_missing_nb_dir(): |
|
50 | def test_missing_nb_dir(): | |
63 | with TemporaryDirectory() as td: |
|
51 | with TemporaryDirectory() as td: | |
64 | nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing') |
|
52 | nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing') | |
65 | app = NotebookApp() |
|
53 | app = NotebookApp() | |
66 | with nt.assert_raises(TraitError): |
|
54 | with nt.assert_raises(TraitError): | |
67 | app.notebook_dir = nbdir |
|
55 | app.notebook_dir = nbdir | |
68 |
|
56 | |||
69 | def test_invalid_nb_dir(): |
|
57 | def test_invalid_nb_dir(): | |
70 | with NamedTemporaryFile() as tf: |
|
58 | with NamedTemporaryFile() as tf: | |
71 | app = NotebookApp() |
|
59 | app = NotebookApp() | |
72 | with nt.assert_raises(TraitError): |
|
60 | with nt.assert_raises(TraitError): | |
73 | app.notebook_dir = tf |
|
61 | app.notebook_dir = tf | |
74 |
|
62 |
@@ -1,76 +1,66 b'' | |||||
1 | """Test HTML utils""" |
|
1 | """Test HTML utils""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) Jupyter Development Team. | |
4 | # Copyright (C) 2013 The IPython Development Team |
|
4 | # Distributed under the terms of the Modified BSD License. | |
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 |
|
5 | |||
14 | import os |
|
6 | import os | |
15 |
|
7 | |||
16 | import nose.tools as nt |
|
8 | import nose.tools as nt | |
17 |
|
9 | |||
18 | import IPython.testing.tools as tt |
|
10 | from traitlets.tests.utils import check_help_all_output | |
19 | from IPython.html.utils import url_escape, url_unescape, is_hidden |
|
11 | from IPython.html.utils import url_escape, url_unescape, is_hidden | |
20 | from IPython.utils.tempdir import TemporaryDirectory |
|
12 | from IPython.utils.tempdir import TemporaryDirectory | |
21 |
|
13 | |||
22 | #----------------------------------------------------------------------------- |
|
|||
23 | # Test functions |
|
|||
24 | #----------------------------------------------------------------------------- |
|
|||
25 |
|
14 | |||
26 | def test_help_output(): |
|
15 | def test_help_output(): | |
27 |
""" |
|
16 | """jupyter notebook --help-all works""" | |
28 | tt.help_all_output_test('notebook') |
|
17 | # FIXME: will be jupyter_notebook | |
|
18 | check_help_all_output('IPython.html') | |||
29 |
|
19 | |||
30 |
|
20 | |||
31 | def test_url_escape(): |
|
21 | def test_url_escape(): | |
32 |
|
22 | |||
33 | # changes path or notebook name with special characters to url encoding |
|
23 | # changes path or notebook name with special characters to url encoding | |
34 | # these tests specifically encode paths with spaces |
|
24 | # these tests specifically encode paths with spaces | |
35 | path = url_escape('/this is a test/for spaces/') |
|
25 | path = url_escape('/this is a test/for spaces/') | |
36 | nt.assert_equal(path, '/this%20is%20a%20test/for%20spaces/') |
|
26 | nt.assert_equal(path, '/this%20is%20a%20test/for%20spaces/') | |
37 |
|
27 | |||
38 | path = url_escape('notebook with space.ipynb') |
|
28 | path = url_escape('notebook with space.ipynb') | |
39 | nt.assert_equal(path, 'notebook%20with%20space.ipynb') |
|
29 | nt.assert_equal(path, 'notebook%20with%20space.ipynb') | |
40 |
|
30 | |||
41 | path = url_escape('/path with a/notebook and space.ipynb') |
|
31 | path = url_escape('/path with a/notebook and space.ipynb') | |
42 | nt.assert_equal(path, '/path%20with%20a/notebook%20and%20space.ipynb') |
|
32 | nt.assert_equal(path, '/path%20with%20a/notebook%20and%20space.ipynb') | |
43 |
|
33 | |||
44 | path = url_escape('/ !@$#%^&* / test %^ notebook @#$ name.ipynb') |
|
34 | path = url_escape('/ !@$#%^&* / test %^ notebook @#$ name.ipynb') | |
45 | nt.assert_equal(path, |
|
35 | nt.assert_equal(path, | |
46 | '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb') |
|
36 | '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb') | |
47 |
|
37 | |||
48 | def test_url_unescape(): |
|
38 | def test_url_unescape(): | |
49 |
|
39 | |||
50 | # decodes a url string to a plain string |
|
40 | # decodes a url string to a plain string | |
51 | # these tests decode paths with spaces |
|
41 | # these tests decode paths with spaces | |
52 | path = url_unescape('/this%20is%20a%20test/for%20spaces/') |
|
42 | path = url_unescape('/this%20is%20a%20test/for%20spaces/') | |
53 | nt.assert_equal(path, '/this is a test/for spaces/') |
|
43 | nt.assert_equal(path, '/this is a test/for spaces/') | |
54 |
|
44 | |||
55 | path = url_unescape('notebook%20with%20space.ipynb') |
|
45 | path = url_unescape('notebook%20with%20space.ipynb') | |
56 | nt.assert_equal(path, 'notebook with space.ipynb') |
|
46 | nt.assert_equal(path, 'notebook with space.ipynb') | |
57 |
|
47 | |||
58 | path = url_unescape('/path%20with%20a/notebook%20and%20space.ipynb') |
|
48 | path = url_unescape('/path%20with%20a/notebook%20and%20space.ipynb') | |
59 | nt.assert_equal(path, '/path with a/notebook and space.ipynb') |
|
49 | nt.assert_equal(path, '/path with a/notebook and space.ipynb') | |
60 |
|
50 | |||
61 | path = url_unescape( |
|
51 | path = url_unescape( | |
62 | '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb') |
|
52 | '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb') | |
63 | nt.assert_equal(path, '/ !@$#%^&* / test %^ notebook @#$ name.ipynb') |
|
53 | nt.assert_equal(path, '/ !@$#%^&* / test %^ notebook @#$ name.ipynb') | |
64 |
|
54 | |||
65 | def test_is_hidden(): |
|
55 | def test_is_hidden(): | |
66 | with TemporaryDirectory() as root: |
|
56 | with TemporaryDirectory() as root: | |
67 | subdir1 = os.path.join(root, 'subdir') |
|
57 | subdir1 = os.path.join(root, 'subdir') | |
68 | os.makedirs(subdir1) |
|
58 | os.makedirs(subdir1) | |
69 | nt.assert_equal(is_hidden(subdir1, root), False) |
|
59 | nt.assert_equal(is_hidden(subdir1, root), False) | |
70 | subdir2 = os.path.join(root, '.subdir2') |
|
60 | subdir2 = os.path.join(root, '.subdir2') | |
71 | os.makedirs(subdir2) |
|
61 | os.makedirs(subdir2) | |
72 | nt.assert_equal(is_hidden(subdir2, root), True) |
|
62 | nt.assert_equal(is_hidden(subdir2, root), True) | |
73 | subdir34 = os.path.join(root, 'subdir3', '.subdir4') |
|
63 | subdir34 = os.path.join(root, 'subdir3', '.subdir4') | |
74 | os.makedirs(subdir34) |
|
64 | os.makedirs(subdir34) | |
75 | nt.assert_equal(is_hidden(subdir34, root), True) |
|
65 | nt.assert_equal(is_hidden(subdir34, root), True) | |
76 | nt.assert_equal(is_hidden(subdir34), True) |
|
66 | nt.assert_equal(is_hidden(subdir34), True) |
@@ -1,487 +1,467 b'' | |||||
1 | """Generic testing tools. |
|
1 | """Generic testing tools. | |
2 |
|
2 | |||
3 | Authors |
|
3 | Authors | |
4 | ------- |
|
4 | ------- | |
5 | - Fernando Perez <Fernando.Perez@berkeley.edu> |
|
5 | - Fernando Perez <Fernando.Perez@berkeley.edu> | |
6 | """ |
|
6 | """ | |
7 |
|
7 | |||
8 | from __future__ import absolute_import |
|
8 | from __future__ import absolute_import | |
9 |
|
9 | |||
10 | #----------------------------------------------------------------------------- |
|
10 | #----------------------------------------------------------------------------- | |
11 | # Copyright (C) 2009 The IPython Development Team |
|
11 | # Copyright (C) 2009 The IPython Development Team | |
12 | # |
|
12 | # | |
13 | # Distributed under the terms of the BSD License. The full license is in |
|
13 | # Distributed under the terms of the BSD License. The full license is in | |
14 | # the file COPYING, distributed as part of this software. |
|
14 | # the file COPYING, distributed as part of this software. | |
15 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
16 |
|
16 | |||
17 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
18 | # Imports |
|
18 | # Imports | |
19 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
20 |
|
20 | |||
21 | import os |
|
21 | import os | |
22 | import re |
|
22 | import re | |
23 | import sys |
|
23 | import sys | |
24 | import tempfile |
|
24 | import tempfile | |
25 |
|
25 | |||
26 | from contextlib import contextmanager |
|
26 | from contextlib import contextmanager | |
27 | from io import StringIO |
|
27 | from io import StringIO | |
28 | from subprocess import Popen, PIPE |
|
28 | from subprocess import Popen, PIPE | |
29 |
|
29 | |||
30 | try: |
|
30 | try: | |
31 | # These tools are used by parts of the runtime, so we make the nose |
|
31 | # These tools are used by parts of the runtime, so we make the nose | |
32 | # dependency optional at this point. Nose is a hard dependency to run the |
|
32 | # dependency optional at this point. Nose is a hard dependency to run the | |
33 | # test suite, but NOT to use ipython itself. |
|
33 | # test suite, but NOT to use ipython itself. | |
34 | import nose.tools as nt |
|
34 | import nose.tools as nt | |
35 | has_nose = True |
|
35 | has_nose = True | |
36 | except ImportError: |
|
36 | except ImportError: | |
37 | has_nose = False |
|
37 | has_nose = False | |
38 |
|
38 | |||
39 | from IPython.config.loader import Config |
|
39 | from IPython.config.loader import Config | |
40 | from IPython.utils.process import get_output_error_code |
|
40 | from IPython.utils.process import get_output_error_code | |
41 | from IPython.utils.text import list_strings |
|
41 | from IPython.utils.text import list_strings | |
42 | from IPython.utils.io import temp_pyfile, Tee |
|
42 | from IPython.utils.io import temp_pyfile, Tee | |
43 | from IPython.utils import py3compat |
|
43 | from IPython.utils import py3compat | |
44 | from IPython.utils.encoding import DEFAULT_ENCODING |
|
44 | from IPython.utils.encoding import DEFAULT_ENCODING | |
45 |
|
45 | |||
46 | from . import decorators as dec |
|
46 | from . import decorators as dec | |
47 | from . import skipdoctest |
|
47 | from . import skipdoctest | |
48 |
|
48 | |||
49 | #----------------------------------------------------------------------------- |
|
49 | #----------------------------------------------------------------------------- | |
50 | # Functions and classes |
|
50 | # Functions and classes | |
51 | #----------------------------------------------------------------------------- |
|
51 | #----------------------------------------------------------------------------- | |
52 |
|
52 | |||
53 | # The docstring for full_path doctests differently on win32 (different path |
|
53 | # The docstring for full_path doctests differently on win32 (different path | |
54 | # separator) so just skip the doctest there. The example remains informative. |
|
54 | # separator) so just skip the doctest there. The example remains informative. | |
55 | doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco |
|
55 | doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco | |
56 |
|
56 | |||
57 | @doctest_deco |
|
57 | @doctest_deco | |
58 | def full_path(startPath,files): |
|
58 | def full_path(startPath,files): | |
59 | """Make full paths for all the listed files, based on startPath. |
|
59 | """Make full paths for all the listed files, based on startPath. | |
60 |
|
60 | |||
61 | Only the base part of startPath is kept, since this routine is typically |
|
61 | Only the base part of startPath is kept, since this routine is typically | |
62 | used with a script's ``__file__`` variable as startPath. The base of startPath |
|
62 | used with a script's ``__file__`` variable as startPath. The base of startPath | |
63 | is then prepended to all the listed files, forming the output list. |
|
63 | is then prepended to all the listed files, forming the output list. | |
64 |
|
64 | |||
65 | Parameters |
|
65 | Parameters | |
66 | ---------- |
|
66 | ---------- | |
67 | startPath : string |
|
67 | startPath : string | |
68 | Initial path to use as the base for the results. This path is split |
|
68 | Initial path to use as the base for the results. This path is split | |
69 | using os.path.split() and only its first component is kept. |
|
69 | using os.path.split() and only its first component is kept. | |
70 |
|
70 | |||
71 | files : string or list |
|
71 | files : string or list | |
72 | One or more files. |
|
72 | One or more files. | |
73 |
|
73 | |||
74 | Examples |
|
74 | Examples | |
75 | -------- |
|
75 | -------- | |
76 |
|
76 | |||
77 | >>> full_path('/foo/bar.py',['a.txt','b.txt']) |
|
77 | >>> full_path('/foo/bar.py',['a.txt','b.txt']) | |
78 | ['/foo/a.txt', '/foo/b.txt'] |
|
78 | ['/foo/a.txt', '/foo/b.txt'] | |
79 |
|
79 | |||
80 | >>> full_path('/foo',['a.txt','b.txt']) |
|
80 | >>> full_path('/foo',['a.txt','b.txt']) | |
81 | ['/a.txt', '/b.txt'] |
|
81 | ['/a.txt', '/b.txt'] | |
82 |
|
82 | |||
83 | If a single file is given, the output is still a list:: |
|
83 | If a single file is given, the output is still a list:: | |
84 |
|
84 | |||
85 | >>> full_path('/foo','a.txt') |
|
85 | >>> full_path('/foo','a.txt') | |
86 | ['/a.txt'] |
|
86 | ['/a.txt'] | |
87 | """ |
|
87 | """ | |
88 |
|
88 | |||
89 | files = list_strings(files) |
|
89 | files = list_strings(files) | |
90 | base = os.path.split(startPath)[0] |
|
90 | base = os.path.split(startPath)[0] | |
91 | return [ os.path.join(base,f) for f in files ] |
|
91 | return [ os.path.join(base,f) for f in files ] | |
92 |
|
92 | |||
93 |
|
93 | |||
94 | def parse_test_output(txt): |
|
94 | def parse_test_output(txt): | |
95 | """Parse the output of a test run and return errors, failures. |
|
95 | """Parse the output of a test run and return errors, failures. | |
96 |
|
96 | |||
97 | Parameters |
|
97 | Parameters | |
98 | ---------- |
|
98 | ---------- | |
99 | txt : str |
|
99 | txt : str | |
100 | Text output of a test run, assumed to contain a line of one of the |
|
100 | Text output of a test run, assumed to contain a line of one of the | |
101 | following forms:: |
|
101 | following forms:: | |
102 |
|
102 | |||
103 | 'FAILED (errors=1)' |
|
103 | 'FAILED (errors=1)' | |
104 | 'FAILED (failures=1)' |
|
104 | 'FAILED (failures=1)' | |
105 | 'FAILED (errors=1, failures=1)' |
|
105 | 'FAILED (errors=1, failures=1)' | |
106 |
|
106 | |||
107 | Returns |
|
107 | Returns | |
108 | ------- |
|
108 | ------- | |
109 | nerr, nfail |
|
109 | nerr, nfail | |
110 | number of errors and failures. |
|
110 | number of errors and failures. | |
111 | """ |
|
111 | """ | |
112 |
|
112 | |||
113 | err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE) |
|
113 | err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE) | |
114 | if err_m: |
|
114 | if err_m: | |
115 | nerr = int(err_m.group(1)) |
|
115 | nerr = int(err_m.group(1)) | |
116 | nfail = 0 |
|
116 | nfail = 0 | |
117 | return nerr, nfail |
|
117 | return nerr, nfail | |
118 |
|
118 | |||
119 | fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE) |
|
119 | fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE) | |
120 | if fail_m: |
|
120 | if fail_m: | |
121 | nerr = 0 |
|
121 | nerr = 0 | |
122 | nfail = int(fail_m.group(1)) |
|
122 | nfail = int(fail_m.group(1)) | |
123 | return nerr, nfail |
|
123 | return nerr, nfail | |
124 |
|
124 | |||
125 | both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt, |
|
125 | both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt, | |
126 | re.MULTILINE) |
|
126 | re.MULTILINE) | |
127 | if both_m: |
|
127 | if both_m: | |
128 | nerr = int(both_m.group(1)) |
|
128 | nerr = int(both_m.group(1)) | |
129 | nfail = int(both_m.group(2)) |
|
129 | nfail = int(both_m.group(2)) | |
130 | return nerr, nfail |
|
130 | return nerr, nfail | |
131 |
|
131 | |||
132 | # If the input didn't match any of these forms, assume no error/failures |
|
132 | # If the input didn't match any of these forms, assume no error/failures | |
133 | return 0, 0 |
|
133 | return 0, 0 | |
134 |
|
134 | |||
135 |
|
135 | |||
136 | # So nose doesn't think this is a test |
|
136 | # So nose doesn't think this is a test | |
137 | parse_test_output.__test__ = False |
|
137 | parse_test_output.__test__ = False | |
138 |
|
138 | |||
139 |
|
139 | |||
140 | def default_argv(): |
|
140 | def default_argv(): | |
141 | """Return a valid default argv for creating testing instances of ipython""" |
|
141 | """Return a valid default argv for creating testing instances of ipython""" | |
142 |
|
142 | |||
143 | return ['--quick', # so no config file is loaded |
|
143 | return ['--quick', # so no config file is loaded | |
144 | # Other defaults to minimize side effects on stdout |
|
144 | # Other defaults to minimize side effects on stdout | |
145 | '--colors=NoColor', '--no-term-title','--no-banner', |
|
145 | '--colors=NoColor', '--no-term-title','--no-banner', | |
146 | '--autocall=0'] |
|
146 | '--autocall=0'] | |
147 |
|
147 | |||
148 |
|
148 | |||
149 | def default_config(): |
|
149 | def default_config(): | |
150 | """Return a config object with good defaults for testing.""" |
|
150 | """Return a config object with good defaults for testing.""" | |
151 | config = Config() |
|
151 | config = Config() | |
152 | config.TerminalInteractiveShell.colors = 'NoColor' |
|
152 | config.TerminalInteractiveShell.colors = 'NoColor' | |
153 | config.TerminalTerminalInteractiveShell.term_title = False, |
|
153 | config.TerminalTerminalInteractiveShell.term_title = False, | |
154 | config.TerminalInteractiveShell.autocall = 0 |
|
154 | config.TerminalInteractiveShell.autocall = 0 | |
155 | f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False) |
|
155 | f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False) | |
156 | config.HistoryManager.hist_file = f.name |
|
156 | config.HistoryManager.hist_file = f.name | |
157 | f.close() |
|
157 | f.close() | |
158 | config.HistoryManager.db_cache_size = 10000 |
|
158 | config.HistoryManager.db_cache_size = 10000 | |
159 | return config |
|
159 | return config | |
160 |
|
160 | |||
161 |
|
161 | |||
162 | def get_ipython_cmd(as_string=False): |
|
162 | def get_ipython_cmd(as_string=False): | |
163 | """ |
|
163 | """ | |
164 | Return appropriate IPython command line name. By default, this will return |
|
164 | Return appropriate IPython command line name. By default, this will return | |
165 | a list that can be used with subprocess.Popen, for example, but passing |
|
165 | a list that can be used with subprocess.Popen, for example, but passing | |
166 | `as_string=True` allows for returning the IPython command as a string. |
|
166 | `as_string=True` allows for returning the IPython command as a string. | |
167 |
|
167 | |||
168 | Parameters |
|
168 | Parameters | |
169 | ---------- |
|
169 | ---------- | |
170 | as_string: bool |
|
170 | as_string: bool | |
171 | Flag to allow to return the command as a string. |
|
171 | Flag to allow to return the command as a string. | |
172 | """ |
|
172 | """ | |
173 | ipython_cmd = [sys.executable, "-m", "IPython"] |
|
173 | ipython_cmd = [sys.executable, "-m", "IPython"] | |
174 |
|
174 | |||
175 | if as_string: |
|
175 | if as_string: | |
176 | ipython_cmd = " ".join(ipython_cmd) |
|
176 | ipython_cmd = " ".join(ipython_cmd) | |
177 |
|
177 | |||
178 | return ipython_cmd |
|
178 | return ipython_cmd | |
179 |
|
179 | |||
180 | def ipexec(fname, options=None, commands=()): |
|
180 | def ipexec(fname, options=None, commands=()): | |
181 | """Utility to call 'ipython filename'. |
|
181 | """Utility to call 'ipython filename'. | |
182 |
|
182 | |||
183 | Starts IPython with a minimal and safe configuration to make startup as fast |
|
183 | Starts IPython with a minimal and safe configuration to make startup as fast | |
184 | as possible. |
|
184 | as possible. | |
185 |
|
185 | |||
186 | Note that this starts IPython in a subprocess! |
|
186 | Note that this starts IPython in a subprocess! | |
187 |
|
187 | |||
188 | Parameters |
|
188 | Parameters | |
189 | ---------- |
|
189 | ---------- | |
190 | fname : str |
|
190 | fname : str | |
191 | Name of file to be executed (should have .py or .ipy extension). |
|
191 | Name of file to be executed (should have .py or .ipy extension). | |
192 |
|
192 | |||
193 | options : optional, list |
|
193 | options : optional, list | |
194 | Extra command-line flags to be passed to IPython. |
|
194 | Extra command-line flags to be passed to IPython. | |
195 |
|
195 | |||
196 | commands : optional, list |
|
196 | commands : optional, list | |
197 | Commands to send in on stdin |
|
197 | Commands to send in on stdin | |
198 |
|
198 | |||
199 | Returns |
|
199 | Returns | |
200 | ------- |
|
200 | ------- | |
201 | (stdout, stderr) of ipython subprocess. |
|
201 | (stdout, stderr) of ipython subprocess. | |
202 | """ |
|
202 | """ | |
203 | if options is None: options = [] |
|
203 | if options is None: options = [] | |
204 |
|
204 | |||
205 | # For these subprocess calls, eliminate all prompt printing so we only see |
|
205 | # For these subprocess calls, eliminate all prompt printing so we only see | |
206 | # output from script execution |
|
206 | # output from script execution | |
207 | prompt_opts = [ '--PromptManager.in_template=""', |
|
207 | prompt_opts = [ '--PromptManager.in_template=""', | |
208 | '--PromptManager.in2_template=""', |
|
208 | '--PromptManager.in2_template=""', | |
209 | '--PromptManager.out_template=""' |
|
209 | '--PromptManager.out_template=""' | |
210 | ] |
|
210 | ] | |
211 | cmdargs = default_argv() + prompt_opts + options |
|
211 | cmdargs = default_argv() + prompt_opts + options | |
212 |
|
212 | |||
213 | test_dir = os.path.dirname(__file__) |
|
213 | test_dir = os.path.dirname(__file__) | |
214 |
|
214 | |||
215 | ipython_cmd = get_ipython_cmd() |
|
215 | ipython_cmd = get_ipython_cmd() | |
216 | # Absolute path for filename |
|
216 | # Absolute path for filename | |
217 | full_fname = os.path.join(test_dir, fname) |
|
217 | full_fname = os.path.join(test_dir, fname) | |
218 | full_cmd = ipython_cmd + cmdargs + [full_fname] |
|
218 | full_cmd = ipython_cmd + cmdargs + [full_fname] | |
219 | env = os.environ.copy() |
|
219 | env = os.environ.copy() | |
220 | # FIXME: ignore all warnings in ipexec while we have shims |
|
220 | # FIXME: ignore all warnings in ipexec while we have shims | |
221 | # should we keep suppressing warnings here, even after removing shims? |
|
221 | # should we keep suppressing warnings here, even after removing shims? | |
222 | env['PYTHONWARNINGS'] = 'ignore' |
|
222 | env['PYTHONWARNINGS'] = 'ignore' | |
223 | # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr |
|
223 | # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr | |
224 | for k, v in env.items(): |
|
224 | for k, v in env.items(): | |
225 | # Debug a bizarre failure we've seen on Windows: |
|
225 | # Debug a bizarre failure we've seen on Windows: | |
226 | # TypeError: environment can only contain strings |
|
226 | # TypeError: environment can only contain strings | |
227 | if not isinstance(v, str): |
|
227 | if not isinstance(v, str): | |
228 | print(k, v) |
|
228 | print(k, v) | |
229 | p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env) |
|
229 | p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env) | |
230 | out, err = p.communicate(input=py3compat.str_to_bytes('\n'.join(commands)) or None) |
|
230 | out, err = p.communicate(input=py3compat.str_to_bytes('\n'.join(commands)) or None) | |
231 | out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err) |
|
231 | out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err) | |
232 | # `import readline` causes 'ESC[?1034h' to be output sometimes, |
|
232 | # `import readline` causes 'ESC[?1034h' to be output sometimes, | |
233 | # so strip that out before doing comparisons |
|
233 | # so strip that out before doing comparisons | |
234 | if out: |
|
234 | if out: | |
235 | out = re.sub(r'\x1b\[[^h]+h', '', out) |
|
235 | out = re.sub(r'\x1b\[[^h]+h', '', out) | |
236 | return out, err |
|
236 | return out, err | |
237 |
|
237 | |||
238 |
|
238 | |||
239 | def ipexec_validate(fname, expected_out, expected_err='', |
|
239 | def ipexec_validate(fname, expected_out, expected_err='', | |
240 | options=None, commands=()): |
|
240 | options=None, commands=()): | |
241 | """Utility to call 'ipython filename' and validate output/error. |
|
241 | """Utility to call 'ipython filename' and validate output/error. | |
242 |
|
242 | |||
243 | This function raises an AssertionError if the validation fails. |
|
243 | This function raises an AssertionError if the validation fails. | |
244 |
|
244 | |||
245 | Note that this starts IPython in a subprocess! |
|
245 | Note that this starts IPython in a subprocess! | |
246 |
|
246 | |||
247 | Parameters |
|
247 | Parameters | |
248 | ---------- |
|
248 | ---------- | |
249 | fname : str |
|
249 | fname : str | |
250 | Name of the file to be executed (should have .py or .ipy extension). |
|
250 | Name of the file to be executed (should have .py or .ipy extension). | |
251 |
|
251 | |||
252 | expected_out : str |
|
252 | expected_out : str | |
253 | Expected stdout of the process. |
|
253 | Expected stdout of the process. | |
254 |
|
254 | |||
255 | expected_err : optional, str |
|
255 | expected_err : optional, str | |
256 | Expected stderr of the process. |
|
256 | Expected stderr of the process. | |
257 |
|
257 | |||
258 | options : optional, list |
|
258 | options : optional, list | |
259 | Extra command-line flags to be passed to IPython. |
|
259 | Extra command-line flags to be passed to IPython. | |
260 |
|
260 | |||
261 | Returns |
|
261 | Returns | |
262 | ------- |
|
262 | ------- | |
263 | None |
|
263 | None | |
264 | """ |
|
264 | """ | |
265 |
|
265 | |||
266 | import nose.tools as nt |
|
266 | import nose.tools as nt | |
267 |
|
267 | |||
268 | out, err = ipexec(fname, options, commands) |
|
268 | out, err = ipexec(fname, options, commands) | |
269 | #print 'OUT', out # dbg |
|
269 | #print 'OUT', out # dbg | |
270 | #print 'ERR', err # dbg |
|
270 | #print 'ERR', err # dbg | |
271 | # If there are any errors, we must check those befor stdout, as they may be |
|
271 | # If there are any errors, we must check those befor stdout, as they may be | |
272 | # more informative than simply having an empty stdout. |
|
272 | # more informative than simply having an empty stdout. | |
273 | if err: |
|
273 | if err: | |
274 | if expected_err: |
|
274 | if expected_err: | |
275 | nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines())) |
|
275 | nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines())) | |
276 | else: |
|
276 | else: | |
277 | raise ValueError('Running file %r produced error: %r' % |
|
277 | raise ValueError('Running file %r produced error: %r' % | |
278 | (fname, err)) |
|
278 | (fname, err)) | |
279 | # If no errors or output on stderr was expected, match stdout |
|
279 | # If no errors or output on stderr was expected, match stdout | |
280 | nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines())) |
|
280 | nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines())) | |
281 |
|
281 | |||
282 |
|
282 | |||
283 | class TempFileMixin(object): |
|
283 | class TempFileMixin(object): | |
284 | """Utility class to create temporary Python/IPython files. |
|
284 | """Utility class to create temporary Python/IPython files. | |
285 |
|
285 | |||
286 | Meant as a mixin class for test cases.""" |
|
286 | Meant as a mixin class for test cases.""" | |
287 |
|
287 | |||
288 | def mktmp(self, src, ext='.py'): |
|
288 | def mktmp(self, src, ext='.py'): | |
289 | """Make a valid python temp file.""" |
|
289 | """Make a valid python temp file.""" | |
290 | fname, f = temp_pyfile(src, ext) |
|
290 | fname, f = temp_pyfile(src, ext) | |
291 | self.tmpfile = f |
|
291 | self.tmpfile = f | |
292 | self.fname = fname |
|
292 | self.fname = fname | |
293 |
|
293 | |||
294 | def tearDown(self): |
|
294 | def tearDown(self): | |
295 | if hasattr(self, 'tmpfile'): |
|
295 | if hasattr(self, 'tmpfile'): | |
296 | # If the tmpfile wasn't made because of skipped tests, like in |
|
296 | # If the tmpfile wasn't made because of skipped tests, like in | |
297 | # win32, there's nothing to cleanup. |
|
297 | # win32, there's nothing to cleanup. | |
298 | self.tmpfile.close() |
|
298 | self.tmpfile.close() | |
299 | try: |
|
299 | try: | |
300 | os.unlink(self.fname) |
|
300 | os.unlink(self.fname) | |
301 | except: |
|
301 | except: | |
302 | # On Windows, even though we close the file, we still can't |
|
302 | # On Windows, even though we close the file, we still can't | |
303 | # delete it. I have no clue why |
|
303 | # delete it. I have no clue why | |
304 | pass |
|
304 | pass | |
305 |
|
305 | |||
306 | pair_fail_msg = ("Testing {0}\n\n" |
|
306 | pair_fail_msg = ("Testing {0}\n\n" | |
307 | "In:\n" |
|
307 | "In:\n" | |
308 | " {1!r}\n" |
|
308 | " {1!r}\n" | |
309 | "Expected:\n" |
|
309 | "Expected:\n" | |
310 | " {2!r}\n" |
|
310 | " {2!r}\n" | |
311 | "Got:\n" |
|
311 | "Got:\n" | |
312 | " {3!r}\n") |
|
312 | " {3!r}\n") | |
313 | def check_pairs(func, pairs): |
|
313 | def check_pairs(func, pairs): | |
314 | """Utility function for the common case of checking a function with a |
|
314 | """Utility function for the common case of checking a function with a | |
315 | sequence of input/output pairs. |
|
315 | sequence of input/output pairs. | |
316 |
|
316 | |||
317 | Parameters |
|
317 | Parameters | |
318 | ---------- |
|
318 | ---------- | |
319 | func : callable |
|
319 | func : callable | |
320 | The function to be tested. Should accept a single argument. |
|
320 | The function to be tested. Should accept a single argument. | |
321 | pairs : iterable |
|
321 | pairs : iterable | |
322 | A list of (input, expected_output) tuples. |
|
322 | A list of (input, expected_output) tuples. | |
323 |
|
323 | |||
324 | Returns |
|
324 | Returns | |
325 | ------- |
|
325 | ------- | |
326 | None. Raises an AssertionError if any output does not match the expected |
|
326 | None. Raises an AssertionError if any output does not match the expected | |
327 | value. |
|
327 | value. | |
328 | """ |
|
328 | """ | |
329 | name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>")) |
|
329 | name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>")) | |
330 | for inp, expected in pairs: |
|
330 | for inp, expected in pairs: | |
331 | out = func(inp) |
|
331 | out = func(inp) | |
332 | assert out == expected, pair_fail_msg.format(name, inp, expected, out) |
|
332 | assert out == expected, pair_fail_msg.format(name, inp, expected, out) | |
333 |
|
333 | |||
334 |
|
334 | |||
335 | if py3compat.PY3: |
|
335 | if py3compat.PY3: | |
336 | MyStringIO = StringIO |
|
336 | MyStringIO = StringIO | |
337 | else: |
|
337 | else: | |
338 | # In Python 2, stdout/stderr can have either bytes or unicode written to them, |
|
338 | # In Python 2, stdout/stderr can have either bytes or unicode written to them, | |
339 | # so we need a class that can handle both. |
|
339 | # so we need a class that can handle both. | |
340 | class MyStringIO(StringIO): |
|
340 | class MyStringIO(StringIO): | |
341 | def write(self, s): |
|
341 | def write(self, s): | |
342 | s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING) |
|
342 | s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING) | |
343 | super(MyStringIO, self).write(s) |
|
343 | super(MyStringIO, self).write(s) | |
344 |
|
344 | |||
345 | _re_type = type(re.compile(r'')) |
|
345 | _re_type = type(re.compile(r'')) | |
346 |
|
346 | |||
347 | notprinted_msg = """Did not find {0!r} in printed output (on {1}): |
|
347 | notprinted_msg = """Did not find {0!r} in printed output (on {1}): | |
348 | ------- |
|
348 | ------- | |
349 | {2!s} |
|
349 | {2!s} | |
350 | ------- |
|
350 | ------- | |
351 | """ |
|
351 | """ | |
352 |
|
352 | |||
353 | class AssertPrints(object): |
|
353 | class AssertPrints(object): | |
354 | """Context manager for testing that code prints certain text. |
|
354 | """Context manager for testing that code prints certain text. | |
355 |
|
355 | |||
356 | Examples |
|
356 | Examples | |
357 | -------- |
|
357 | -------- | |
358 | >>> with AssertPrints("abc", suppress=False): |
|
358 | >>> with AssertPrints("abc", suppress=False): | |
359 | ... print("abcd") |
|
359 | ... print("abcd") | |
360 | ... print("def") |
|
360 | ... print("def") | |
361 | ... |
|
361 | ... | |
362 | abcd |
|
362 | abcd | |
363 | def |
|
363 | def | |
364 | """ |
|
364 | """ | |
365 | def __init__(self, s, channel='stdout', suppress=True): |
|
365 | def __init__(self, s, channel='stdout', suppress=True): | |
366 | self.s = s |
|
366 | self.s = s | |
367 | if isinstance(self.s, (py3compat.string_types, _re_type)): |
|
367 | if isinstance(self.s, (py3compat.string_types, _re_type)): | |
368 | self.s = [self.s] |
|
368 | self.s = [self.s] | |
369 | self.channel = channel |
|
369 | self.channel = channel | |
370 | self.suppress = suppress |
|
370 | self.suppress = suppress | |
371 |
|
371 | |||
372 | def __enter__(self): |
|
372 | def __enter__(self): | |
373 | self.orig_stream = getattr(sys, self.channel) |
|
373 | self.orig_stream = getattr(sys, self.channel) | |
374 | self.buffer = MyStringIO() |
|
374 | self.buffer = MyStringIO() | |
375 | self.tee = Tee(self.buffer, channel=self.channel) |
|
375 | self.tee = Tee(self.buffer, channel=self.channel) | |
376 | setattr(sys, self.channel, self.buffer if self.suppress else self.tee) |
|
376 | setattr(sys, self.channel, self.buffer if self.suppress else self.tee) | |
377 |
|
377 | |||
378 | def __exit__(self, etype, value, traceback): |
|
378 | def __exit__(self, etype, value, traceback): | |
379 | try: |
|
379 | try: | |
380 | if value is not None: |
|
380 | if value is not None: | |
381 | # If an error was raised, don't check anything else |
|
381 | # If an error was raised, don't check anything else | |
382 | return False |
|
382 | return False | |
383 | self.tee.flush() |
|
383 | self.tee.flush() | |
384 | setattr(sys, self.channel, self.orig_stream) |
|
384 | setattr(sys, self.channel, self.orig_stream) | |
385 | printed = self.buffer.getvalue() |
|
385 | printed = self.buffer.getvalue() | |
386 | for s in self.s: |
|
386 | for s in self.s: | |
387 | if isinstance(s, _re_type): |
|
387 | if isinstance(s, _re_type): | |
388 | assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed) |
|
388 | assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed) | |
389 | else: |
|
389 | else: | |
390 | assert s in printed, notprinted_msg.format(s, self.channel, printed) |
|
390 | assert s in printed, notprinted_msg.format(s, self.channel, printed) | |
391 | return False |
|
391 | return False | |
392 | finally: |
|
392 | finally: | |
393 | self.tee.close() |
|
393 | self.tee.close() | |
394 |
|
394 | |||
395 | printed_msg = """Found {0!r} in printed output (on {1}): |
|
395 | printed_msg = """Found {0!r} in printed output (on {1}): | |
396 | ------- |
|
396 | ------- | |
397 | {2!s} |
|
397 | {2!s} | |
398 | ------- |
|
398 | ------- | |
399 | """ |
|
399 | """ | |
400 |
|
400 | |||
401 | class AssertNotPrints(AssertPrints): |
|
401 | class AssertNotPrints(AssertPrints): | |
402 | """Context manager for checking that certain output *isn't* produced. |
|
402 | """Context manager for checking that certain output *isn't* produced. | |
403 |
|
403 | |||
404 | Counterpart of AssertPrints""" |
|
404 | Counterpart of AssertPrints""" | |
405 | def __exit__(self, etype, value, traceback): |
|
405 | def __exit__(self, etype, value, traceback): | |
406 | try: |
|
406 | try: | |
407 | if value is not None: |
|
407 | if value is not None: | |
408 | # If an error was raised, don't check anything else |
|
408 | # If an error was raised, don't check anything else | |
409 | self.tee.close() |
|
409 | self.tee.close() | |
410 | return False |
|
410 | return False | |
411 | self.tee.flush() |
|
411 | self.tee.flush() | |
412 | setattr(sys, self.channel, self.orig_stream) |
|
412 | setattr(sys, self.channel, self.orig_stream) | |
413 | printed = self.buffer.getvalue() |
|
413 | printed = self.buffer.getvalue() | |
414 | for s in self.s: |
|
414 | for s in self.s: | |
415 | if isinstance(s, _re_type): |
|
415 | if isinstance(s, _re_type): | |
416 | assert not s.search(printed),printed_msg.format( |
|
416 | assert not s.search(printed),printed_msg.format( | |
417 | s.pattern, self.channel, printed) |
|
417 | s.pattern, self.channel, printed) | |
418 | else: |
|
418 | else: | |
419 | assert s not in printed, printed_msg.format( |
|
419 | assert s not in printed, printed_msg.format( | |
420 | s, self.channel, printed) |
|
420 | s, self.channel, printed) | |
421 | return False |
|
421 | return False | |
422 | finally: |
|
422 | finally: | |
423 | self.tee.close() |
|
423 | self.tee.close() | |
424 |
|
424 | |||
425 | @contextmanager |
|
425 | @contextmanager | |
426 | def mute_warn(): |
|
426 | def mute_warn(): | |
427 | from IPython.utils import warn |
|
427 | from IPython.utils import warn | |
428 | save_warn = warn.warn |
|
428 | save_warn = warn.warn | |
429 | warn.warn = lambda *a, **kw: None |
|
429 | warn.warn = lambda *a, **kw: None | |
430 | try: |
|
430 | try: | |
431 | yield |
|
431 | yield | |
432 | finally: |
|
432 | finally: | |
433 | warn.warn = save_warn |
|
433 | warn.warn = save_warn | |
434 |
|
434 | |||
435 | @contextmanager |
|
435 | @contextmanager | |
436 | def make_tempfile(name): |
|
436 | def make_tempfile(name): | |
437 | """ Create an empty, named, temporary file for the duration of the context. |
|
437 | """ Create an empty, named, temporary file for the duration of the context. | |
438 | """ |
|
438 | """ | |
439 | f = open(name, 'w') |
|
439 | f = open(name, 'w') | |
440 | f.close() |
|
440 | f.close() | |
441 | try: |
|
441 | try: | |
442 | yield |
|
442 | yield | |
443 | finally: |
|
443 | finally: | |
444 | os.unlink(name) |
|
444 | os.unlink(name) | |
445 |
|
445 | |||
446 |
|
446 | |||
447 | def help_output_test(subcommand=''): |
|
447 | def help_output_test(subcommand=''): | |
448 | """test that `ipython [subcommand] -h` works""" |
|
448 | """test that `ipython [subcommand] -h` works""" | |
449 | cmd = get_ipython_cmd() + [subcommand, '-h'] |
|
449 | cmd = get_ipython_cmd() + [subcommand, '-h'] | |
450 | out, err, rc = get_output_error_code(cmd) |
|
450 | out, err, rc = get_output_error_code(cmd) | |
451 | nt.assert_equal(rc, 0, err) |
|
451 | nt.assert_equal(rc, 0, err) | |
452 | nt.assert_not_in("Traceback", err) |
|
452 | nt.assert_not_in("Traceback", err) | |
453 | nt.assert_in("Options", out) |
|
453 | nt.assert_in("Options", out) | |
454 | nt.assert_in("--help-all", out) |
|
454 | nt.assert_in("--help-all", out) | |
455 | return out, err |
|
455 | return out, err | |
456 |
|
456 | |||
457 |
|
457 | |||
458 | def help_all_output_test(subcommand=''): |
|
458 | def help_all_output_test(subcommand=''): | |
459 | """test that `ipython [subcommand] --help-all` works""" |
|
459 | """test that `ipython [subcommand] --help-all` works""" | |
460 | cmd = get_ipython_cmd() + [subcommand, '--help-all'] |
|
460 | cmd = get_ipython_cmd() + [subcommand, '--help-all'] | |
461 | out, err, rc = get_output_error_code(cmd) |
|
461 | out, err, rc = get_output_error_code(cmd) | |
462 | nt.assert_equal(rc, 0, err) |
|
462 | nt.assert_equal(rc, 0, err) | |
463 | nt.assert_not_in("Traceback", err) |
|
463 | nt.assert_not_in("Traceback", err) | |
464 | nt.assert_in("Options", out) |
|
464 | nt.assert_in("Options", out) | |
465 | nt.assert_in("Class parameters", out) |
|
465 | nt.assert_in("Class parameters", out) | |
466 | return out, err |
|
466 | return out, err | |
467 |
|
467 | |||
468 | def assert_big_text_equal(a, b, chunk_size=80): |
|
|||
469 | """assert that large strings are equal |
|
|||
470 |
|
||||
471 | Zooms in on first chunk that differs, |
|
|||
472 | to give better info than vanilla assertEqual for large text blobs. |
|
|||
473 | """ |
|
|||
474 | for i in range(0, len(a), chunk_size): |
|
|||
475 | chunk_a = a[i:i + chunk_size] |
|
|||
476 | chunk_b = b[i:i + chunk_size] |
|
|||
477 | nt.assert_equal(chunk_a, chunk_b, "[offset: %i]\n%r != \n%r" % ( |
|
|||
478 | i, chunk_a, chunk_b)) |
|
|||
479 |
|
||||
480 | if len(a) > len(b): |
|
|||
481 | nt.fail("Length doesn't match (%i > %i). Extra text:\n%r" % ( |
|
|||
482 | len(a), len(b), a[len(b):] |
|
|||
483 | )) |
|
|||
484 | elif len(a) < len(b): |
|
|||
485 | nt.fail("Length doesn't match (%i < %i). Extra text:\n%r" % ( |
|
|||
486 | len(a), len(b), b[len(a):] |
|
|||
487 | )) |
|
@@ -1,78 +1,64 b'' | |||||
1 | """Tests for two-process terminal frontend |
|
1 | """Tests for two-process terminal frontend""" | |
2 |
|
2 | |||
3 | Currently only has the most simple test possible, starting a console and running |
|
3 | # Copyright (c) Jupyter Development Team. | |
4 | a single command. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
||||
6 | Authors: |
|
|||
7 |
|
||||
8 | * Min RK |
|
|||
9 | """ |
|
|||
10 |
|
||||
11 | #----------------------------------------------------------------------------- |
|
|||
12 | # Imports |
|
|||
13 | #----------------------------------------------------------------------------- |
|
|||
14 |
|
5 | |||
15 | import sys |
|
6 | import sys | |
16 |
|
7 | |||
17 | from nose import SkipTest |
|
8 | from nose import SkipTest | |
18 |
|
9 | |||
19 | import IPython.testing.tools as tt |
|
10 | from traitlets.tests.utils import check_help_all_output | |
20 | from IPython.testing import decorators as dec |
|
11 | from IPython.testing import decorators as dec | |
21 |
|
12 | |||
22 | #----------------------------------------------------------------------------- |
|
|||
23 | # Tests |
|
|||
24 | #----------------------------------------------------------------------------- |
|
|||
25 |
|
||||
26 | @dec.skip_win32 |
|
13 | @dec.skip_win32 | |
27 | def test_console_starts(): |
|
14 | def test_console_starts(): | |
28 | """test that `ipython console` starts a terminal""" |
|
15 | """test that `ipython console` starts a terminal""" | |
29 | p, pexpect, t = start_console() |
|
16 | p, pexpect, t = start_console() | |
30 | p.sendline('5') |
|
17 | p.sendline('5') | |
31 | idx = p.expect([r'Out\[\d+\]: 5', pexpect.EOF], timeout=t) |
|
18 | idx = p.expect([r'Out\[\d+\]: 5', pexpect.EOF], timeout=t) | |
32 | idx = p.expect([r'In \[\d+\]', pexpect.EOF], timeout=t) |
|
19 | idx = p.expect([r'In \[\d+\]', pexpect.EOF], timeout=t) | |
33 | stop_console(p, pexpect, t) |
|
20 | stop_console(p, pexpect, t) | |
34 |
|
21 | |||
35 | def test_help_output(): |
|
22 | def test_help_output(): | |
36 | """ipython console --help-all works""" |
|
23 | """ipython console --help-all works""" | |
37 |
|
|
24 | check_help_all_output('jupyter_console') | |
38 |
|
||||
39 |
|
25 | |||
40 | def test_display_text(): |
|
26 | def test_display_text(): | |
41 | "Ensure display protocol plain/text key is supported" |
|
27 | "Ensure display protocol plain/text key is supported" | |
42 | # equivalent of: |
|
28 | # equivalent of: | |
43 | # |
|
29 | # | |
44 | # x = %lsmagic |
|
30 | # x = %lsmagic | |
45 | # from IPython.display import display; display(x); |
|
31 | # from IPython.display import display; display(x); | |
46 | p, pexpect, t = start_console() |
|
32 | p, pexpect, t = start_console() | |
47 | p.sendline('x = %lsmagic') |
|
33 | p.sendline('x = %lsmagic') | |
48 | idx = p.expect([r'In \[\d+\]', pexpect.EOF], timeout=t) |
|
34 | idx = p.expect([r'In \[\d+\]', pexpect.EOF], timeout=t) | |
49 | p.sendline('from IPython.display import display; display(x);') |
|
35 | p.sendline('from IPython.display import display; display(x);') | |
50 | p.expect([r'Available line magics:', pexpect.EOF], timeout=t) |
|
36 | p.expect([r'Available line magics:', pexpect.EOF], timeout=t) | |
51 | stop_console(p, pexpect, t) |
|
37 | stop_console(p, pexpect, t) | |
52 |
|
38 | |||
53 | def stop_console(p, pexpect, t): |
|
39 | def stop_console(p, pexpect, t): | |
54 | "Stop a running `ipython console` running via pexpect" |
|
40 | "Stop a running `ipython console` running via pexpect" | |
55 | # send ctrl-D;ctrl-D to exit |
|
41 | # send ctrl-D;ctrl-D to exit | |
56 | p.sendeof() |
|
42 | p.sendeof() | |
57 | p.sendeof() |
|
43 | p.sendeof() | |
58 | p.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=t) |
|
44 | p.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=t) | |
59 | if p.isalive(): |
|
45 | if p.isalive(): | |
60 | p.terminate() |
|
46 | p.terminate() | |
61 |
|
47 | |||
62 |
|
48 | |||
63 | def start_console(): |
|
49 | def start_console(): | |
64 | "Start `ipython console` using pexpect" |
|
50 | "Start `ipython console` using pexpect" | |
65 | import pexpect |
|
51 | import pexpect | |
66 |
|
52 | |||
67 | args = ['-m', 'IPython', 'console', '--colors=NoColor'] |
|
53 | args = ['-m', 'IPython', 'console', '--colors=NoColor'] | |
68 | cmd = sys.executable |
|
54 | cmd = sys.executable | |
69 |
|
55 | |||
70 | try: |
|
56 | try: | |
71 | p = pexpect.spawn(cmd, args=args) |
|
57 | p = pexpect.spawn(cmd, args=args) | |
72 | except IOError: |
|
58 | except IOError: | |
73 | raise SkipTest("Couldn't find command %s" % cmd) |
|
59 | raise SkipTest("Couldn't find command %s" % cmd) | |
74 |
|
60 | |||
75 | # timeout after one minute |
|
61 | # timeout after one minute | |
76 | t = 60 |
|
62 | t = 60 | |
77 | idx = p.expect([r'In \[\d+\]', pexpect.EOF], timeout=t) |
|
63 | idx = p.expect([r'In \[\d+\]', pexpect.EOF], timeout=t) | |
78 | return p, pexpect, t |
|
64 | return p, pexpect, t |
@@ -1,95 +1,94 b'' | |||||
1 | # Copyright (c) IPython Development Team. |
|
1 | # Copyright (c) IPython Development Team. | |
2 | # Distributed under the terms of the Modified BSD License. |
|
2 | # Distributed under the terms of the Modified BSD License. | |
3 |
|
3 | |||
4 | import os |
|
4 | import os | |
5 | import sys |
|
5 | import sys | |
6 | import unittest |
|
6 | import unittest | |
7 | import base64 |
|
7 | import base64 | |
8 |
|
8 | |||
9 | try: |
|
9 | try: | |
10 | from unittest.mock import patch |
|
10 | from unittest.mock import patch | |
11 | except ImportError: |
|
11 | except ImportError: | |
12 | from mock import patch |
|
12 | from mock import patch | |
13 |
|
13 | |||
14 | from IPython.kernel import KernelClient |
|
14 | from IPython.kernel import KernelClient | |
15 | from IPython.terminal.console.interactiveshell import ZMQTerminalInteractiveShell |
|
15 | from IPython.terminal.console.interactiveshell import ZMQTerminalInteractiveShell | |
16 | from IPython.utils.tempdir import TemporaryDirectory |
|
16 | from IPython.utils.tempdir import TemporaryDirectory | |
17 | from IPython.testing.tools import monkeypatch |
|
|||
18 | from IPython.testing.decorators import skip_without |
|
17 | from IPython.testing.decorators import skip_without | |
19 | from IPython.utils.ipstruct import Struct |
|
18 | from IPython.utils.ipstruct import Struct | |
20 |
|
19 | |||
21 |
|
20 | |||
22 | SCRIPT_PATH = os.path.join( |
|
21 | SCRIPT_PATH = os.path.join( | |
23 | os.path.abspath(os.path.dirname(__file__)), 'writetofile.py') |
|
22 | os.path.abspath(os.path.dirname(__file__)), 'writetofile.py') | |
24 |
|
23 | |||
25 |
|
24 | |||
26 | class ZMQTerminalInteractiveShellTestCase(unittest.TestCase): |
|
25 | class ZMQTerminalInteractiveShellTestCase(unittest.TestCase): | |
27 |
|
26 | |||
28 | def setUp(self): |
|
27 | def setUp(self): | |
29 | client = KernelClient() |
|
28 | client = KernelClient() | |
30 | self.shell = ZMQTerminalInteractiveShell(kernel_client=client) |
|
29 | self.shell = ZMQTerminalInteractiveShell(kernel_client=client) | |
31 | self.raw = b'dummy data' |
|
30 | self.raw = b'dummy data' | |
32 | self.mime = 'image/png' |
|
31 | self.mime = 'image/png' | |
33 | self.data = {self.mime: base64.encodestring(self.raw).decode('ascii')} |
|
32 | self.data = {self.mime: base64.encodestring(self.raw).decode('ascii')} | |
34 |
|
33 | |||
35 | def test_no_call_by_default(self): |
|
34 | def test_no_call_by_default(self): | |
36 | def raise_if_called(*args, **kwds): |
|
35 | def raise_if_called(*args, **kwds): | |
37 | assert False |
|
36 | assert False | |
38 |
|
37 | |||
39 | shell = self.shell |
|
38 | shell = self.shell | |
40 | shell.handle_image_PIL = raise_if_called |
|
39 | shell.handle_image_PIL = raise_if_called | |
41 | shell.handle_image_stream = raise_if_called |
|
40 | shell.handle_image_stream = raise_if_called | |
42 | shell.handle_image_tempfile = raise_if_called |
|
41 | shell.handle_image_tempfile = raise_if_called | |
43 | shell.handle_image_callable = raise_if_called |
|
42 | shell.handle_image_callable = raise_if_called | |
44 |
|
43 | |||
45 | shell.handle_image(None, None) # arguments are dummy |
|
44 | shell.handle_image(None, None) # arguments are dummy | |
46 |
|
45 | |||
47 | @skip_without('PIL') |
|
46 | @skip_without('PIL') | |
48 | def test_handle_image_PIL(self): |
|
47 | def test_handle_image_PIL(self): | |
49 | import PIL.Image |
|
48 | import PIL.Image | |
50 |
|
49 | |||
51 | open_called_with = [] |
|
50 | open_called_with = [] | |
52 | show_called_with = [] |
|
51 | show_called_with = [] | |
53 |
|
52 | |||
54 | def fake_open(arg): |
|
53 | def fake_open(arg): | |
55 | open_called_with.append(arg) |
|
54 | open_called_with.append(arg) | |
56 | return Struct(show=lambda: show_called_with.append(None)) |
|
55 | return Struct(show=lambda: show_called_with.append(None)) | |
57 |
|
56 | |||
58 | with patch.object(PIL.Image, 'open', fake_open): |
|
57 | with patch.object(PIL.Image, 'open', fake_open): | |
59 | self.shell.handle_image_PIL(self.data, self.mime) |
|
58 | self.shell.handle_image_PIL(self.data, self.mime) | |
60 |
|
59 | |||
61 | self.assertEqual(len(open_called_with), 1) |
|
60 | self.assertEqual(len(open_called_with), 1) | |
62 | self.assertEqual(len(show_called_with), 1) |
|
61 | self.assertEqual(len(show_called_with), 1) | |
63 | self.assertEqual(open_called_with[0].getvalue(), self.raw) |
|
62 | self.assertEqual(open_called_with[0].getvalue(), self.raw) | |
64 |
|
63 | |||
65 | def check_handler_with_file(self, inpath, handler): |
|
64 | def check_handler_with_file(self, inpath, handler): | |
66 | shell = self.shell |
|
65 | shell = self.shell | |
67 | configname = '{0}_image_handler'.format(handler) |
|
66 | configname = '{0}_image_handler'.format(handler) | |
68 | funcname = 'handle_image_{0}'.format(handler) |
|
67 | funcname = 'handle_image_{0}'.format(handler) | |
69 |
|
68 | |||
70 | assert hasattr(shell, configname) |
|
69 | assert hasattr(shell, configname) | |
71 | assert hasattr(shell, funcname) |
|
70 | assert hasattr(shell, funcname) | |
72 |
|
71 | |||
73 | with TemporaryDirectory() as tmpdir: |
|
72 | with TemporaryDirectory() as tmpdir: | |
74 | outpath = os.path.join(tmpdir, 'data') |
|
73 | outpath = os.path.join(tmpdir, 'data') | |
75 | cmd = [sys.executable, SCRIPT_PATH, inpath, outpath] |
|
74 | cmd = [sys.executable, SCRIPT_PATH, inpath, outpath] | |
76 | setattr(shell, configname, cmd) |
|
75 | setattr(shell, configname, cmd) | |
77 | getattr(shell, funcname)(self.data, self.mime) |
|
76 | getattr(shell, funcname)(self.data, self.mime) | |
78 | # cmd is called and file is closed. So it's safe to open now. |
|
77 | # cmd is called and file is closed. So it's safe to open now. | |
79 | with open(outpath, 'rb') as file: |
|
78 | with open(outpath, 'rb') as file: | |
80 | transferred = file.read() |
|
79 | transferred = file.read() | |
81 |
|
80 | |||
82 | self.assertEqual(transferred, self.raw) |
|
81 | self.assertEqual(transferred, self.raw) | |
83 |
|
82 | |||
84 | def test_handle_image_stream(self): |
|
83 | def test_handle_image_stream(self): | |
85 | self.check_handler_with_file('-', 'stream') |
|
84 | self.check_handler_with_file('-', 'stream') | |
86 |
|
85 | |||
87 | def test_handle_image_tempfile(self): |
|
86 | def test_handle_image_tempfile(self): | |
88 | self.check_handler_with_file('{file}', 'tempfile') |
|
87 | self.check_handler_with_file('{file}', 'tempfile') | |
89 |
|
88 | |||
90 | def test_handle_image_callable(self): |
|
89 | def test_handle_image_callable(self): | |
91 | called_with = [] |
|
90 | called_with = [] | |
92 | self.shell.callable_image_handler = called_with.append |
|
91 | self.shell.callable_image_handler = called_with.append | |
93 | self.shell.handle_image_callable(self.data, self.mime) |
|
92 | self.shell.handle_image_callable(self.data, self.mime) | |
94 | self.assertEqual(len(called_with), 1) |
|
93 | self.assertEqual(len(called_with), 1) | |
95 | assert called_with[0] is self.data |
|
94 | assert called_with[0] is self.data |
@@ -1,39 +1,39 b'' | |||||
1 | """Tests for notebook.py""" |
|
1 | """Tests for notebook.py""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | import json |
|
6 | import json | |
7 |
|
7 | |||
8 | from .base import ExportersTestsBase |
|
8 | from .base import ExportersTestsBase | |
9 | from ..notebook import NotebookExporter |
|
9 | from ..notebook import NotebookExporter | |
10 |
|
10 | |||
11 | from IPython.nbformat import validate |
|
11 | from IPython.nbformat import validate | |
12 |
from |
|
12 | from jupyter_nbconvert.tests.base import assert_big_text_equal | |
13 |
|
13 | |||
14 | class TestNotebookExporter(ExportersTestsBase): |
|
14 | class TestNotebookExporter(ExportersTestsBase): | |
15 | """Contains test functions for notebook.py""" |
|
15 | """Contains test functions for notebook.py""" | |
16 |
|
16 | |||
17 | exporter_class = NotebookExporter |
|
17 | exporter_class = NotebookExporter | |
18 |
|
18 | |||
19 | def test_export(self): |
|
19 | def test_export(self): | |
20 | """ |
|
20 | """ | |
21 | Does the NotebookExporter return the file unchanged? |
|
21 | Does the NotebookExporter return the file unchanged? | |
22 | """ |
|
22 | """ | |
23 | with open(self._get_notebook()) as f: |
|
23 | with open(self._get_notebook()) as f: | |
24 | file_contents = f.read() |
|
24 | file_contents = f.read() | |
25 | (output, resources) = self.exporter_class().from_filename(self._get_notebook()) |
|
25 | (output, resources) = self.exporter_class().from_filename(self._get_notebook()) | |
26 | assert len(output) > 0 |
|
26 | assert len(output) > 0 | |
27 | assert_big_text_equal(output.strip(), file_contents.strip()) |
|
27 | assert_big_text_equal(output.strip(), file_contents.strip()) | |
28 |
|
28 | |||
29 | def test_downgrade_3(self): |
|
29 | def test_downgrade_3(self): | |
30 | exporter = self.exporter_class(nbformat_version=3) |
|
30 | exporter = self.exporter_class(nbformat_version=3) | |
31 | (output, resources) = exporter.from_filename(self._get_notebook()) |
|
31 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
32 | nb = json.loads(output) |
|
32 | nb = json.loads(output) | |
33 | validate(nb) |
|
33 | validate(nb) | |
34 |
|
34 | |||
35 | def test_downgrade_2(self): |
|
35 | def test_downgrade_2(self): | |
36 | exporter = self.exporter_class(nbformat_version=2) |
|
36 | exporter = self.exporter_class(nbformat_version=2) | |
37 | (output, resources) = exporter.from_filename(self._get_notebook()) |
|
37 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
38 | nb = json.loads(output) |
|
38 | nb = json.loads(output) | |
39 | self.assertEqual(nb['nbformat'], 2) |
|
39 | self.assertEqual(nb['nbformat'], 2) |
@@ -1,145 +1,168 b'' | |||||
1 | """Base test class for nbconvert""" |
|
1 | """Base test class for nbconvert""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | import io |
|
6 | import io | |
7 | import os |
|
7 | import os | |
8 | import glob |
|
8 | import glob | |
|
9 | import shlex | |||
9 | import shutil |
|
10 | import shutil | |
|
11 | import sys | |||
10 | import unittest |
|
12 | import unittest | |
11 |
|
13 | |||
|
14 | import nose.tools as nt | |||
|
15 | ||||
12 | from IPython.nbformat import v4, write |
|
16 | from IPython.nbformat import v4, write | |
13 | from IPython.utils.tempdir import TemporaryWorkingDirectory |
|
17 | from IPython.utils.tempdir import TemporaryWorkingDirectory | |
14 | from IPython.utils.process import get_output_error_code |
|
18 | from IPython.utils.process import get_output_error_code | |
15 | from IPython.testing.tools import get_ipython_cmd |
|
19 | from IPython.utils.py3compat import string_types | |
16 |
|
||||
17 | # a trailing space allows for simpler concatenation with the other arguments |
|
|||
18 | ipy_cmd = get_ipython_cmd(as_string=True) + " " |
|
|||
19 |
|
||||
20 |
|
20 | |||
21 | class TestsBase(unittest.TestCase): |
|
21 | class TestsBase(unittest.TestCase): | |
22 | """Base tests class. Contains useful fuzzy comparison and nbconvert |
|
22 | """Base tests class. Contains useful fuzzy comparison and nbconvert | |
23 | functions.""" |
|
23 | functions.""" | |
24 |
|
24 | |||
25 |
|
25 | |||
26 | def fuzzy_compare(self, a, b, newlines_are_spaces=True, tabs_are_spaces=True, |
|
26 | def fuzzy_compare(self, a, b, newlines_are_spaces=True, tabs_are_spaces=True, | |
27 | fuzzy_spacing=True, ignore_spaces=False, |
|
27 | fuzzy_spacing=True, ignore_spaces=False, | |
28 | ignore_newlines=False, case_sensitive=False, leave_padding=False): |
|
28 | ignore_newlines=False, case_sensitive=False, leave_padding=False): | |
29 | """ |
|
29 | """ | |
30 | Performs a fuzzy comparison of two strings. A fuzzy comparison is a |
|
30 | Performs a fuzzy comparison of two strings. A fuzzy comparison is a | |
31 | comparison that ignores insignificant differences in the two comparands. |
|
31 | comparison that ignores insignificant differences in the two comparands. | |
32 | The significance of certain differences can be specified via the keyword |
|
32 | The significance of certain differences can be specified via the keyword | |
33 | parameters of this method. |
|
33 | parameters of this method. | |
34 | """ |
|
34 | """ | |
35 |
|
35 | |||
36 | if not leave_padding: |
|
36 | if not leave_padding: | |
37 | a = a.strip() |
|
37 | a = a.strip() | |
38 | b = b.strip() |
|
38 | b = b.strip() | |
39 |
|
39 | |||
40 | if ignore_newlines: |
|
40 | if ignore_newlines: | |
41 | a = a.replace('\n', '') |
|
41 | a = a.replace('\n', '') | |
42 | b = b.replace('\n', '') |
|
42 | b = b.replace('\n', '') | |
43 |
|
43 | |||
44 | if newlines_are_spaces: |
|
44 | if newlines_are_spaces: | |
45 | a = a.replace('\n', ' ') |
|
45 | a = a.replace('\n', ' ') | |
46 | b = b.replace('\n', ' ') |
|
46 | b = b.replace('\n', ' ') | |
47 |
|
47 | |||
48 | if tabs_are_spaces: |
|
48 | if tabs_are_spaces: | |
49 | a = a.replace('\t', ' ') |
|
49 | a = a.replace('\t', ' ') | |
50 | b = b.replace('\t', ' ') |
|
50 | b = b.replace('\t', ' ') | |
51 |
|
51 | |||
52 | if ignore_spaces: |
|
52 | if ignore_spaces: | |
53 | a = a.replace(' ', '') |
|
53 | a = a.replace(' ', '') | |
54 | b = b.replace(' ', '') |
|
54 | b = b.replace(' ', '') | |
55 |
|
55 | |||
56 | if fuzzy_spacing: |
|
56 | if fuzzy_spacing: | |
57 | a = self.recursive_replace(a, ' ', ' ') |
|
57 | a = self.recursive_replace(a, ' ', ' ') | |
58 | b = self.recursive_replace(b, ' ', ' ') |
|
58 | b = self.recursive_replace(b, ' ', ' ') | |
59 |
|
59 | |||
60 | if not case_sensitive: |
|
60 | if not case_sensitive: | |
61 | a = a.lower() |
|
61 | a = a.lower() | |
62 | b = b.lower() |
|
62 | b = b.lower() | |
63 |
|
63 | |||
64 | self.assertEqual(a, b) |
|
64 | self.assertEqual(a, b) | |
65 |
|
65 | |||
66 |
|
66 | |||
67 | def recursive_replace(self, text, search, replacement): |
|
67 | def recursive_replace(self, text, search, replacement): | |
68 | """ |
|
68 | """ | |
69 | Performs a recursive replacement operation. Replaces all instances |
|
69 | Performs a recursive replacement operation. Replaces all instances | |
70 | of a search string in a text string with a replacement string until |
|
70 | of a search string in a text string with a replacement string until | |
71 | the search string no longer exists. Recursion is needed because the |
|
71 | the search string no longer exists. Recursion is needed because the | |
72 | replacement string may generate additional search strings. |
|
72 | replacement string may generate additional search strings. | |
73 |
|
73 | |||
74 | For example: |
|
74 | For example: | |
75 | Replace "ii" with "i" in the string "Hiiii" yields "Hii" |
|
75 | Replace "ii" with "i" in the string "Hiiii" yields "Hii" | |
76 | Another replacement cds "Hi" (the desired output) |
|
76 | Another replacement cds "Hi" (the desired output) | |
77 |
|
77 | |||
78 | Parameters |
|
78 | Parameters | |
79 | ---------- |
|
79 | ---------- | |
80 | text : string |
|
80 | text : string | |
81 | Text to replace in. |
|
81 | Text to replace in. | |
82 | search : string |
|
82 | search : string | |
83 | String to search for within "text" |
|
83 | String to search for within "text" | |
84 | replacement : string |
|
84 | replacement : string | |
85 | String to replace "search" with |
|
85 | String to replace "search" with | |
86 | """ |
|
86 | """ | |
87 | while search in text: |
|
87 | while search in text: | |
88 | text = text.replace(search, replacement) |
|
88 | text = text.replace(search, replacement) | |
89 | return text |
|
89 | return text | |
90 |
|
90 | |||
91 | def create_temp_cwd(self, copy_filenames=None): |
|
91 | def create_temp_cwd(self, copy_filenames=None): | |
92 | temp_dir = TemporaryWorkingDirectory() |
|
92 | temp_dir = TemporaryWorkingDirectory() | |
93 |
|
93 | |||
94 | #Copy the files if requested. |
|
94 | #Copy the files if requested. | |
95 | if copy_filenames is not None: |
|
95 | if copy_filenames is not None: | |
96 | self.copy_files_to(copy_filenames, dest=temp_dir.name) |
|
96 | self.copy_files_to(copy_filenames, dest=temp_dir.name) | |
97 |
|
97 | |||
98 | #Return directory handler |
|
98 | #Return directory handler | |
99 | return temp_dir |
|
99 | return temp_dir | |
100 |
|
100 | |||
101 | def create_empty_notebook(self, path): |
|
101 | def create_empty_notebook(self, path): | |
102 | nb = v4.new_notebook() |
|
102 | nb = v4.new_notebook() | |
103 | with io.open(path, 'w', encoding='utf-8') as f: |
|
103 | with io.open(path, 'w', encoding='utf-8') as f: | |
104 | write(nb, f, 4) |
|
104 | write(nb, f, 4) | |
105 |
|
105 | |||
106 | def copy_files_to(self, copy_filenames, dest='.'): |
|
106 | def copy_files_to(self, copy_filenames, dest='.'): | |
107 | "Copy test files into the destination directory" |
|
107 | "Copy test files into the destination directory" | |
108 | if not os.path.isdir(dest): |
|
108 | if not os.path.isdir(dest): | |
109 | os.makedirs(dest) |
|
109 | os.makedirs(dest) | |
110 | files_path = self._get_files_path() |
|
110 | files_path = self._get_files_path() | |
111 | for pattern in copy_filenames: |
|
111 | for pattern in copy_filenames: | |
112 | for match in glob.glob(os.path.join(files_path, pattern)): |
|
112 | for match in glob.glob(os.path.join(files_path, pattern)): | |
113 | shutil.copyfile(match, os.path.join(dest, os.path.basename(match))) |
|
113 | shutil.copyfile(match, os.path.join(dest, os.path.basename(match))) | |
114 |
|
114 | |||
115 |
|
115 | |||
116 | def _get_files_path(self): |
|
116 | def _get_files_path(self): | |
117 |
|
117 | |||
118 | #Get the relative path to this module in the IPython directory. |
|
118 | #Get the relative path to this module in the IPython directory. | |
119 | names = self.__module__.split('.')[1:-1] |
|
119 | names = self.__module__.split('.')[1:-1] | |
120 | names.append('files') |
|
120 | names.append('files') | |
121 |
|
121 | |||
122 | #Build a path using the nbconvert directory and the relative path we just |
|
122 | #Build a path using the nbconvert directory and the relative path we just | |
123 | #found. |
|
123 | #found. | |
124 | import jupyter_nbconvert |
|
124 | import jupyter_nbconvert | |
125 | path = os.path.dirname(jupyter_nbconvert.__file__) |
|
125 | path = os.path.dirname(jupyter_nbconvert.__file__) | |
126 | return os.path.join(path, *names) |
|
126 | return os.path.join(path, *names) | |
127 |
|
127 | |||
128 |
|
128 | |||
129 |
def |
|
129 | def nbconvert(self, parameters, ignore_return_code=False): | |
130 | """ |
|
130 | """ | |
131 |
|
|
131 | Run nbconvert a, IPython shell command, listening for both Errors and non-zero | |
132 | return codes. |
|
132 | return codes. | |
133 |
|
133 | |||
134 | Parameters |
|
134 | Parameters | |
135 | ---------- |
|
135 | ---------- | |
136 | parameters : str |
|
136 | parameters : str, list(str) | |
137 | List of parameters to pass to IPython. |
|
137 | List of parameters to pass to IPython. | |
138 | ignore_return_code : optional bool (default False) |
|
138 | ignore_return_code : optional bool (default False) | |
139 | Throw an OSError if the return code |
|
139 | Throw an OSError if the return code | |
140 | """ |
|
140 | """ | |
141 |
|
141 | if isinstance(parameters, string_types): | ||
142 | stdout, stderr, retcode = get_output_error_code(ipy_cmd + parameters) |
|
142 | parameters = shlex.split(parameters) | |
|
143 | cmd = [sys.executable, '-m', 'jupyter_nbconvert'] + parameters | |||
|
144 | stdout, stderr, retcode = get_output_error_code(cmd) | |||
143 | if not (retcode == 0 or ignore_return_code): |
|
145 | if not (retcode == 0 or ignore_return_code): | |
144 | raise OSError(stderr) |
|
146 | raise OSError(stderr) | |
145 | return stdout, stderr |
|
147 | return stdout, stderr | |
|
148 | ||||
|
149 | def assert_big_text_equal(a, b, chunk_size=80): | |||
|
150 | """assert that large strings are equal | |||
|
151 | ||||
|
152 | Zooms in on first chunk that differs, | |||
|
153 | to give better info than vanilla assertEqual for large text blobs. | |||
|
154 | """ | |||
|
155 | for i in range(0, len(a), chunk_size): | |||
|
156 | chunk_a = a[i:i + chunk_size] | |||
|
157 | chunk_b = b[i:i + chunk_size] | |||
|
158 | nt.assert_equal(chunk_a, chunk_b, "[offset: %i]\n%r != \n%r" % ( | |||
|
159 | i, chunk_a, chunk_b)) | |||
|
160 | ||||
|
161 | if len(a) > len(b): | |||
|
162 | nt.fail("Length doesn't match (%i > %i). Extra text:\n%r" % ( | |||
|
163 | len(a), len(b), a[len(b):] | |||
|
164 | )) | |||
|
165 | elif len(a) < len(b): | |||
|
166 | nt.fail("Length doesn't match (%i < %i). Extra text:\n%r" % ( | |||
|
167 | len(a), len(b), b[len(a):] | |||
|
168 | )) |
@@ -1,243 +1,243 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Test NbConvertApp""" |
|
2 | """Test NbConvertApp""" | |
3 |
|
3 | |||
4 | # Copyright (c) IPython Development Team. |
|
4 | # Copyright (c) IPython Development Team. | |
5 | # Distributed under the terms of the Modified BSD License. |
|
5 | # Distributed under the terms of the Modified BSD License. | |
6 |
|
6 | |||
7 | import os |
|
7 | import os | |
8 | import glob |
|
8 | import glob | |
9 | import sys |
|
9 | import sys | |
10 |
|
10 | |||
11 | from .base import TestsBase |
|
11 | from .base import TestsBase | |
12 | from ..postprocessors import PostProcessorBase |
|
12 | from ..postprocessors import PostProcessorBase | |
13 |
|
13 | |||
14 | import IPython.testing.tools as tt |
|
14 | from traitlets.tests.utils import check_help_all_output | |
15 | from IPython.testing import decorators as dec |
|
15 | from IPython.testing import decorators as dec | |
16 |
|
16 | |||
17 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
18 | # Classes and functions |
|
18 | # Classes and functions | |
19 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
20 |
|
20 | |||
21 | class DummyPost(PostProcessorBase): |
|
21 | class DummyPost(PostProcessorBase): | |
22 | def postprocess(self, filename): |
|
22 | def postprocess(self, filename): | |
23 | print("Dummy:%s" % filename) |
|
23 | print("Dummy:%s" % filename) | |
24 |
|
24 | |||
25 | class TestNbConvertApp(TestsBase): |
|
25 | class TestNbConvertApp(TestsBase): | |
26 | """Collection of NbConvertApp tests""" |
|
26 | """Collection of NbConvertApp tests""" | |
27 |
|
27 | |||
28 |
|
28 | |||
29 | def test_notebook_help(self): |
|
29 | def test_notebook_help(self): | |
30 | """Will help show if no notebooks are specified?""" |
|
30 | """Will help show if no notebooks are specified?""" | |
31 | with self.create_temp_cwd(): |
|
31 | with self.create_temp_cwd(): | |
32 |
out, err = self. |
|
32 | out, err = self.nbconvert('--log-level 0', ignore_return_code=True) | |
33 | self.assertIn("see '--help-all'", out) |
|
33 | self.assertIn("see '--help-all'", out) | |
34 |
|
34 | |||
35 | def test_help_output(self): |
|
35 | def test_help_output(self): | |
36 | """ipython nbconvert --help-all works""" |
|
36 | """ipython nbconvert --help-all works""" | |
37 |
|
|
37 | check_help_all_output('jupyter_nbconvert') | |
38 |
|
38 | |||
39 | def test_glob(self): |
|
39 | def test_glob(self): | |
40 | """ |
|
40 | """ | |
41 | Do search patterns work for notebook names? |
|
41 | Do search patterns work for notebook names? | |
42 | """ |
|
42 | """ | |
43 | with self.create_temp_cwd(['notebook*.ipynb']): |
|
43 | with self.create_temp_cwd(['notebook*.ipynb']): | |
44 |
self. |
|
44 | self.nbconvert('--to python *.ipynb --log-level 0') | |
45 | assert os.path.isfile('notebook1.py') |
|
45 | assert os.path.isfile('notebook1.py') | |
46 | assert os.path.isfile('notebook2.py') |
|
46 | assert os.path.isfile('notebook2.py') | |
47 |
|
47 | |||
48 |
|
48 | |||
49 | def test_glob_subdir(self): |
|
49 | def test_glob_subdir(self): | |
50 | """ |
|
50 | """ | |
51 | Do search patterns work for subdirectory notebook names? |
|
51 | Do search patterns work for subdirectory notebook names? | |
52 | """ |
|
52 | """ | |
53 | with self.create_temp_cwd(): |
|
53 | with self.create_temp_cwd(): | |
54 | self.copy_files_to(['notebook*.ipynb'], 'subdir/') |
|
54 | self.copy_files_to(['notebook*.ipynb'], 'subdir/') | |
55 |
self. |
|
55 | self.nbconvert('--to python --log-level 0 ' + | |
56 | os.path.join('subdir', '*.ipynb')) |
|
56 | os.path.join('subdir', '*.ipynb')) | |
57 | assert os.path.isfile('notebook1.py') |
|
57 | assert os.path.isfile('notebook1.py') | |
58 | assert os.path.isfile('notebook2.py') |
|
58 | assert os.path.isfile('notebook2.py') | |
59 |
|
59 | |||
60 |
|
60 | |||
61 | def test_explicit(self): |
|
61 | def test_explicit(self): | |
62 | """ |
|
62 | """ | |
63 | Do explicit notebook names work? |
|
63 | Do explicit notebook names work? | |
64 | """ |
|
64 | """ | |
65 | with self.create_temp_cwd(['notebook*.ipynb']): |
|
65 | with self.create_temp_cwd(['notebook*.ipynb']): | |
66 |
self. |
|
66 | self.nbconvert('--log-level 0 --to python notebook2') | |
67 | assert not os.path.isfile('notebook1.py') |
|
67 | assert not os.path.isfile('notebook1.py') | |
68 | assert os.path.isfile('notebook2.py') |
|
68 | assert os.path.isfile('notebook2.py') | |
69 |
|
69 | |||
70 |
|
70 | |||
71 | @dec.onlyif_cmds_exist('pdflatex') |
|
71 | @dec.onlyif_cmds_exist('pdflatex') | |
72 | @dec.onlyif_cmds_exist('pandoc') |
|
72 | @dec.onlyif_cmds_exist('pandoc') | |
73 | def test_filename_spaces(self): |
|
73 | def test_filename_spaces(self): | |
74 | """ |
|
74 | """ | |
75 | Generate PDFs with graphics if notebooks have spaces in the name? |
|
75 | Generate PDFs with graphics if notebooks have spaces in the name? | |
76 | """ |
|
76 | """ | |
77 | with self.create_temp_cwd(['notebook2.ipynb']): |
|
77 | with self.create_temp_cwd(['notebook2.ipynb']): | |
78 | os.rename('notebook2.ipynb', 'notebook with spaces.ipynb') |
|
78 | os.rename('notebook2.ipynb', 'notebook with spaces.ipynb') | |
79 |
self. |
|
79 | self.nbconvert('--log-level 0 --to pdf' | |
80 | ' "notebook with spaces"' |
|
80 | ' "notebook with spaces"' | |
81 | ' --PDFExporter.latex_count=1' |
|
81 | ' --PDFExporter.latex_count=1' | |
82 | ' --PDFExporter.verbose=True' |
|
82 | ' --PDFExporter.verbose=True' | |
83 | ) |
|
83 | ) | |
84 | assert os.path.isfile('notebook with spaces.pdf') |
|
84 | assert os.path.isfile('notebook with spaces.pdf') | |
85 |
|
85 | |||
86 | def test_post_processor(self): |
|
86 | def test_post_processor(self): | |
87 | """Do post processors work?""" |
|
87 | """Do post processors work?""" | |
88 | with self.create_temp_cwd(['notebook1.ipynb']): |
|
88 | with self.create_temp_cwd(['notebook1.ipynb']): | |
89 |
out, err = self. |
|
89 | out, err = self.nbconvert('--log-level 0 --to python notebook1 ' | |
90 | '--post jupyter_nbconvert.tests.test_nbconvertapp.DummyPost') |
|
90 | '--post jupyter_nbconvert.tests.test_nbconvertapp.DummyPost') | |
91 | self.assertIn('Dummy:notebook1.py', out) |
|
91 | self.assertIn('Dummy:notebook1.py', out) | |
92 |
|
92 | |||
93 | @dec.onlyif_cmds_exist('pandoc') |
|
93 | @dec.onlyif_cmds_exist('pandoc') | |
94 | def test_spurious_cr(self): |
|
94 | def test_spurious_cr(self): | |
95 | """Check for extra CR characters""" |
|
95 | """Check for extra CR characters""" | |
96 | with self.create_temp_cwd(['notebook2.ipynb']): |
|
96 | with self.create_temp_cwd(['notebook2.ipynb']): | |
97 |
self. |
|
97 | self.nbconvert('--log-level 0 --to latex notebook2') | |
98 | assert os.path.isfile('notebook2.tex') |
|
98 | assert os.path.isfile('notebook2.tex') | |
99 | with open('notebook2.tex') as f: |
|
99 | with open('notebook2.tex') as f: | |
100 | tex = f.read() |
|
100 | tex = f.read() | |
101 |
self. |
|
101 | self.nbconvert('--log-level 0 --to html notebook2') | |
102 | assert os.path.isfile('notebook2.html') |
|
102 | assert os.path.isfile('notebook2.html') | |
103 | with open('notebook2.html') as f: |
|
103 | with open('notebook2.html') as f: | |
104 | html = f.read() |
|
104 | html = f.read() | |
105 | self.assertEqual(tex.count('\r'), tex.count('\r\n')) |
|
105 | self.assertEqual(tex.count('\r'), tex.count('\r\n')) | |
106 | self.assertEqual(html.count('\r'), html.count('\r\n')) |
|
106 | self.assertEqual(html.count('\r'), html.count('\r\n')) | |
107 |
|
107 | |||
108 | @dec.onlyif_cmds_exist('pandoc') |
|
108 | @dec.onlyif_cmds_exist('pandoc') | |
109 | def test_png_base64_html_ok(self): |
|
109 | def test_png_base64_html_ok(self): | |
110 | """Is embedded png data well formed in HTML?""" |
|
110 | """Is embedded png data well formed in HTML?""" | |
111 | with self.create_temp_cwd(['notebook2.ipynb']): |
|
111 | with self.create_temp_cwd(['notebook2.ipynb']): | |
112 |
self. |
|
112 | self.nbconvert('--log-level 0 --to HTML ' | |
113 | 'notebook2.ipynb --template full') |
|
113 | 'notebook2.ipynb --template full') | |
114 | assert os.path.isfile('notebook2.html') |
|
114 | assert os.path.isfile('notebook2.html') | |
115 | with open('notebook2.html') as f: |
|
115 | with open('notebook2.html') as f: | |
116 | assert "data:image/png;base64,b'" not in f.read() |
|
116 | assert "data:image/png;base64,b'" not in f.read() | |
117 |
|
117 | |||
118 | @dec.onlyif_cmds_exist('pandoc') |
|
118 | @dec.onlyif_cmds_exist('pandoc') | |
119 | def test_template(self): |
|
119 | def test_template(self): | |
120 | """ |
|
120 | """ | |
121 | Do export templates work? |
|
121 | Do export templates work? | |
122 | """ |
|
122 | """ | |
123 | with self.create_temp_cwd(['notebook2.ipynb']): |
|
123 | with self.create_temp_cwd(['notebook2.ipynb']): | |
124 |
self. |
|
124 | self.nbconvert('--log-level 0 --to slides ' | |
125 | 'notebook2.ipynb') |
|
125 | 'notebook2.ipynb') | |
126 | assert os.path.isfile('notebook2.slides.html') |
|
126 | assert os.path.isfile('notebook2.slides.html') | |
127 | with open('notebook2.slides.html') as f: |
|
127 | with open('notebook2.slides.html') as f: | |
128 | assert '/reveal.css' in f.read() |
|
128 | assert '/reveal.css' in f.read() | |
129 |
|
129 | |||
130 | def test_output_ext(self): |
|
130 | def test_output_ext(self): | |
131 | """test --output=outputfile[.ext]""" |
|
131 | """test --output=outputfile[.ext]""" | |
132 | with self.create_temp_cwd(['notebook1.ipynb']): |
|
132 | with self.create_temp_cwd(['notebook1.ipynb']): | |
133 |
self. |
|
133 | self.nbconvert('--log-level 0 --to python ' | |
134 | 'notebook1.ipynb --output nb.py') |
|
134 | 'notebook1.ipynb --output nb.py') | |
135 | assert os.path.exists('nb.py') |
|
135 | assert os.path.exists('nb.py') | |
136 |
|
136 | |||
137 |
self. |
|
137 | self.nbconvert('--log-level 0 --to python ' | |
138 | 'notebook1.ipynb --output nb2') |
|
138 | 'notebook1.ipynb --output nb2') | |
139 | assert os.path.exists('nb2.py') |
|
139 | assert os.path.exists('nb2.py') | |
140 |
|
140 | |||
141 | def test_glob_explicit(self): |
|
141 | def test_glob_explicit(self): | |
142 | """ |
|
142 | """ | |
143 | Can a search pattern be used along with matching explicit notebook names? |
|
143 | Can a search pattern be used along with matching explicit notebook names? | |
144 | """ |
|
144 | """ | |
145 | with self.create_temp_cwd(['notebook*.ipynb']): |
|
145 | with self.create_temp_cwd(['notebook*.ipynb']): | |
146 |
self. |
|
146 | self.nbconvert('--log-level 0 --to python ' | |
147 | '*.ipynb notebook1.ipynb notebook2.ipynb') |
|
147 | '*.ipynb notebook1.ipynb notebook2.ipynb') | |
148 | assert os.path.isfile('notebook1.py') |
|
148 | assert os.path.isfile('notebook1.py') | |
149 | assert os.path.isfile('notebook2.py') |
|
149 | assert os.path.isfile('notebook2.py') | |
150 |
|
150 | |||
151 |
|
151 | |||
152 | def test_explicit_glob(self): |
|
152 | def test_explicit_glob(self): | |
153 | """ |
|
153 | """ | |
154 | Can explicit notebook names be used and then a matching search pattern? |
|
154 | Can explicit notebook names be used and then a matching search pattern? | |
155 | """ |
|
155 | """ | |
156 | with self.create_temp_cwd(['notebook*.ipynb']): |
|
156 | with self.create_temp_cwd(['notebook*.ipynb']): | |
157 |
self. |
|
157 | self.nbconvert('--log-level 0 --to=python ' | |
158 | 'notebook1.ipynb notebook2.ipynb *.ipynb') |
|
158 | 'notebook1.ipynb notebook2.ipynb *.ipynb') | |
159 | assert os.path.isfile('notebook1.py') |
|
159 | assert os.path.isfile('notebook1.py') | |
160 | assert os.path.isfile('notebook2.py') |
|
160 | assert os.path.isfile('notebook2.py') | |
161 |
|
161 | |||
162 |
|
162 | |||
163 | def test_default_config(self): |
|
163 | def test_default_config(self): | |
164 | """ |
|
164 | """ | |
165 | Does the default config work? |
|
165 | Does the default config work? | |
166 | """ |
|
166 | """ | |
167 | with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']): |
|
167 | with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']): | |
168 |
self. |
|
168 | self.nbconvert('--log-level 0') | |
169 | assert os.path.isfile('notebook1.py') |
|
169 | assert os.path.isfile('notebook1.py') | |
170 | assert not os.path.isfile('notebook2.py') |
|
170 | assert not os.path.isfile('notebook2.py') | |
171 |
|
171 | |||
172 |
|
172 | |||
173 | def test_override_config(self): |
|
173 | def test_override_config(self): | |
174 | """ |
|
174 | """ | |
175 | Can the default config be overriden? |
|
175 | Can the default config be overriden? | |
176 | """ |
|
176 | """ | |
177 | with self.create_temp_cwd(['notebook*.ipynb', |
|
177 | with self.create_temp_cwd(['notebook*.ipynb', | |
178 | 'ipython_nbconvert_config.py', |
|
178 | 'ipython_nbconvert_config.py', | |
179 | 'override.py']): |
|
179 | 'override.py']): | |
180 |
self. |
|
180 | self.nbconvert('--log-level 0 --config="override.py"') | |
181 | assert not os.path.isfile('notebook1.py') |
|
181 | assert not os.path.isfile('notebook1.py') | |
182 | assert os.path.isfile('notebook2.py') |
|
182 | assert os.path.isfile('notebook2.py') | |
183 |
|
183 | |||
184 | def test_accents_in_filename(self): |
|
184 | def test_accents_in_filename(self): | |
185 | """ |
|
185 | """ | |
186 | Can notebook names include accents? |
|
186 | Can notebook names include accents? | |
187 | """ |
|
187 | """ | |
188 | with self.create_temp_cwd(): |
|
188 | with self.create_temp_cwd(): | |
189 | self.create_empty_notebook(u'nb1_análisis.ipynb') |
|
189 | self.create_empty_notebook(u'nb1_análisis.ipynb') | |
190 |
self. |
|
190 | self.nbconvert('--log-level 0 --to python nb1_*') | |
191 | assert os.path.isfile(u'nb1_análisis.py') |
|
191 | assert os.path.isfile(u'nb1_análisis.py') | |
192 |
|
192 | |||
193 | @dec.onlyif_cmds_exist('pdflatex', 'pandoc') |
|
193 | @dec.onlyif_cmds_exist('pdflatex', 'pandoc') | |
194 | def test_filename_accent_pdf(self): |
|
194 | def test_filename_accent_pdf(self): | |
195 | """ |
|
195 | """ | |
196 | Generate PDFs if notebooks have an accent in their name? |
|
196 | Generate PDFs if notebooks have an accent in their name? | |
197 | """ |
|
197 | """ | |
198 | with self.create_temp_cwd(): |
|
198 | with self.create_temp_cwd(): | |
199 | self.create_empty_notebook(u'nb1_análisis.ipynb') |
|
199 | self.create_empty_notebook(u'nb1_análisis.ipynb') | |
200 |
self. |
|
200 | self.nbconvert('--log-level 0 --to pdf "nb1_*"' | |
201 | ' --PDFExporter.latex_count=1' |
|
201 | ' --PDFExporter.latex_count=1' | |
202 | ' --PDFExporter.verbose=True') |
|
202 | ' --PDFExporter.verbose=True') | |
203 | assert os.path.isfile(u'nb1_análisis.pdf') |
|
203 | assert os.path.isfile(u'nb1_análisis.pdf') | |
204 |
|
204 | |||
205 | def test_cwd_plugin(self): |
|
205 | def test_cwd_plugin(self): | |
206 | """ |
|
206 | """ | |
207 | Verify that an extension in the cwd can be imported. |
|
207 | Verify that an extension in the cwd can be imported. | |
208 | """ |
|
208 | """ | |
209 | with self.create_temp_cwd(['hello.py']): |
|
209 | with self.create_temp_cwd(['hello.py']): | |
210 | self.create_empty_notebook(u'empty.ipynb') |
|
210 | self.create_empty_notebook(u'empty.ipynb') | |
211 |
self. |
|
211 | self.nbconvert('empty --to html --NbConvertApp.writer_class=\'hello.HelloWriter\'') | |
212 | assert os.path.isfile(u'hello.txt') |
|
212 | assert os.path.isfile(u'hello.txt') | |
213 |
|
213 | |||
214 | def test_output_suffix(self): |
|
214 | def test_output_suffix(self): | |
215 | """ |
|
215 | """ | |
216 | Verify that the output suffix is applied |
|
216 | Verify that the output suffix is applied | |
217 | """ |
|
217 | """ | |
218 | with self.create_temp_cwd(): |
|
218 | with self.create_temp_cwd(): | |
219 | self.create_empty_notebook('empty.ipynb') |
|
219 | self.create_empty_notebook('empty.ipynb') | |
220 |
self. |
|
220 | self.nbconvert('empty.ipynb --to notebook') | |
221 | assert os.path.isfile('empty.nbconvert.ipynb') |
|
221 | assert os.path.isfile('empty.nbconvert.ipynb') | |
222 |
|
222 | |||
223 | def test_different_build_dir(self): |
|
223 | def test_different_build_dir(self): | |
224 | """ |
|
224 | """ | |
225 | Verify that the output suffix is not applied |
|
225 | Verify that the output suffix is not applied | |
226 | """ |
|
226 | """ | |
227 | with self.create_temp_cwd(): |
|
227 | with self.create_temp_cwd(): | |
228 | self.create_empty_notebook('empty.ipynb') |
|
228 | self.create_empty_notebook('empty.ipynb') | |
229 | os.mkdir('output') |
|
229 | os.mkdir('output') | |
230 |
self. |
|
230 | self.nbconvert( | |
231 |
' |
|
231 | 'empty.ipynb --to notebook ' | |
232 | '--FilesWriter.build_directory=output') |
|
232 | '--FilesWriter.build_directory=output') | |
233 | assert os.path.isfile('output/empty.ipynb') |
|
233 | assert os.path.isfile('output/empty.ipynb') | |
234 |
|
234 | |||
235 | def test_inplace(self): |
|
235 | def test_inplace(self): | |
236 | """ |
|
236 | """ | |
237 | Verify that the notebook is converted in place |
|
237 | Verify that the notebook is converted in place | |
238 | """ |
|
238 | """ | |
239 | with self.create_temp_cwd(): |
|
239 | with self.create_temp_cwd(): | |
240 | self.create_empty_notebook('empty.ipynb') |
|
240 | self.create_empty_notebook('empty.ipynb') | |
241 |
self. |
|
241 | self.nbconvert('empty.ipynb --to notebook --inplace') | |
242 | assert os.path.isfile('empty.ipynb') |
|
242 | assert os.path.isfile('empty.ipynb') | |
243 | assert not os.path.isfile('empty.nbconvert.ipynb') |
|
243 | assert not os.path.isfile('empty.nbconvert.ipynb') |
@@ -1,27 +1,15 b'' | |||||
1 | """Test QtConsoleApp""" |
|
1 | """Test QtConsoleApp""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) Jupyter Development Team. | |
4 | # Copyright (C) 2013 The IPython Development Team |
|
4 | # Distributed under the terms of the Modified BSD License. | |
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 |
|
5 | |||
14 | import nose.tools as nt |
|
6 | import nose.tools as nt | |
15 |
|
7 | |||
16 | import IPython.testing.tools as tt |
|
8 | from traitlets.tests.utils import check_help_all_output | |
17 | from IPython.testing.decorators import skip_if_no_x11 |
|
9 | from IPython.testing.decorators import skip_if_no_x11 | |
18 |
|
10 | |||
19 | #----------------------------------------------------------------------------- |
|
|||
20 | # Test functions |
|
|||
21 | #----------------------------------------------------------------------------- |
|
|||
22 |
|
||||
23 | @skip_if_no_x11 |
|
11 | @skip_if_no_x11 | |
24 | def test_help_output(): |
|
12 | def test_help_output(): | |
25 |
""" |
|
13 | """jupyter qtconsole --help-all works""" | |
26 |
|
|
14 | check_help_all_output('jupyter_qtconsole') | |
27 |
|
15 |
General Comments 0
You need to be logged in to leave comments.
Login now