Sitelet https://github.com/python/cpython/commit/c1e7c747f92a27f1f2bb392dbbfbd2817591f44f
Skip to content

Commit c1e7c74

Browse files
Issue 19851: Fix a regression in reloading submodules.
1 parent fc25d62 commit c1e7c74

3 files changed

Lines changed: 81 additions & 4 deletions

File tree

‎Lib/importlib/__init__.py‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,17 @@ def reload(module):
153153
_RELOADING[name] = module
154154
try:
155155
parent_name = name.rpartition('.')[0]
156-
if parent_name and parent_name not in sys.modules:
157-
msg = "parent {!r} not in sys.modules"
158-
raise ImportError(msg.format(parent_name), name=parent_name)
159-
spec = module.__spec__ = _bootstrap._find_spec(name, None, module)
156+
if parent_name:
157+
try:
158+
parent = sys.modules[parent_name]
159+
except KeyError:
160+
msg = "parent {!r} not in sys.modules"
161+
raise ImportError(msg.format(parent_name), name=parent_name)
162+
else:
163+
pkgpath = parent.__path__
164+
else:
165+
pkgpath = None
166+
spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, module)
160167
methods = _bootstrap._SpecMethods(spec)
161168
methods.exec(module)
162169
# The module may have replaced itself in sys.modules!

‎Lib/test/test_importlib/test_api.py‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,45 @@
44
frozen_util, source_util = util.import_importlib('importlib.util')
55
frozen_machinery, source_machinery = util.import_importlib('importlib.machinery')
66

7+
from contextlib import contextmanager
78
import os.path
89
import sys
910
from test import support
1011
import types
1112
import unittest
1213

1314

15+
@contextmanager
16+
def temp_module(name, content='', *, pkg=False):
17+
conflicts = [n for n in sys.modules if n.partition('.')[0] == name]
18+
with support.temp_cwd(None) as cwd:
19+
with util.uncache(name, *conflicts):
20+
with support.DirsOnSysPath(cwd):
21+
frozen_init.invalidate_caches()
22+
23+
location = os.path.join(cwd, name)
24+
if pkg:
25+
modpath = os.path.join(location, '__init__.py')
26+
os.mkdir(name)
27+
else:
28+
modpath = location + '.py'
29+
if content is None:
30+
# Make sure the module file gets created.
31+
content = ''
32+
if content is not None:
33+
# not a namespace package
34+
with open(modpath, 'w') as modfile:
35+
modfile.write(content)
36+
yield location
37+
38+
39+
def submodule(parent, name, pkg_dir, content=''):
40+
path = os.path.join(pkg_dir, name + '.py')
41+
with open(path, 'w') as subfile:
42+
subfile.write(content)
43+
return '{}.{}'.format(parent, name), path
44+
45+
1446
class ImportModuleTests:
1547

1648
"""Test importlib.import_module."""
@@ -246,6 +278,32 @@ def test_nothing(self):
246278
# None is returned upon failure to find a loader.
247279
self.assertIsNone(self.init.find_spec('nevergoingtofindthismodule'))
248280

281+
def test_find_submodule(self):
282+
name = 'spam'
283+
subname = 'ham'
284+
with temp_module(name, pkg=True) as pkg_dir:
285+
fullname, _ = submodule(name, subname, pkg_dir)
286+
spec = self.init.find_spec(fullname, [pkg_dir])
287+
self.assertIsNot(spec, None)
288+
self.assertNotIn(name, sorted(sys.modules))
289+
# Ensure successive calls behave the same.
290+
spec_again = self.init.find_spec(fullname, [pkg_dir])
291+
# XXX Once #19927 is resolved, uncomment this line.
292+
#self.assertEqual(spec_again, spec)
293+
294+
def test_find_submodule_missing_path(self):
295+
name = 'spam'
296+
subname = 'ham'
297+
with temp_module(name, pkg=True) as pkg_dir:
298+
fullname, _ = submodule(name, subname, pkg_dir)
299+
spec = self.init.find_spec(fullname)
300+
self.assertIs(spec, None)
301+
self.assertNotIn(name, sorted(sys.modules))
302+
# Ensure successive calls behave the same.
303+
spec = self.init.find_spec(fullname)
304+
self.assertIs(spec, None)
305+
306+
249307
class Frozen_FindSpecTests(FindSpecTests, unittest.TestCase):
250308
init = frozen_init
251309
machinery = frozen_machinery
@@ -410,6 +468,16 @@ def test_reload_namespace_changed(self):
410468
self.assertEqual(loader.path, init_path)
411469
self.assertEqual(ns, expected)
412470

471+
def test_reload_submodule(self):
472+
# See #19851.
473+
name = 'spam'
474+
subname = 'ham'
475+
with temp_module(name, pkg=True) as pkg_dir:
476+
fullname, _ = submodule(name, subname, pkg_dir)
477+
ham = self.init.import_module(fullname)
478+
reloaded = self.init.reload(ham)
479+
self.assertIs(reloaded, ham)
480+
413481

414482
class Frozen_ReloadTests(ReloadTests, unittest.TestCase):
415483
init = frozen_init

‎Misc/NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ Library
114114
- Issue #6477: Added support for pickling the types of built-in singletons
115115
(i.e., Ellipsis, NotImplemented, None).
116116

117+
- Issue #19851: Fixed a regression in reloading sub-modules.
118+
117119
- ssl.create_default_context() sets OP_NO_COMPRESSION to prevent CRIME.
118120

119121
- Issue #19802: Add socket.SO_PRIORITY.

0 commit comments

Comments
 (0)