]> granicus.if.org Git - python/commitdiff
Issue 19851: Fix a regression in reloading submodules.
authorEric Snow <ericsnowcurrently@gmail.com>
Tue, 10 Dec 2013 02:59:10 +0000 (19:59 -0700)
committerEric Snow <ericsnowcurrently@gmail.com>
Tue, 10 Dec 2013 02:59:10 +0000 (19:59 -0700)
Lib/importlib/__init__.py
Lib/test/test_importlib/test_api.py
Misc/NEWS

index 3e969bbead97e70d845e4d311875ae1d15975cdc..a4b1f55361ab93a65e2f53731e00449b8fbd48d0 100644 (file)
@@ -153,10 +153,17 @@ def reload(module):
     _RELOADING[name] = module
     try:
         parent_name = name.rpartition('.')[0]
-        if parent_name and parent_name not in sys.modules:
-            msg = "parent {!r} not in sys.modules"
-            raise ImportError(msg.format(parent_name), name=parent_name)
-        spec = module.__spec__ = _bootstrap._find_spec(name, None, module)
+        if parent_name:
+            try:
+                parent = sys.modules[parent_name]
+            except KeyError:
+                msg = "parent {!r} not in sys.modules"
+                raise ImportError(msg.format(parent_name), name=parent_name)
+            else:
+                pkgpath = parent.__path__
+        else:
+            pkgpath = None
+        spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, module)
         methods = _bootstrap._SpecMethods(spec)
         methods.exec(module)
         # The module may have replaced itself in sys.modules!
index c6c2d47051a3470f1b473cd0a546c3587ed06c09..792c82dd9ae5559268cf9b5c5a240fd216c1cd0d 100644 (file)
@@ -4,6 +4,7 @@ frozen_init, source_init = util.import_importlib('importlib')
 frozen_util, source_util = util.import_importlib('importlib.util')
 frozen_machinery, source_machinery = util.import_importlib('importlib.machinery')
 
+from contextlib import contextmanager
 import os.path
 import sys
 from test import support
@@ -11,6 +12,37 @@ import types
 import unittest
 
 
+@contextmanager
+def temp_module(name, content='', *, pkg=False):
+    conflicts = [n for n in sys.modules if n.partition('.')[0] == name]
+    with support.temp_cwd(None) as cwd:
+        with util.uncache(name, *conflicts):
+            with support.DirsOnSysPath(cwd):
+                frozen_init.invalidate_caches()
+
+                location = os.path.join(cwd, name)
+                if pkg:
+                    modpath = os.path.join(location, '__init__.py')
+                    os.mkdir(name)
+                else:
+                    modpath = location + '.py'
+                    if content is None:
+                        # Make sure the module file gets created.
+                        content = ''
+                if content is not None:
+                    # not a namespace package
+                    with open(modpath, 'w') as modfile:
+                        modfile.write(content)
+                yield location
+
+
+def submodule(parent, name, pkg_dir, content=''):
+    path = os.path.join(pkg_dir, name + '.py')
+    with open(path, 'w') as subfile:
+        subfile.write(content)
+    return '{}.{}'.format(parent, name), path
+
+
 class ImportModuleTests:
 
     """Test importlib.import_module."""
@@ -246,6 +278,32 @@ class FindSpecTests:
         # None is returned upon failure to find a loader.
         self.assertIsNone(self.init.find_spec('nevergoingtofindthismodule'))
 
+    def test_find_submodule(self):
+        name = 'spam'
+        subname = 'ham'
+        with temp_module(name, pkg=True) as pkg_dir:
+            fullname, _ = submodule(name, subname, pkg_dir)
+            spec = self.init.find_spec(fullname, [pkg_dir])
+            self.assertIsNot(spec, None)
+            self.assertNotIn(name, sorted(sys.modules))
+            # Ensure successive calls behave the same.
+            spec_again = self.init.find_spec(fullname, [pkg_dir])
+            # XXX Once #19927 is resolved, uncomment this line.
+            #self.assertEqual(spec_again, spec)
+
+    def test_find_submodule_missing_path(self):
+        name = 'spam'
+        subname = 'ham'
+        with temp_module(name, pkg=True) as pkg_dir:
+            fullname, _ = submodule(name, subname, pkg_dir)
+            spec = self.init.find_spec(fullname)
+            self.assertIs(spec, None)
+            self.assertNotIn(name, sorted(sys.modules))
+            # Ensure successive calls behave the same.
+            spec = self.init.find_spec(fullname)
+            self.assertIs(spec, None)
+
+
 class Frozen_FindSpecTests(FindSpecTests, unittest.TestCase):
     init = frozen_init
     machinery = frozen_machinery
@@ -410,6 +468,16 @@ class ReloadTests:
                     self.assertEqual(loader.path, init_path)
                     self.assertEqual(ns, expected)
 
+    def test_reload_submodule(self):
+        # See #19851.
+        name = 'spam'
+        subname = 'ham'
+        with temp_module(name, pkg=True) as pkg_dir:
+            fullname, _ = submodule(name, subname, pkg_dir)
+            ham = self.init.import_module(fullname)
+            reloaded = self.init.reload(ham)
+            self.assertIs(reloaded, ham)
+
 
 class Frozen_ReloadTests(ReloadTests, unittest.TestCase):
     init = frozen_init
index eab79a9cf3485ad3c3ef586081943bceba12406f..e4797327fd2663426fc5901843bd416515d317af 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -114,6 +114,8 @@ Library
 - Issue #6477: Added support for pickling the types of built-in singletons
   (i.e., Ellipsis, NotImplemented, None).
 
+- Issue #19851: Fixed a regression in reloading sub-modules.
+
 - ssl.create_default_context() sets OP_NO_COMPRESSION to prevent CRIME.
 
 - Issue #19802: Add socket.SO_PRIORITY.