[Python-checkins] cpython: Issue #19698: Remove exec_module() from the built-in and extension

brett.cannon python-checkins at python.org
Fri Nov 29 17:00:21 CET 2013


http://hg.python.org/cpython/rev/b5bbd47a9bc4
changeset:   87636:b5bbd47a9bc4
user:        Brett Cannon <brett at python.org>
date:        Fri Nov 29 11:00:11 2013 -0500
summary:
  Issue #19698: Remove exec_module() from the built-in and extension
module loaders.

Due to the fact that the call signatures for extension modules and
built-in modules does not allow for the specifying of what module to
initialize and that on Windows all extension modules are built-in
modules, work to clean up built-in and extension module initialization
will have to wait until Python 3.5. Because of this the semantics of
exec_module() would be incorrect, so removing the methods for now is
the best option; load_module() is still used as a fallback by
importlib and so this won't affect semantics.

files:
  Lib/importlib/_bootstrap.py                      |    63 +-
  Lib/test/test_importlib/builtin/test_loader.py   |    72 -
  Lib/test/test_importlib/extension/test_loader.py |    65 -
  Misc/NEWS                                        |     3 +
  Python/importlib.h                               |  8369 +++++----
  5 files changed, 4241 insertions(+), 4331 deletions(-)


diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -133,6 +133,24 @@
 _code_type = type(_wrap.__code__)
 
 
+
+class _ManageReload:
+
+    """Manages the possible clean-up of sys.modules for load_module()."""
+
+    def __init__(self, name):
+        self._name = name
+
+    def __enter__(self):
+        self._is_reload = self._name in sys.modules
+
+    def __exit__(self, *args):
+        if any(arg is not None for arg in args) and not self._is_reload:
+            try:
+                del sys.modules[self._name]
+            except KeyError:
+                pass
+
 # Module-level locking ########################################################
 
 # A dict mapping module names to weakrefs of _ModuleLock instances
@@ -1242,23 +1260,15 @@
         spec = cls.find_spec(fullname, path)
         return spec.loader if spec is not None else None
 
-    @staticmethod
-    def exec_module(module):
-        spec = module.__spec__
-        name = spec.name
-        if not _imp.is_builtin(name):
-            raise ImportError('{!r} is not a built-in module'.format(name),
-                              name=name)
-        _call_with_frames_removed(_imp.init_builtin, name)
-        # Have to manually initialize attributes since init_builtin() is not
-        # going to do it for us.
-        # XXX: Create _imp.exec_builtin(module)
-        _SpecMethods(spec).init_module_attrs(sys.modules[name])
-
     @classmethod
+    @_requires_builtin
     def load_module(cls, fullname):
         """Load a built-in module."""
-        return _load_module_shim(cls, fullname)
+        with _ManageReload(fullname):
+            module = _call_with_frames_removed(_imp.init_builtin, fullname)
+        module.__loader__ = cls
+        module.__package__ = ''
+        return module
 
     @classmethod
     @_requires_builtin
@@ -1639,22 +1649,21 @@
         self.name = name
         self.path = path
 
-    def exec_module(self, module):
-        # XXX create _imp.exec_dynamic()
-        spec = module.__spec__
-        fullname = spec.name
-        module = _call_with_frames_removed(_imp.load_dynamic,
-                                               fullname, self.path)
-        _verbose_message('extension module loaded from {!r}', self.path)
-        if self.is_package(fullname) and not hasattr(module, '__path__'):
-            module.__path__ = [_path_split(self.path)[0]]
-        _SpecMethods(spec).init_module_attrs(module)
-
-    # XXX deprecate
     @_check_name
     def load_module(self, fullname):
         """Load an extension module."""
-        return _load_module_shim(self, fullname)
+        with _ManageReload(fullname):
+            module = _call_with_frames_removed(_imp.load_dynamic,
+                                               fullname, self.path)
+        _verbose_message('extension module loaded from {!r}', self.path)
+        is_package = self.is_package(fullname)
+        if is_package and not hasattr(module, '__path__'):
+            module.__path__ = [_path_split(self.path)[0]]
+        module.__loader__ = self
+        module.__package__ = module.__name__
+        if not is_package:
+            module.__package__ = module.__package__.rpartition('.')[0]
+        return module
 
     def is_package(self, fullname):
         """Return True if the extension module is a package."""
diff --git a/Lib/test/test_importlib/builtin/test_loader.py b/Lib/test/test_importlib/builtin/test_loader.py
--- a/Lib/test/test_importlib/builtin/test_loader.py
+++ b/Lib/test/test_importlib/builtin/test_loader.py
@@ -9,78 +9,6 @@
 import unittest
 
 
-class ExecModTests(abc.LoaderTests):
-
-    """Test exec_module() for built-in modules."""
-
-    @classmethod
-    def setUpClass(cls):
-        cls.verification = {'__name__': 'errno', '__package__': '',
-                             '__loader__': cls.machinery.BuiltinImporter}
-
-    def verify(self, module):
-        """Verify that the module matches against what it should have."""
-        self.assertIsInstance(module, types.ModuleType)
-        for attr, value in self.verification.items():
-            self.assertEqual(getattr(module, attr), value)
-        self.assertIn(module.__name__, sys.modules)
-        self.assertTrue(hasattr(module, '__spec__'))
-        self.assertEqual(module.__spec__.origin, 'built-in')
-
-    def load_spec(self, name):
-        spec = self.machinery.ModuleSpec(name, self.machinery.BuiltinImporter,
-                                         origin='built-in')
-        module = types.ModuleType(name)
-        module.__spec__ = spec
-        self.machinery.BuiltinImporter.exec_module(module)
-        # Strictly not what exec_module() is supposed to do, but since
-        # _imp.init_builtin() does this we can't get around it.
-        return sys.modules[name]
-
-    def test_module(self):
-        # Common case.
-        with util.uncache(builtin_util.NAME):
-            module = self.load_spec(builtin_util.NAME)
-            self.verify(module)
-            self.assertIn('built-in', str(module))
-
-    # Built-in modules cannot be a package.
-    test_package = None
-
-    # Built-in modules cannot be a package.
-    test_lacking_parent = None
-
-    # Not way to force an import failure.
-    test_state_after_failure = None
-
-    def test_unloadable(self):
-        name = 'dssdsdfff'
-        assert name not in sys.builtin_module_names
-        with self.assertRaises(ImportError) as cm:
-            self.load_spec(name)
-        self.assertEqual(cm.exception.name, name)
-
-    def test_already_imported(self):
-        # Using the name of a module already imported but not a built-in should
-        # still fail.
-        module_name = 'builtin_reload_test'
-        assert module_name not in sys.builtin_module_names
-        with util.uncache(module_name):
-            module = types.ModuleType(module_name)
-            spec = self.machinery.ModuleSpec(module_name,
-                                             self.machinery.BuiltinImporter,
-                                             origin='built-in')
-            module.__spec__ = spec
-            sys.modules[module_name] = module
-        with self.assertRaises(ImportError) as cm:
-            self.machinery.BuiltinImporter.exec_module(module)
-        self.assertEqual(cm.exception.name, module_name)
-
-
-Frozen_ExecModTests, Source_ExecModTests = util.test_both(ExecModTests,
-        machinery=[frozen_machinery, source_machinery])
-
-
 class LoaderTests(abc.LoaderTests):
 
     """Test load_module() for built-in modules."""
diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py
--- a/Lib/test/test_importlib/extension/test_loader.py
+++ b/Lib/test/test_importlib/extension/test_loader.py
@@ -10,71 +10,6 @@
 import unittest
 
 
-class ExecModuleTests(abc.LoaderTests):
-
-    """Test load_module() for extension modules."""
-
-    def setUp(self):
-        self.loader = self.machinery.ExtensionFileLoader(ext_util.NAME,
-                                                         ext_util.FILEPATH)
-
-    def exec_module(self, fullname):
-        module = types.ModuleType(fullname)
-        module.__spec__ = self.machinery.ModuleSpec(fullname, self.loader)
-        self.loader.exec_module(module)
-        return sys.modules[fullname]
-
-    def test_exec_module_API(self):
-        with self.assertRaises(ImportError):
-            self.exec_module('XXX')
-
-
-    def test_module(self):
-        with util.uncache(ext_util.NAME):
-            module = self.exec_module(ext_util.NAME)
-            for attr, value in [('__name__', ext_util.NAME),
-                                ('__file__', ext_util.FILEPATH),
-                                ('__package__', '')]:
-                given = getattr(module, attr)
-                self.assertEqual(given, value,
-                                 '{}: {!r} != {!r}'.format(attr, given, value))
-            self.assertIn(ext_util.NAME, sys.modules)
-            self.assertIsInstance(module.__loader__,
-                                  self.machinery.ExtensionFileLoader)
-
-    # No extension module as __init__ available for testing.
-    test_package = None
-
-    # No extension module in a package available for testing.
-    test_lacking_parent = None
-
-    def test_module_reuse(self):
-        with util.uncache(ext_util.NAME):
-            module1 = self.exec_module(ext_util.NAME)
-            module2 = self.exec_module(ext_util.NAME)
-            self.assertIs(module1, module2)
-
-    def test_state_after_failure(self):
-        # No easy way to trigger a failure after a successful import.
-        pass
-
-    def test_unloadable(self):
-        name = 'asdfjkl;'
-        with self.assertRaises(ImportError) as cm:
-            self.exec_module(name)
-        self.assertEqual(cm.exception.name, name)
-
-    def test_is_package(self):
-        self.assertFalse(self.loader.is_package(ext_util.NAME))
-        for suffix in self.machinery.EXTENSION_SUFFIXES:
-            path = os.path.join('some', 'path', 'pkg', '__init__' + suffix)
-            loader = self.machinery.ExtensionFileLoader('pkg', path)
-            self.assertTrue(loader.is_package('pkg'))
-
-Frozen_ExecModuleTests, Source_ExecModuleTests = util.test_both(
-        ExecModuleTests, machinery=machinery)
-
-
 class LoaderTests(abc.LoaderTests):
 
     """Test load_module() for extension modules."""
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,9 @@
 Library
 -------
 
+- Issue #19698: Removed exec_module() methods from
+  importlib.machinery.BuiltinImporter and ExtensionFileLoader.
+
 - ssl.create_default_context() sets OP_NO_COMPRESSION to prevent CRIME.
 
 - Issue #19802: Add socket.SO_PRIORITY.
diff --git a/Python/importlib.h b/Python/importlib.h
--- a/Python/importlib.h
+++ b/Python/importlib.h
[stripped]

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list