Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7f364bb

Browse files
committed
Port deepreload to importlib
`imp` is deprecated since Python 3.3 and scheduled for removal in Python 3.12 The code is severely undertested, I added several, and also fixed a bug that `modules_reloading` was not cleaned-up on an import failure.
1 parent e95b1e8 commit 7f364bb

2 files changed

Lines changed: 33 additions & 53 deletions

File tree

IPython/lib/deepreload.py

Lines changed: 8 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
import builtins as builtin_mod
3030
from contextlib import contextmanager
31-
import imp
31+
import importlib
3232
import sys
3333

3434
from types import ModuleType
@@ -174,33 +174,17 @@ def import_submodule(mod, subname, fullname):
174174
print('Reloading', fullname)
175175
found_now[fullname] = 1
176176
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-
194177
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)
196182
except:
197183
# load_module probably removed name from modules because of
198184
# the error. Put back the original module object.
199185
if oldm:
200186
sys.modules[fullname] = oldm
201187
raise
202-
finally:
203-
if fp: fp.close()
204188

205189
add_submodule(mod, m, fullname, subname)
206190

@@ -285,43 +269,17 @@ def deep_reload_hook(m):
285269
except:
286270
modules_reloading[name] = m
287271

288-
dot = name.rfind('.')
289-
if dot < 0:
290-
subname = name
291-
path = None
292-
else:
293-
try:
294-
parent = sys.modules[name[:dot]]
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-
310272
try:
311-
newm = imp.load_module(name, fp, filename, stuff)
273+
newm = importlib.reload(m)
312274
except:
313-
# load_module probably removed name from modules because of
314-
# the error. Put back the original module object.
315275
sys.modules[name] = m
316276
raise
317277
finally:
318-
if fp: fp.close()
319-
320-
modules_reloading.clear()
278+
modules_reloading.clear()
321279
return newm
322280

323281
# Save the original hooks
324-
original_reload = imp.reload
282+
original_reload = importlib.reload
325283

326284
# Replacement for reload()
327285
def reload(module, exclude=('sys', 'os.path', 'builtins', '__main__',

IPython/lib/tests/test_deepreload.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
# Copyright (c) IPython Development Team.
55
# Distributed under the terms of the Modified BSD License.
66

7+
import pytest
8+
import types
9+
710
from pathlib import Path
811

912
from IPython.utils.syspathcontext import prepended_to_syspath
1013
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
1215

1316

1417
def test_deepreload():
@@ -17,9 +20,9 @@ def test_deepreload():
1720
with prepended_to_syspath(tmpdir):
1821
tmpdirpath = Path(tmpdir)
1922
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")
2124
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")
2326
import A
2427
import B
2528

@@ -28,7 +31,26 @@ def test_deepreload():
2831
dreload(B, exclude=["A"])
2932
assert isinstance(obj, A.Object) is True
3033

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+
3141
# Test that A is reloaded.
3242
obj = A.Object()
43+
A.ok = False
3344
dreload(B)
45+
assert A.ok
3446
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)

0 commit comments

Comments
 (0)