##// END OF EJS Templates
skip numpy autoreload test if numpy not installed
sleeping -
Show More
@@ -1,595 +1,597 b''
1 """Tests for autoreload extension.
1 """Tests for autoreload extension.
2 """
2 """
3 # -----------------------------------------------------------------------------
3 # -----------------------------------------------------------------------------
4 # Copyright (c) 2012 IPython Development Team.
4 # Copyright (c) 2012 IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 # -----------------------------------------------------------------------------
9 # -----------------------------------------------------------------------------
10
10
11 # -----------------------------------------------------------------------------
11 # -----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 # -----------------------------------------------------------------------------
13 # -----------------------------------------------------------------------------
14
14
15 import os
15 import os
16 import platform
16 import platform
17 import pytest
17 import pytest
18 import sys
18 import sys
19 import tempfile
19 import tempfile
20 import textwrap
20 import textwrap
21 import shutil
21 import shutil
22 import random
22 import random
23 import time
23 import time
24 from io import StringIO
24 from io import StringIO
25
25
26 import IPython.testing.tools as tt
26 import IPython.testing.tools as tt
27
27
28 from unittest import TestCase
28 from unittest import TestCase
29
29
30 from IPython.extensions.autoreload import AutoreloadMagics
30 from IPython.extensions.autoreload import AutoreloadMagics
31 from IPython.core.events import EventManager, pre_run_cell
31 from IPython.core.events import EventManager, pre_run_cell
32 from IPython.testing.decorators import skipif_not_numpy
32
33
33 if platform.python_implementation() == "PyPy":
34 if platform.python_implementation() == "PyPy":
34 pytest.skip(
35 pytest.skip(
35 "Current autoreload implementation is extremly slow on PyPy",
36 "Current autoreload implementation is extremly slow on PyPy",
36 allow_module_level=True,
37 allow_module_level=True,
37 )
38 )
38
39
39 # -----------------------------------------------------------------------------
40 # -----------------------------------------------------------------------------
40 # Test fixture
41 # Test fixture
41 # -----------------------------------------------------------------------------
42 # -----------------------------------------------------------------------------
42
43
43 noop = lambda *a, **kw: None
44 noop = lambda *a, **kw: None
44
45
45
46
46 class FakeShell:
47 class FakeShell:
47 def __init__(self):
48 def __init__(self):
48 self.ns = {}
49 self.ns = {}
49 self.user_ns = self.ns
50 self.user_ns = self.ns
50 self.user_ns_hidden = {}
51 self.user_ns_hidden = {}
51 self.events = EventManager(self, {"pre_run_cell", pre_run_cell})
52 self.events = EventManager(self, {"pre_run_cell", pre_run_cell})
52 self.auto_magics = AutoreloadMagics(shell=self)
53 self.auto_magics = AutoreloadMagics(shell=self)
53 self.events.register("pre_run_cell", self.auto_magics.pre_run_cell)
54 self.events.register("pre_run_cell", self.auto_magics.pre_run_cell)
54
55
55 register_magics = set_hook = noop
56 register_magics = set_hook = noop
56
57
57 def run_code(self, code):
58 def run_code(self, code):
58 self.events.trigger("pre_run_cell")
59 self.events.trigger("pre_run_cell")
59 exec(code, self.user_ns)
60 exec(code, self.user_ns)
60 self.auto_magics.post_execute_hook()
61 self.auto_magics.post_execute_hook()
61
62
62 def push(self, items):
63 def push(self, items):
63 self.ns.update(items)
64 self.ns.update(items)
64
65
65 def magic_autoreload(self, parameter):
66 def magic_autoreload(self, parameter):
66 self.auto_magics.autoreload(parameter)
67 self.auto_magics.autoreload(parameter)
67
68
68 def magic_aimport(self, parameter, stream=None):
69 def magic_aimport(self, parameter, stream=None):
69 self.auto_magics.aimport(parameter, stream=stream)
70 self.auto_magics.aimport(parameter, stream=stream)
70 self.auto_magics.post_execute_hook()
71 self.auto_magics.post_execute_hook()
71
72
72
73
73 class Fixture(TestCase):
74 class Fixture(TestCase):
74 """Fixture for creating test module files"""
75 """Fixture for creating test module files"""
75
76
76 test_dir = None
77 test_dir = None
77 old_sys_path = None
78 old_sys_path = None
78 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
79 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
79
80
80 def setUp(self):
81 def setUp(self):
81 self.test_dir = tempfile.mkdtemp()
82 self.test_dir = tempfile.mkdtemp()
82 self.old_sys_path = list(sys.path)
83 self.old_sys_path = list(sys.path)
83 sys.path.insert(0, self.test_dir)
84 sys.path.insert(0, self.test_dir)
84 self.shell = FakeShell()
85 self.shell = FakeShell()
85
86
86 def tearDown(self):
87 def tearDown(self):
87 shutil.rmtree(self.test_dir)
88 shutil.rmtree(self.test_dir)
88 sys.path = self.old_sys_path
89 sys.path = self.old_sys_path
89
90
90 self.test_dir = None
91 self.test_dir = None
91 self.old_sys_path = None
92 self.old_sys_path = None
92 self.shell = None
93 self.shell = None
93
94
94 def get_module(self):
95 def get_module(self):
95 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars, 20))
96 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars, 20))
96 if module_name in sys.modules:
97 if module_name in sys.modules:
97 del sys.modules[module_name]
98 del sys.modules[module_name]
98 file_name = os.path.join(self.test_dir, module_name + ".py")
99 file_name = os.path.join(self.test_dir, module_name + ".py")
99 return module_name, file_name
100 return module_name, file_name
100
101
101 def write_file(self, filename, content):
102 def write_file(self, filename, content):
102 """
103 """
103 Write a file, and force a timestamp difference of at least one second
104 Write a file, and force a timestamp difference of at least one second
104
105
105 Notes
106 Notes
106 -----
107 -----
107 Python's .pyc files record the timestamp of their compilation
108 Python's .pyc files record the timestamp of their compilation
108 with a time resolution of one second.
109 with a time resolution of one second.
109
110
110 Therefore, we need to force a timestamp difference between .py
111 Therefore, we need to force a timestamp difference between .py
111 and .pyc, without having the .py file be timestamped in the
112 and .pyc, without having the .py file be timestamped in the
112 future, and without changing the timestamp of the .pyc file
113 future, and without changing the timestamp of the .pyc file
113 (because that is stored in the file). The only reliable way
114 (because that is stored in the file). The only reliable way
114 to achieve this seems to be to sleep.
115 to achieve this seems to be to sleep.
115 """
116 """
116 content = textwrap.dedent(content)
117 content = textwrap.dedent(content)
117 # Sleep one second + eps
118 # Sleep one second + eps
118 time.sleep(1.05)
119 time.sleep(1.05)
119
120
120 # Write
121 # Write
121 with open(filename, "w") as f:
122 with open(filename, "w") as f:
122 f.write(content)
123 f.write(content)
123
124
124 def new_module(self, code):
125 def new_module(self, code):
125 code = textwrap.dedent(code)
126 code = textwrap.dedent(code)
126 mod_name, mod_fn = self.get_module()
127 mod_name, mod_fn = self.get_module()
127 with open(mod_fn, "w") as f:
128 with open(mod_fn, "w") as f:
128 f.write(code)
129 f.write(code)
129 return mod_name, mod_fn
130 return mod_name, mod_fn
130
131
131
132
132 # -----------------------------------------------------------------------------
133 # -----------------------------------------------------------------------------
133 # Test automatic reloading
134 # Test automatic reloading
134 # -----------------------------------------------------------------------------
135 # -----------------------------------------------------------------------------
135
136
136
137
137 def pickle_get_current_class(obj):
138 def pickle_get_current_class(obj):
138 """
139 """
139 Original issue comes from pickle; hence the name.
140 Original issue comes from pickle; hence the name.
140 """
141 """
141 name = obj.__class__.__name__
142 name = obj.__class__.__name__
142 module_name = getattr(obj, "__module__", None)
143 module_name = getattr(obj, "__module__", None)
143 obj2 = sys.modules[module_name]
144 obj2 = sys.modules[module_name]
144 for subpath in name.split("."):
145 for subpath in name.split("."):
145 obj2 = getattr(obj2, subpath)
146 obj2 = getattr(obj2, subpath)
146 return obj2
147 return obj2
147
148
148
149
149 class TestAutoreload(Fixture):
150 class TestAutoreload(Fixture):
150 def test_reload_enums(self):
151 def test_reload_enums(self):
151 mod_name, mod_fn = self.new_module(
152 mod_name, mod_fn = self.new_module(
152 textwrap.dedent(
153 textwrap.dedent(
153 """
154 """
154 from enum import Enum
155 from enum import Enum
155 class MyEnum(Enum):
156 class MyEnum(Enum):
156 A = 'A'
157 A = 'A'
157 B = 'B'
158 B = 'B'
158 """
159 """
159 )
160 )
160 )
161 )
161 self.shell.magic_autoreload("2")
162 self.shell.magic_autoreload("2")
162 self.shell.magic_aimport(mod_name)
163 self.shell.magic_aimport(mod_name)
163 self.write_file(
164 self.write_file(
164 mod_fn,
165 mod_fn,
165 textwrap.dedent(
166 textwrap.dedent(
166 """
167 """
167 from enum import Enum
168 from enum import Enum
168 class MyEnum(Enum):
169 class MyEnum(Enum):
169 A = 'A'
170 A = 'A'
170 B = 'B'
171 B = 'B'
171 C = 'C'
172 C = 'C'
172 """
173 """
173 ),
174 ),
174 )
175 )
175 with tt.AssertNotPrints(
176 with tt.AssertNotPrints(
176 ("[autoreload of %s failed:" % mod_name), channel="stderr"
177 ("[autoreload of %s failed:" % mod_name), channel="stderr"
177 ):
178 ):
178 self.shell.run_code("pass") # trigger another reload
179 self.shell.run_code("pass") # trigger another reload
179
180
180 def test_reload_class_type(self):
181 def test_reload_class_type(self):
181 self.shell.magic_autoreload("2")
182 self.shell.magic_autoreload("2")
182 mod_name, mod_fn = self.new_module(
183 mod_name, mod_fn = self.new_module(
183 """
184 """
184 class Test():
185 class Test():
185 def meth(self):
186 def meth(self):
186 return "old"
187 return "old"
187 """
188 """
188 )
189 )
189 assert "test" not in self.shell.ns
190 assert "test" not in self.shell.ns
190 assert "result" not in self.shell.ns
191 assert "result" not in self.shell.ns
191
192
192 self.shell.run_code("from %s import Test" % mod_name)
193 self.shell.run_code("from %s import Test" % mod_name)
193 self.shell.run_code("test = Test()")
194 self.shell.run_code("test = Test()")
194
195
195 self.write_file(
196 self.write_file(
196 mod_fn,
197 mod_fn,
197 """
198 """
198 class Test():
199 class Test():
199 def meth(self):
200 def meth(self):
200 return "new"
201 return "new"
201 """,
202 """,
202 )
203 )
203
204
204 test_object = self.shell.ns["test"]
205 test_object = self.shell.ns["test"]
205
206
206 # important to trigger autoreload logic !
207 # important to trigger autoreload logic !
207 self.shell.run_code("pass")
208 self.shell.run_code("pass")
208
209
209 test_class = pickle_get_current_class(test_object)
210 test_class = pickle_get_current_class(test_object)
210 assert isinstance(test_object, test_class)
211 assert isinstance(test_object, test_class)
211
212
212 # extra check.
213 # extra check.
213 self.shell.run_code("import pickle")
214 self.shell.run_code("import pickle")
214 self.shell.run_code("p = pickle.dumps(test)")
215 self.shell.run_code("p = pickle.dumps(test)")
215
216
216 def test_reload_class_attributes(self):
217 def test_reload_class_attributes(self):
217 self.shell.magic_autoreload("2")
218 self.shell.magic_autoreload("2")
218 mod_name, mod_fn = self.new_module(
219 mod_name, mod_fn = self.new_module(
219 textwrap.dedent(
220 textwrap.dedent(
220 """
221 """
221 class MyClass:
222 class MyClass:
222
223
223 def __init__(self, a=10):
224 def __init__(self, a=10):
224 self.a = a
225 self.a = a
225 self.b = 22
226 self.b = 22
226 # self.toto = 33
227 # self.toto = 33
227
228
228 def square(self):
229 def square(self):
229 print('compute square')
230 print('compute square')
230 return self.a*self.a
231 return self.a*self.a
231 """
232 """
232 )
233 )
233 )
234 )
234 self.shell.run_code("from %s import MyClass" % mod_name)
235 self.shell.run_code("from %s import MyClass" % mod_name)
235 self.shell.run_code("first = MyClass(5)")
236 self.shell.run_code("first = MyClass(5)")
236 self.shell.run_code("first.square()")
237 self.shell.run_code("first.square()")
237 with self.assertRaises(AttributeError):
238 with self.assertRaises(AttributeError):
238 self.shell.run_code("first.cube()")
239 self.shell.run_code("first.cube()")
239 with self.assertRaises(AttributeError):
240 with self.assertRaises(AttributeError):
240 self.shell.run_code("first.power(5)")
241 self.shell.run_code("first.power(5)")
241 self.shell.run_code("first.b")
242 self.shell.run_code("first.b")
242 with self.assertRaises(AttributeError):
243 with self.assertRaises(AttributeError):
243 self.shell.run_code("first.toto")
244 self.shell.run_code("first.toto")
244
245
245 # remove square, add power
246 # remove square, add power
246
247
247 self.write_file(
248 self.write_file(
248 mod_fn,
249 mod_fn,
249 textwrap.dedent(
250 textwrap.dedent(
250 """
251 """
251 class MyClass:
252 class MyClass:
252
253
253 def __init__(self, a=10):
254 def __init__(self, a=10):
254 self.a = a
255 self.a = a
255 self.b = 11
256 self.b = 11
256
257
257 def power(self, p):
258 def power(self, p):
258 print('compute power '+str(p))
259 print('compute power '+str(p))
259 return self.a**p
260 return self.a**p
260 """
261 """
261 ),
262 ),
262 )
263 )
263
264
264 self.shell.run_code("second = MyClass(5)")
265 self.shell.run_code("second = MyClass(5)")
265
266
266 for object_name in {"first", "second"}:
267 for object_name in {"first", "second"}:
267 self.shell.run_code(f"{object_name}.power(5)")
268 self.shell.run_code(f"{object_name}.power(5)")
268 with self.assertRaises(AttributeError):
269 with self.assertRaises(AttributeError):
269 self.shell.run_code(f"{object_name}.cube()")
270 self.shell.run_code(f"{object_name}.cube()")
270 with self.assertRaises(AttributeError):
271 with self.assertRaises(AttributeError):
271 self.shell.run_code(f"{object_name}.square()")
272 self.shell.run_code(f"{object_name}.square()")
272 self.shell.run_code(f"{object_name}.b")
273 self.shell.run_code(f"{object_name}.b")
273 self.shell.run_code(f"{object_name}.a")
274 self.shell.run_code(f"{object_name}.a")
274 with self.assertRaises(AttributeError):
275 with self.assertRaises(AttributeError):
275 self.shell.run_code(f"{object_name}.toto")
276 self.shell.run_code(f"{object_name}.toto")
276
277
278 @skipif_not_numpy
277 def test_comparing_numpy_structures(self):
279 def test_comparing_numpy_structures(self):
278 self.shell.magic_autoreload("2")
280 self.shell.magic_autoreload("2")
279 mod_name, mod_fn = self.new_module(
281 mod_name, mod_fn = self.new_module(
280 textwrap.dedent(
282 textwrap.dedent(
281 """
283 """
282 import numpy as np
284 import numpy as np
283 class MyClass:
285 class MyClass:
284 a = (np.array((.1, .2)),
286 a = (np.array((.1, .2)),
285 np.array((.2, .3)))
287 np.array((.2, .3)))
286 """
288 """
287 )
289 )
288 )
290 )
289 self.shell.run_code("from %s import MyClass" % mod_name)
291 self.shell.run_code("from %s import MyClass" % mod_name)
290 self.shell.run_code("first = MyClass()")
292 self.shell.run_code("first = MyClass()")
291
293
292 # change property `a`
294 # change property `a`
293 self.write_file(
295 self.write_file(
294 mod_fn,
296 mod_fn,
295 textwrap.dedent(
297 textwrap.dedent(
296 """
298 """
297 import numpy as np
299 import numpy as np
298 class MyClass:
300 class MyClass:
299 a = (np.array((.3, .4)),
301 a = (np.array((.3, .4)),
300 np.array((.5, .6)))
302 np.array((.5, .6)))
301 """
303 """
302 ),
304 ),
303 )
305 )
304
306
305 with tt.AssertNotPrints(
307 with tt.AssertNotPrints(
306 ("[autoreload of %s failed:" % mod_name), channel="stderr"
308 ("[autoreload of %s failed:" % mod_name), channel="stderr"
307 ):
309 ):
308 self.shell.run_code("pass") # trigger another reload
310 self.shell.run_code("pass") # trigger another reload
309
311
310 def test_autoload_newly_added_objects(self):
312 def test_autoload_newly_added_objects(self):
311 self.shell.magic_autoreload("3")
313 self.shell.magic_autoreload("3")
312 mod_code = """
314 mod_code = """
313 def func1(): pass
315 def func1(): pass
314 """
316 """
315 mod_name, mod_fn = self.new_module(textwrap.dedent(mod_code))
317 mod_name, mod_fn = self.new_module(textwrap.dedent(mod_code))
316 self.shell.run_code(f"from {mod_name} import *")
318 self.shell.run_code(f"from {mod_name} import *")
317 self.shell.run_code("func1()")
319 self.shell.run_code("func1()")
318 with self.assertRaises(NameError):
320 with self.assertRaises(NameError):
319 self.shell.run_code("func2()")
321 self.shell.run_code("func2()")
320 with self.assertRaises(NameError):
322 with self.assertRaises(NameError):
321 self.shell.run_code("t = Test()")
323 self.shell.run_code("t = Test()")
322 with self.assertRaises(NameError):
324 with self.assertRaises(NameError):
323 self.shell.run_code("number")
325 self.shell.run_code("number")
324
326
325 # ----------- TEST NEW OBJ LOADED --------------------------
327 # ----------- TEST NEW OBJ LOADED --------------------------
326
328
327 new_code = """
329 new_code = """
328 def func1(): pass
330 def func1(): pass
329 def func2(): pass
331 def func2(): pass
330 class Test: pass
332 class Test: pass
331 number = 0
333 number = 0
332 from enum import Enum
334 from enum import Enum
333 class TestEnum(Enum):
335 class TestEnum(Enum):
334 A = 'a'
336 A = 'a'
335 """
337 """
336 self.write_file(mod_fn, textwrap.dedent(new_code))
338 self.write_file(mod_fn, textwrap.dedent(new_code))
337
339
338 # test function now exists in shell's namespace namespace
340 # test function now exists in shell's namespace namespace
339 self.shell.run_code("func2()")
341 self.shell.run_code("func2()")
340 # test function now exists in module's dict
342 # test function now exists in module's dict
341 self.shell.run_code(f"import sys; sys.modules['{mod_name}'].func2()")
343 self.shell.run_code(f"import sys; sys.modules['{mod_name}'].func2()")
342 # test class now exists
344 # test class now exists
343 self.shell.run_code("t = Test()")
345 self.shell.run_code("t = Test()")
344 # test global built-in var now exists
346 # test global built-in var now exists
345 self.shell.run_code("number")
347 self.shell.run_code("number")
346 # test the enumerations gets loaded successfully
348 # test the enumerations gets loaded successfully
347 self.shell.run_code("TestEnum.A")
349 self.shell.run_code("TestEnum.A")
348
350
349 # ----------- TEST NEW OBJ CAN BE CHANGED --------------------
351 # ----------- TEST NEW OBJ CAN BE CHANGED --------------------
350
352
351 new_code = """
353 new_code = """
352 def func1(): return 'changed'
354 def func1(): return 'changed'
353 def func2(): return 'changed'
355 def func2(): return 'changed'
354 class Test:
356 class Test:
355 def new_func(self):
357 def new_func(self):
356 return 'changed'
358 return 'changed'
357 number = 1
359 number = 1
358 from enum import Enum
360 from enum import Enum
359 class TestEnum(Enum):
361 class TestEnum(Enum):
360 A = 'a'
362 A = 'a'
361 B = 'added'
363 B = 'added'
362 """
364 """
363 self.write_file(mod_fn, textwrap.dedent(new_code))
365 self.write_file(mod_fn, textwrap.dedent(new_code))
364 self.shell.run_code("assert func1() == 'changed'")
366 self.shell.run_code("assert func1() == 'changed'")
365 self.shell.run_code("assert func2() == 'changed'")
367 self.shell.run_code("assert func2() == 'changed'")
366 self.shell.run_code("t = Test(); assert t.new_func() == 'changed'")
368 self.shell.run_code("t = Test(); assert t.new_func() == 'changed'")
367 self.shell.run_code("assert number == 1")
369 self.shell.run_code("assert number == 1")
368 self.shell.run_code("assert TestEnum.B.value == 'added'")
370 self.shell.run_code("assert TestEnum.B.value == 'added'")
369
371
370 # ----------- TEST IMPORT FROM MODULE --------------------------
372 # ----------- TEST IMPORT FROM MODULE --------------------------
371
373
372 new_mod_code = """
374 new_mod_code = """
373 from enum import Enum
375 from enum import Enum
374 class Ext(Enum):
376 class Ext(Enum):
375 A = 'ext'
377 A = 'ext'
376 def ext_func():
378 def ext_func():
377 return 'ext'
379 return 'ext'
378 class ExtTest:
380 class ExtTest:
379 def meth(self):
381 def meth(self):
380 return 'ext'
382 return 'ext'
381 ext_int = 2
383 ext_int = 2
382 """
384 """
383 new_mod_name, new_mod_fn = self.new_module(textwrap.dedent(new_mod_code))
385 new_mod_name, new_mod_fn = self.new_module(textwrap.dedent(new_mod_code))
384 current_mod_code = f"""
386 current_mod_code = f"""
385 from {new_mod_name} import *
387 from {new_mod_name} import *
386 """
388 """
387 self.write_file(mod_fn, textwrap.dedent(current_mod_code))
389 self.write_file(mod_fn, textwrap.dedent(current_mod_code))
388 self.shell.run_code("assert Ext.A.value == 'ext'")
390 self.shell.run_code("assert Ext.A.value == 'ext'")
389 self.shell.run_code("assert ext_func() == 'ext'")
391 self.shell.run_code("assert ext_func() == 'ext'")
390 self.shell.run_code("t = ExtTest(); assert t.meth() == 'ext'")
392 self.shell.run_code("t = ExtTest(); assert t.meth() == 'ext'")
391 self.shell.run_code("assert ext_int == 2")
393 self.shell.run_code("assert ext_int == 2")
392
394
393 def _check_smoketest(self, use_aimport=True):
395 def _check_smoketest(self, use_aimport=True):
394 """
396 """
395 Functional test for the automatic reloader using either
397 Functional test for the automatic reloader using either
396 '%autoreload 1' or '%autoreload 2'
398 '%autoreload 1' or '%autoreload 2'
397 """
399 """
398
400
399 mod_name, mod_fn = self.new_module(
401 mod_name, mod_fn = self.new_module(
400 """
402 """
401 x = 9
403 x = 9
402
404
403 z = 123 # this item will be deleted
405 z = 123 # this item will be deleted
404
406
405 def foo(y):
407 def foo(y):
406 return y + 3
408 return y + 3
407
409
408 class Baz(object):
410 class Baz(object):
409 def __init__(self, x):
411 def __init__(self, x):
410 self.x = x
412 self.x = x
411 def bar(self, y):
413 def bar(self, y):
412 return self.x + y
414 return self.x + y
413 @property
415 @property
414 def quux(self):
416 def quux(self):
415 return 42
417 return 42
416 def zzz(self):
418 def zzz(self):
417 '''This method will be deleted below'''
419 '''This method will be deleted below'''
418 return 99
420 return 99
419
421
420 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
422 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
421 def foo(self):
423 def foo(self):
422 return 1
424 return 1
423 """
425 """
424 )
426 )
425
427
426 #
428 #
427 # Import module, and mark for reloading
429 # Import module, and mark for reloading
428 #
430 #
429 if use_aimport:
431 if use_aimport:
430 self.shell.magic_autoreload("1")
432 self.shell.magic_autoreload("1")
431 self.shell.magic_aimport(mod_name)
433 self.shell.magic_aimport(mod_name)
432 stream = StringIO()
434 stream = StringIO()
433 self.shell.magic_aimport("", stream=stream)
435 self.shell.magic_aimport("", stream=stream)
434 self.assertIn(("Modules to reload:\n%s" % mod_name), stream.getvalue())
436 self.assertIn(("Modules to reload:\n%s" % mod_name), stream.getvalue())
435
437
436 with self.assertRaises(ImportError):
438 with self.assertRaises(ImportError):
437 self.shell.magic_aimport("tmpmod_as318989e89ds")
439 self.shell.magic_aimport("tmpmod_as318989e89ds")
438 else:
440 else:
439 self.shell.magic_autoreload("2")
441 self.shell.magic_autoreload("2")
440 self.shell.run_code("import %s" % mod_name)
442 self.shell.run_code("import %s" % mod_name)
441 stream = StringIO()
443 stream = StringIO()
442 self.shell.magic_aimport("", stream=stream)
444 self.shell.magic_aimport("", stream=stream)
443 self.assertTrue(
445 self.assertTrue(
444 "Modules to reload:\nall-except-skipped" in stream.getvalue()
446 "Modules to reload:\nall-except-skipped" in stream.getvalue()
445 )
447 )
446 self.assertIn(mod_name, self.shell.ns)
448 self.assertIn(mod_name, self.shell.ns)
447
449
448 mod = sys.modules[mod_name]
450 mod = sys.modules[mod_name]
449
451
450 #
452 #
451 # Test module contents
453 # Test module contents
452 #
454 #
453 old_foo = mod.foo
455 old_foo = mod.foo
454 old_obj = mod.Baz(9)
456 old_obj = mod.Baz(9)
455 old_obj2 = mod.Bar()
457 old_obj2 = mod.Bar()
456
458
457 def check_module_contents():
459 def check_module_contents():
458 self.assertEqual(mod.x, 9)
460 self.assertEqual(mod.x, 9)
459 self.assertEqual(mod.z, 123)
461 self.assertEqual(mod.z, 123)
460
462
461 self.assertEqual(old_foo(0), 3)
463 self.assertEqual(old_foo(0), 3)
462 self.assertEqual(mod.foo(0), 3)
464 self.assertEqual(mod.foo(0), 3)
463
465
464 obj = mod.Baz(9)
466 obj = mod.Baz(9)
465 self.assertEqual(old_obj.bar(1), 10)
467 self.assertEqual(old_obj.bar(1), 10)
466 self.assertEqual(obj.bar(1), 10)
468 self.assertEqual(obj.bar(1), 10)
467 self.assertEqual(obj.quux, 42)
469 self.assertEqual(obj.quux, 42)
468 self.assertEqual(obj.zzz(), 99)
470 self.assertEqual(obj.zzz(), 99)
469
471
470 obj2 = mod.Bar()
472 obj2 = mod.Bar()
471 self.assertEqual(old_obj2.foo(), 1)
473 self.assertEqual(old_obj2.foo(), 1)
472 self.assertEqual(obj2.foo(), 1)
474 self.assertEqual(obj2.foo(), 1)
473
475
474 check_module_contents()
476 check_module_contents()
475
477
476 #
478 #
477 # Simulate a failed reload: no reload should occur and exactly
479 # Simulate a failed reload: no reload should occur and exactly
478 # one error message should be printed
480 # one error message should be printed
479 #
481 #
480 self.write_file(
482 self.write_file(
481 mod_fn,
483 mod_fn,
482 """
484 """
483 a syntax error
485 a syntax error
484 """,
486 """,
485 )
487 )
486
488
487 with tt.AssertPrints(
489 with tt.AssertPrints(
488 ("[autoreload of %s failed:" % mod_name), channel="stderr"
490 ("[autoreload of %s failed:" % mod_name), channel="stderr"
489 ):
491 ):
490 self.shell.run_code("pass") # trigger reload
492 self.shell.run_code("pass") # trigger reload
491 with tt.AssertNotPrints(
493 with tt.AssertNotPrints(
492 ("[autoreload of %s failed:" % mod_name), channel="stderr"
494 ("[autoreload of %s failed:" % mod_name), channel="stderr"
493 ):
495 ):
494 self.shell.run_code("pass") # trigger another reload
496 self.shell.run_code("pass") # trigger another reload
495 check_module_contents()
497 check_module_contents()
496
498
497 #
499 #
498 # Rewrite module (this time reload should succeed)
500 # Rewrite module (this time reload should succeed)
499 #
501 #
500 self.write_file(
502 self.write_file(
501 mod_fn,
503 mod_fn,
502 """
504 """
503 x = 10
505 x = 10
504
506
505 def foo(y):
507 def foo(y):
506 return y + 4
508 return y + 4
507
509
508 class Baz(object):
510 class Baz(object):
509 def __init__(self, x):
511 def __init__(self, x):
510 self.x = x
512 self.x = x
511 def bar(self, y):
513 def bar(self, y):
512 return self.x + y + 1
514 return self.x + y + 1
513 @property
515 @property
514 def quux(self):
516 def quux(self):
515 return 43
517 return 43
516
518
517 class Bar: # old-style class
519 class Bar: # old-style class
518 def foo(self):
520 def foo(self):
519 return 2
521 return 2
520 """,
522 """,
521 )
523 )
522
524
523 def check_module_contents():
525 def check_module_contents():
524 self.assertEqual(mod.x, 10)
526 self.assertEqual(mod.x, 10)
525 self.assertFalse(hasattr(mod, "z"))
527 self.assertFalse(hasattr(mod, "z"))
526
528
527 self.assertEqual(old_foo(0), 4) # superreload magic!
529 self.assertEqual(old_foo(0), 4) # superreload magic!
528 self.assertEqual(mod.foo(0), 4)
530 self.assertEqual(mod.foo(0), 4)
529
531
530 obj = mod.Baz(9)
532 obj = mod.Baz(9)
531 self.assertEqual(old_obj.bar(1), 11) # superreload magic!
533 self.assertEqual(old_obj.bar(1), 11) # superreload magic!
532 self.assertEqual(obj.bar(1), 11)
534 self.assertEqual(obj.bar(1), 11)
533
535
534 self.assertEqual(old_obj.quux, 43)
536 self.assertEqual(old_obj.quux, 43)
535 self.assertEqual(obj.quux, 43)
537 self.assertEqual(obj.quux, 43)
536
538
537 self.assertFalse(hasattr(old_obj, "zzz"))
539 self.assertFalse(hasattr(old_obj, "zzz"))
538 self.assertFalse(hasattr(obj, "zzz"))
540 self.assertFalse(hasattr(obj, "zzz"))
539
541
540 obj2 = mod.Bar()
542 obj2 = mod.Bar()
541 self.assertEqual(old_obj2.foo(), 2)
543 self.assertEqual(old_obj2.foo(), 2)
542 self.assertEqual(obj2.foo(), 2)
544 self.assertEqual(obj2.foo(), 2)
543
545
544 self.shell.run_code("pass") # trigger reload
546 self.shell.run_code("pass") # trigger reload
545 check_module_contents()
547 check_module_contents()
546
548
547 #
549 #
548 # Another failure case: deleted file (shouldn't reload)
550 # Another failure case: deleted file (shouldn't reload)
549 #
551 #
550 os.unlink(mod_fn)
552 os.unlink(mod_fn)
551
553
552 self.shell.run_code("pass") # trigger reload
554 self.shell.run_code("pass") # trigger reload
553 check_module_contents()
555 check_module_contents()
554
556
555 #
557 #
556 # Disable autoreload and rewrite module: no reload should occur
558 # Disable autoreload and rewrite module: no reload should occur
557 #
559 #
558 if use_aimport:
560 if use_aimport:
559 self.shell.magic_aimport("-" + mod_name)
561 self.shell.magic_aimport("-" + mod_name)
560 stream = StringIO()
562 stream = StringIO()
561 self.shell.magic_aimport("", stream=stream)
563 self.shell.magic_aimport("", stream=stream)
562 self.assertTrue(("Modules to skip:\n%s" % mod_name) in stream.getvalue())
564 self.assertTrue(("Modules to skip:\n%s" % mod_name) in stream.getvalue())
563
565
564 # This should succeed, although no such module exists
566 # This should succeed, although no such module exists
565 self.shell.magic_aimport("-tmpmod_as318989e89ds")
567 self.shell.magic_aimport("-tmpmod_as318989e89ds")
566 else:
568 else:
567 self.shell.magic_autoreload("0")
569 self.shell.magic_autoreload("0")
568
570
569 self.write_file(
571 self.write_file(
570 mod_fn,
572 mod_fn,
571 """
573 """
572 x = -99
574 x = -99
573 """,
575 """,
574 )
576 )
575
577
576 self.shell.run_code("pass") # trigger reload
578 self.shell.run_code("pass") # trigger reload
577 self.shell.run_code("pass")
579 self.shell.run_code("pass")
578 check_module_contents()
580 check_module_contents()
579
581
580 #
582 #
581 # Re-enable autoreload: reload should now occur
583 # Re-enable autoreload: reload should now occur
582 #
584 #
583 if use_aimport:
585 if use_aimport:
584 self.shell.magic_aimport(mod_name)
586 self.shell.magic_aimport(mod_name)
585 else:
587 else:
586 self.shell.magic_autoreload("")
588 self.shell.magic_autoreload("")
587
589
588 self.shell.run_code("pass") # trigger reload
590 self.shell.run_code("pass") # trigger reload
589 self.assertEqual(mod.x, -99)
591 self.assertEqual(mod.x, -99)
590
592
591 def test_smoketest_aimport(self):
593 def test_smoketest_aimport(self):
592 self._check_smoketest(use_aimport=True)
594 self._check_smoketest(use_aimport=True)
593
595
594 def test_smoketest_autoreload(self):
596 def test_smoketest_autoreload(self):
595 self._check_smoketest(use_aimport=False)
597 self._check_smoketest(use_aimport=False)
General Comments 0
You need to be logged in to leave comments. Login now