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