##// END OF EJS Templates
Port `deepreload` to `importlib`...
Nikita Kniazev -
Show More
@@ -28,7 +28,7 b' re-implementation of hierarchical module import.'
28
28
29 import builtins as builtin_mod
29 import builtins as builtin_mod
30 from contextlib import contextmanager
30 from contextlib import contextmanager
31 import imp
31 import importlib
32 import sys
32 import sys
33
33
34 from types import ModuleType
34 from types import ModuleType
@@ -174,33 +174,17 b' def import_submodule(mod, subname, fullname):'
174 print('Reloading', fullname)
174 print('Reloading', fullname)
175 found_now[fullname] = 1
175 found_now[fullname] = 1
176 oldm = sys.modules.get(fullname, None)
176 oldm = sys.modules.get(fullname, None)
177
178 if mod is None:
179 path = None
180 elif hasattr(mod, '__path__'):
181 path = mod.__path__
182 else:
183 return None
184
185 try:
186 # This appears to be necessary on Python 3, because imp.find_module()
187 # tries to import standard libraries (like io) itself, and we don't
188 # want them to be processed by our deep_import_hook.
189 with replace_import_hook(original_import):
190 fp, filename, stuff = imp.find_module(subname, path)
191 except ImportError:
192 return None
193
194 try:
177 try:
195 m = imp.load_module(fullname, fp, filename, stuff)
178 if oldm is not None:
179 m = importlib.reload(oldm)
180 else:
181 m = importlib.import_module(subname, mod)
196 except:
182 except:
197 # load_module probably removed name from modules because of
183 # load_module probably removed name from modules because of
198 # the error. Put back the original module object.
184 # the error. Put back the original module object.
199 if oldm:
185 if oldm:
200 sys.modules[fullname] = oldm
186 sys.modules[fullname] = oldm
201 raise
187 raise
202 finally:
203 if fp: fp.close()
204
188
205 add_submodule(mod, m, fullname, subname)
189 add_submodule(mod, m, fullname, subname)
206
190
@@ -285,43 +269,17 b' def deep_reload_hook(m):'
285 except:
269 except:
286 modules_reloading[name] = m
270 modules_reloading[name] = m
287
271
288 dot = name.rfind('.')
289 if dot < 0:
290 subname = name
291 path = None
292 else:
293 try:
272 try:
294 parent = sys.modules[name[:dot]]
273 newm = importlib.reload(m)
295 except KeyError as e:
296 modules_reloading.clear()
297 raise ImportError("reload(): parent %.200s not in sys.modules" % name[:dot]) from e
298 subname = name[dot+1:]
299 path = getattr(parent, "__path__", None)
300
301 try:
302 # This appears to be necessary on Python 3, because imp.find_module()
303 # tries to import standard libraries (like io) itself, and we don't
304 # want them to be processed by our deep_import_hook.
305 with replace_import_hook(original_import):
306 fp, filename, stuff = imp.find_module(subname, path)
307 finally:
308 modules_reloading.clear()
309
310 try:
311 newm = imp.load_module(name, fp, filename, stuff)
312 except:
274 except:
313 # load_module probably removed name from modules because of
314 # the error. Put back the original module object.
315 sys.modules[name] = m
275 sys.modules[name] = m
316 raise
276 raise
317 finally:
277 finally:
318 if fp: fp.close()
319
320 modules_reloading.clear()
278 modules_reloading.clear()
321 return newm
279 return newm
322
280
323 # Save the original hooks
281 # Save the original hooks
324 original_reload = imp.reload
282 original_reload = importlib.reload
325
283
326 # Replacement for reload()
284 # Replacement for reload()
327 def reload(module, exclude=('sys', 'os.path', 'builtins', '__main__',
285 def reload(module, exclude=('sys', 'os.path', 'builtins', '__main__',
@@ -4,11 +4,14 b''
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 pytest
8 import types
9
7 from pathlib import Path
10 from pathlib import Path
8
11
9 from IPython.utils.syspathcontext import prepended_to_syspath
12 from IPython.utils.syspathcontext import prepended_to_syspath
10 from IPython.utils.tempdir import TemporaryDirectory
13 from IPython.utils.tempdir import TemporaryDirectory
11 from IPython.lib.deepreload import reload as dreload
14 from IPython.lib.deepreload import reload as dreload, modules_reloading
12
15
13
16
14 def test_deepreload():
17 def test_deepreload():
@@ -17,9 +20,9 b' def test_deepreload():'
17 with prepended_to_syspath(tmpdir):
20 with prepended_to_syspath(tmpdir):
18 tmpdirpath = Path(tmpdir)
21 tmpdirpath = Path(tmpdir)
19 with open(tmpdirpath / "A.py", "w") as f:
22 with open(tmpdirpath / "A.py", "w") as f:
20 f.write("class Object(object):\n pass\n")
23 f.write("class Object:\n pass\nok = True\n")
21 with open(tmpdirpath / "B.py", "w") as f:
24 with open(tmpdirpath / "B.py", "w") as f:
22 f.write("import A\n")
25 f.write("import A\nassert A.ok, 'we are fine'\n")
23 import A
26 import A
24 import B
27 import B
25
28
@@ -28,7 +31,26 b' def test_deepreload():'
28 dreload(B, exclude=["A"])
31 dreload(B, exclude=["A"])
29 assert isinstance(obj, A.Object) is True
32 assert isinstance(obj, A.Object) is True
30
33
34 # Test that an import failure will not blow-up us.
35 A.ok = False
36 with pytest.raises(AssertionError, match="we are fine"):
37 dreload(B, exclude=["A"])
38 assert len(modules_reloading) == 0
39 assert not A.ok
40
31 # Test that A is reloaded.
41 # Test that A is reloaded.
32 obj = A.Object()
42 obj = A.Object()
43 A.ok = False
33 dreload(B)
44 dreload(B)
45 assert A.ok
34 assert isinstance(obj, A.Object) is False
46 assert isinstance(obj, A.Object) is False
47
48
49 def test_not_module():
50 pytest.raises(TypeError, dreload, "modulename")
51
52
53 def test_not_in_sys_modules():
54 fake_module = types.ModuleType("fake_module")
55 with pytest.raises(ImportError, match="not in sys.modules"):
56 dreload(fake_module)
General Comments 0
You need to be logged in to leave comments. Login now