[Python-checkins] cpython: Issue #20383: Introduce importlib.util.module_from_spec().

brett.cannon python-checkins at python.org
Fri May 30 20:55:40 CEST 2014


http://hg.python.org/cpython/rev/b26d021081d2
changeset:   90915:b26d021081d2
parent:      90913:69011f6ce573
user:        Brett Cannon <brett at python.org>
date:        Fri May 30 14:55:29 2014 -0400
summary:
  Issue #20383: Introduce importlib.util.module_from_spec().

Along the way, dismantle importlib._bootstrap._SpecMethods as it was
no longer relevant and constructing the new function required
partially dismantling the class anyway.

files:
  Doc/library/importlib.rst            |    15 +
  Doc/library/types.rst                |     4 +
  Doc/whatsnew/3.5.rst                 |     4 +
  Lib/imp.py                           |    17 +-
  Lib/importlib/__init__.py            |     3 +-
  Lib/importlib/_bootstrap.py          |   393 +-
  Lib/importlib/abc.py                 |     2 +-
  Lib/importlib/util.py                |     1 +
  Lib/pkgutil.py                       |     2 +-
  Lib/pydoc.py                         |     9 +-
  Lib/runpy.py                         |     2 +-
  Lib/test/test_importlib/test_spec.py |   187 +-
  Lib/test/test_importlib/test_util.py |   152 +-
  Lib/test/test_tools.py               |     4 +-
  Misc/NEWS                            |     3 +
  Python/importlib.h                   |  8486 ++++++-------
  setup.py                             |     2 +-
  17 files changed, 4543 insertions(+), 4743 deletions(-)


diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst
--- a/Doc/library/importlib.rst
+++ b/Doc/library/importlib.rst
@@ -1129,6 +1129,21 @@
 
    .. versionadded:: 3.4
 
+.. function:: module_from_spec(spec)
+
+   Create a new module based on **spec**.
+
+   If the module object is from ``spec.loader.create_module()``, then any
+   pre-existing attributes will not be reset. Also, no :exc:`AttributeError`
+   will be raised if triggered while accessing **spec** or setting an attribute
+   on the module.
+
+   This function is preferred over using :class:`types.ModuleType` to create a
+   new module as **spec** is used to set as many import-controlled attributes on
+   the module as possible.
+
+   .. versionadded:: 3.5
+
 .. decorator:: module_for_loader
 
     A :term:`decorator` for :meth:`importlib.abc.Loader.load_module`
diff --git a/Doc/library/types.rst b/Doc/library/types.rst
--- a/Doc/library/types.rst
+++ b/Doc/library/types.rst
@@ -115,6 +115,10 @@
    The type of :term:`modules <module>`. Constructor takes the name of the
    module to be created and optionally its :term:`docstring`.
 
+   .. note::
+      Use :func:`importlib.util.module_from_spec` to create a new module if you
+      wish to set the various import-controlled attributes.
+
    .. attribute:: __doc__
 
       The :term:`docstring` of the module. Defaults to ``None``.
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -153,6 +153,10 @@
   With a module object that you want to initialize you can then use
   ``exec(code, module.__dict__)`` to execute the code in the module.
 
+* :func:`importlib.util.module_from_spec` is now the preferred way to create a
+  new module. Compared to :class:`types.ModuleType`, this new function will set
+  the various import-controlled attributes based on the passed-in spec object.
+
 inspect
 -------
 
diff --git a/Lib/imp.py b/Lib/imp.py
--- a/Lib/imp.py
+++ b/Lib/imp.py
@@ -16,7 +16,7 @@
     # Platform doesn't support dynamic loading.
     load_dynamic = None
 
-from importlib._bootstrap import SourcelessFileLoader, _ERR_MSG, _SpecMethods
+from importlib._bootstrap import SourcelessFileLoader, _ERR_MSG, _exec, _load
 
 from importlib import machinery
 from importlib import util
@@ -164,11 +164,10 @@
 def load_source(name, pathname, file=None):
     loader = _LoadSourceCompatibility(name, pathname, file)
     spec = util.spec_from_file_location(name, pathname, loader=loader)
-    methods = _SpecMethods(spec)
     if name in sys.modules:
-        module = methods.exec(sys.modules[name])
+        module = _exec(spec, sys.modules[name])
     else:
-        module = methods.load()
+        module = _load(spec)
     # To allow reloading to potentially work, use a non-hacked loader which
     # won't rely on a now-closed file object.
     module.__loader__ = machinery.SourceFileLoader(name, pathname)
@@ -185,11 +184,10 @@
     """**DEPRECATED**"""
     loader = _LoadCompiledCompatibility(name, pathname, file)
     spec = util.spec_from_file_location(name, pathname, loader=loader)
-    methods = _SpecMethods(spec)
     if name in sys.modules:
-        module = methods.exec(sys.modules[name])
+        module = _exec(spec, sys.modules[name])
     else:
-        module = methods.load()
+        module = _load(spec)
     # To allow reloading to potentially work, use a non-hacked loader which
     # won't rely on a now-closed file object.
     module.__loader__ = SourcelessFileLoader(name, pathname)
@@ -210,11 +208,10 @@
             raise ValueError('{!r} is not a package'.format(path))
     spec = util.spec_from_file_location(name, path,
                                         submodule_search_locations=[])
-    methods = _SpecMethods(spec)
     if name in sys.modules:
-        return methods.exec(sys.modules[name])
+        return _exec(spec, sys.modules[name])
     else:
-        return methods.load()
+        return _load(spec)
 
 
 def load_module(name, file, filename, details):
diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py
--- a/Lib/importlib/__init__.py
+++ b/Lib/importlib/__init__.py
@@ -145,8 +145,7 @@
             pkgpath = None
         target = module
         spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, target)
-        methods = _bootstrap._SpecMethods(spec)
-        methods.exec(module)
+        _bootstrap._exec(spec, module)
         # The module may have replaced itself in sys.modules!
         return sys.modules[name]
     finally:
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -9,7 +9,7 @@
 #
 # IMPORTANT: Whenever making changes to this module, be sure to run
 # a top-level make in order to get the frozen version of the module
-# update. Not doing so will result in the Makefile to fail for
+# updated. Not doing so will result in the Makefile to fail for
 # all others who don't have a ./python around to freeze the module
 # in the early stages of compilation.
 #
@@ -581,20 +581,19 @@
     return loader
 
 
-def _load_module_shim(self, fullname):
+def _load_module_shim(spec, fullname):
     """Load the specified module into sys.modules and return it.
 
     This method is deprecated.  Use loader.exec_module instead.
 
     """
-    spec = spec_from_loader(fullname, self)
-    methods = _SpecMethods(spec)
+    spec = spec_from_loader(fullname, spec)
     if fullname in sys.modules:
         module = sys.modules[fullname]
-        methods.exec(module)
+        _exec(spec, module)
         return sys.modules[fullname]
     else:
-        return methods.load()
+        return _load(spec)
 
 
 def _validate_bytecode_header(data, source_stats=None, name=None, path=None):
@@ -705,7 +704,7 @@
         pass
     else:
         if spec is not None:
-            return _SpecMethods(spec).module_repr()
+            return _module_repr_from_spec(spec)
 
     # We could use module.__class__.__name__ instead of 'module' in the
     # various repr permutations.
@@ -991,234 +990,182 @@
     return spec
 
 
-class _SpecMethods:
-
-    """Convenience wrapper around spec objects to provide spec-specific
-    methods."""
-
-    # The various spec_from_* functions could be made factory methods here.
-
-    def __init__(self, spec):
-        self.spec = spec
-
-    def module_repr(self):
-        """Return the repr to use for the module."""
-        # We mostly replicate _module_repr() using the spec attributes.
-        spec = self.spec
-        name = '?' if spec.name is None else spec.name
-        if spec.origin is None:
-            if spec.loader is None:
-                return '<module {!r}>'.format(name)
-            else:
-                return '<module {!r} ({!r})>'.format(name, spec.loader)
-        else:
-            if spec.has_location:
-                return '<module {!r} from {!r}>'.format(name, spec.origin)
-            else:
-                return '<module {!r} ({})>'.format(spec.name, spec.origin)
-
-    def init_module_attrs(self, module, *, _override=False, _force_name=True):
-        """Set the module's attributes.
-
-        All missing import-related module attributes will be set.  Here
-        is how the spec attributes map onto the module:
-
-        spec.name -> module.__name__
-        spec.loader -> module.__loader__
-        spec.parent -> module.__package__
-        spec -> module.__spec__
-
-        Optional:
-        spec.origin -> module.__file__ (if spec.set_fileattr is true)
-        spec.cached -> module.__cached__ (if __file__ also set)
-        spec.submodule_search_locations -> module.__path__ (if set)
-
-        """
-        spec = self.spec
-
-        # The passed in module may be not support attribute assignment,
-        # in which case we simply don't set the attributes.
-
-        # __name__
-        if (_override or _force_name or
-            getattr(module, '__name__', None) is None):
+def _init_module_attrs(spec, module, *, override=False):
+    # The passed-in module may be not support attribute assignment,
+    # in which case we simply don't set the attributes.
+    # __name__
+    if (override or getattr(module, '__name__', None) is None):
+        try:
+            module.__name__ = spec.name
+        except AttributeError:
+            pass
+    # __loader__
+    if override or getattr(module, '__loader__', None) is None:
+        loader = spec.loader
+        if loader is None:
+            # A backward compatibility hack.
+            if spec.submodule_search_locations is not None:
+                loader = _NamespaceLoader.__new__(_NamespaceLoader)
+                loader._path = spec.submodule_search_locations
+        try:
+            module.__loader__ = loader
+        except AttributeError:
+            pass
+    # __package__
+    if override or getattr(module, '__package__', None) is None:
+        try:
+            module.__package__ = spec.parent
+        except AttributeError:
+            pass
+    # __spec__
+    try:
+        module.__spec__ = spec
+    except AttributeError:
+        pass
+    # __path__
+    if override or getattr(module, '__path__', None) is None:
+        if spec.submodule_search_locations is not None:
             try:
-                module.__name__ = spec.name
+                module.__path__ = spec.submodule_search_locations
+            except AttributeError:
+                pass
+    # __file__/__cached__
+    if spec.has_location:
+        if override or getattr(module, '__file__', None) is None:
+            try:
+                module.__file__ = spec.origin
             except AttributeError:
                 pass
 
-        # __loader__
-        if _override or getattr(module, '__loader__', None) is None:
-            loader = spec.loader
-            if loader is None:
-                # A backward compatibility hack.
-                if spec.submodule_search_locations is not None:
-                    loader = _NamespaceLoader.__new__(_NamespaceLoader)
-                    loader._path = spec.submodule_search_locations
-            try:
-                module.__loader__ = loader
-            except AttributeError:
-                pass
+        if override or getattr(module, '__cached__', None) is None:
+            if spec.cached is not None:
+                try:
+                    module.__cached__ = spec.cached
+                except AttributeError:
+                    pass
+    return module
 
-        # __package__
-        if _override or getattr(module, '__package__', None) is None:
-            try:
-                module.__package__ = spec.parent
-            except AttributeError:
-                pass
 
-        # __spec__
+def module_from_spec(spec):
+    """Create a module based on the provided spec."""
+    # Typically loaders will not implement create_module().
+    module = None
+    if hasattr(spec.loader, 'create_module'):
+        # If create_module() returns `None` then it means default
+        # module creation should be used.
+        module = spec.loader.create_module(spec)
+    if module is None:
+        module = _new_module(spec.name)
+    _init_module_attrs(spec, module)
+    return module
+
+
+def _module_repr_from_spec(spec):
+    """Return the repr to use for the module."""
+    # We mostly replicate _module_repr() using the spec attributes.
+    name = '?' if spec.name is None else spec.name
+    if spec.origin is None:
+        if spec.loader is None:
+            return '<module {!r}>'.format(name)
+        else:
+            return '<module {!r} ({!r})>'.format(name, spec.loader)
+    else:
+        if spec.has_location:
+            return '<module {!r} from {!r}>'.format(name, spec.origin)
+        else:
+            return '<module {!r} ({})>'.format(spec.name, spec.origin)
+
+
+# Used by importlib.reload() and _load_module_shim().
+def _exec(spec, module):
+    """Execute the spec in an existing module's namespace."""
+    name = spec.name
+    _imp.acquire_lock()
+    with _ModuleLockManager(name):
+        if sys.modules.get(name) is not module:
+            msg = 'module {!r} not in sys.modules'.format(name)
+            raise ImportError(msg, name=name)
+        if spec.loader is None:
+            if spec.submodule_search_locations is None:
+                raise ImportError('missing loader', name=spec.name)
+            # namespace package
+            _init_module_attrs(spec, module, override=True)
+            return module
+        _init_module_attrs(spec, module, override=True)
+        if not hasattr(spec.loader, 'exec_module'):
+            # (issue19713) Once BuiltinImporter and ExtensionFileLoader
+            # have exec_module() implemented, we can add a deprecation
+            # warning here.
+            spec.loader.load_module(name)
+        else:
+            spec.loader.exec_module(module)
+    return sys.modules[name]
+
+
+def _load_backward_compatible(spec):
+    # (issue19713) Once BuiltinImporter and ExtensionFileLoader
+    # have exec_module() implemented, we can add a deprecation
+    # warning here.
+    spec.loader.load_module(spec.name)
+    # The module must be in sys.modules at this point!
+    module = sys.modules[spec.name]
+    if getattr(module, '__loader__', None) is None:
+        try:
+            module.__loader__ = spec.loader
+        except AttributeError:
+            pass
+    if getattr(module, '__package__', None) is None:
+        try:
+            # Since module.__path__ may not line up with
+            # spec.submodule_search_paths, we can't necessarily rely
+            # on spec.parent here.
+            module.__package__ = module.__name__
+            if not hasattr(module, '__path__'):
+                module.__package__ = spec.name.rpartition('.')[0]
+        except AttributeError:
+            pass
+    if getattr(module, '__spec__', None) is None:
         try:
             module.__spec__ = spec
         except AttributeError:
             pass
+    return module
 
-        # __path__
-        if _override or getattr(module, '__path__', None) is None:
-            if spec.submodule_search_locations is not None:
-                try:
-                    module.__path__ = spec.submodule_search_locations
-                except AttributeError:
-                    pass
+def _load_unlocked(spec):
+    # A helper for direct use by the import system.
+    if spec.loader is not None:
+        # not a namespace package
+        if not hasattr(spec.loader, 'exec_module'):
+            return _load_backward_compatible(spec)
 
-        if spec.has_location:
-            # __file__
-            if _override or getattr(module, '__file__', None) is None:
-                try:
-                    module.__file__ = spec.origin
-                except AttributeError:
-                    pass
+    module = module_from_spec(spec)
+    with _installed_safely(module):
+        if spec.loader is None:
+            if spec.submodule_search_locations is None:
+                raise ImportError('missing loader', name=spec.name)
+            # A namespace package so do nothing.
+        else:
+            spec.loader.exec_module(module)
 
-            # __cached__
-            if _override or getattr(module, '__cached__', None) is None:
-                if spec.cached is not None:
-                    try:
-                        module.__cached__ = spec.cached
-                    except AttributeError:
-                        pass
+    # We don't ensure that the import-related module attributes get
+    # set in the sys.modules replacement case.  Such modules are on
+    # their own.
+    return sys.modules[spec.name]
 
-    def create(self):
-        """Return a new module to be loaded.
+# A method used during testing of _load_unlocked() and by
+# _load_module_shim().
+def _load(spec):
+    """Return a new module object, loaded by the spec's loader.
 
-        The import-related module attributes are also set with the
-        appropriate values from the spec.
+    The module is not added to its parent.
 
-        """
-        spec = self.spec
-        # Typically loaders will not implement create_module().
-        if hasattr(spec.loader, 'create_module'):
-            # If create_module() returns `None` it means the default
-            # module creation should be used.
-            module = spec.loader.create_module(spec)
-        else:
-            module = None
-        if module is None:
-            # This must be done before open() is ever called as the 'io'
-            # module implicitly imports 'locale' and would otherwise
-            # trigger an infinite loop.
-            module = _new_module(spec.name)
-        self.init_module_attrs(module)
-        return module
+    If a module is already in sys.modules, that existing module gets
+    clobbered.
 
-    def _exec(self, module):
-        """Do everything necessary to execute the module.
-
-        The namespace of `module` is used as the target of execution.
-        This method uses the loader's `exec_module()` method.
-
-        """
-        self.spec.loader.exec_module(module)
-
-    # Used by importlib.reload() and _load_module_shim().
-    def exec(self, module):
-        """Execute the spec in an existing module's namespace."""
-        name = self.spec.name
-        _imp.acquire_lock()
-        with _ModuleLockManager(name):
-            if sys.modules.get(name) is not module:
-                msg = 'module {!r} not in sys.modules'.format(name)
-                raise ImportError(msg, name=name)
-            if self.spec.loader is None:
-                if self.spec.submodule_search_locations is None:
-                    raise ImportError('missing loader', name=self.spec.name)
-                # namespace package
-                self.init_module_attrs(module, _override=True)
-                return module
-            self.init_module_attrs(module, _override=True)
-            if not hasattr(self.spec.loader, 'exec_module'):
-                # (issue19713) Once BuiltinImporter and ExtensionFileLoader
-                # have exec_module() implemented, we can add a deprecation
-                # warning here.
-                self.spec.loader.load_module(name)
-            else:
-                self._exec(module)
-        return sys.modules[name]
-
-    def _load_backward_compatible(self):
-        # (issue19713) Once BuiltinImporter and ExtensionFileLoader
-        # have exec_module() implemented, we can add a deprecation
-        # warning here.
-        spec = self.spec
-        spec.loader.load_module(spec.name)
-        # The module must be in sys.modules at this point!
-        module = sys.modules[spec.name]
-        if getattr(module, '__loader__', None) is None:
-            try:
-                module.__loader__ = spec.loader
-            except AttributeError:
-                pass
-        if getattr(module, '__package__', None) is None:
-            try:
-                # Since module.__path__ may not line up with
-                # spec.submodule_search_paths, we can't necessarily rely
-                # on spec.parent here.
-                module.__package__ = module.__name__
-                if not hasattr(module, '__path__'):
-                    module.__package__ = spec.name.rpartition('.')[0]
-            except AttributeError:
-                pass
-        if getattr(module, '__spec__', None) is None:
-            try:
-                module.__spec__ = spec
-            except AttributeError:
-                pass
-        return module
-
-    def _load_unlocked(self):
-        # A helper for direct use by the import system.
-        if self.spec.loader is not None:
-            # not a namespace package
-            if not hasattr(self.spec.loader, 'exec_module'):
-                return self._load_backward_compatible()
-
-        module = self.create()
-        with _installed_safely(module):
-            if self.spec.loader is None:
-                if self.spec.submodule_search_locations is None:
-                    raise ImportError('missing loader', name=self.spec.name)
-                # A namespace package so do nothing.
-            else:
-                self._exec(module)
-
-        # We don't ensure that the import-related module attributes get
-        # set in the sys.modules replacement case.  Such modules are on
-        # their own.
-        return sys.modules[self.spec.name]
-
-    # A method used during testing of _load_unlocked() and by
-    # _load_module_shim().
-    def load(self):
-        """Return a new module object, loaded by the spec's loader.
-
-        The module is not added to its parent.
-
-        If a module is already in sys.modules, that existing module gets
-        clobbered.
-
-        """
-        _imp.acquire_lock()
-        with _ModuleLockManager(self.spec.name):
-            return self._load_unlocked()
+    """
+    _imp.acquire_lock()
+    with _ModuleLockManager(spec.name):
+        return _load_unlocked(spec)
 
 
 def _fix_up_module(ns, name, pathname, cpathname=None):
@@ -1800,7 +1747,7 @@
         self._path.append(item)
 
 
-# We use this exclusively in init_module_attrs() for backward-compatibility.
+# We use this exclusively in module_from_spec() for backward-compatibility.
 class _NamespaceLoader:
     def __init__(self, name, path, path_finder):
         self._path = _NamespacePath(name, path, path_finder)
@@ -2224,7 +2171,7 @@
     if spec is None:
         raise ImportError(_ERR_MSG.format(name), name=name)
     else:
-        module = _SpecMethods(spec)._load_unlocked()
+        module = _load_unlocked(spec)
     if parent:
         # Set the module as an attribute on its parent.
         parent_module = sys.modules[parent]
@@ -2359,8 +2306,7 @@
     spec = BuiltinImporter.find_spec(name)
     if spec is None:
         raise ImportError('no built-in module named ' + name)
-    methods = _SpecMethods(spec)
-    return methods._load_unlocked()
+    return _load_unlocked(spec)
 
 
 def _setup(sys_module, _imp_module):
@@ -2391,8 +2337,7 @@
             else:
                 continue
             spec = _spec_from_module(module, loader)
-            methods = _SpecMethods(spec)
-            methods.init_module_attrs(module)
+            _init_module_attrs(spec, module)
 
     # Directly load built-in modules needed during bootstrap.
     self_module = sys.modules[__name__]
diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py
--- a/Lib/importlib/abc.py
+++ b/Lib/importlib/abc.py
@@ -126,7 +126,7 @@
         create_module() is optional.
 
         """
-        # By default, defer to _SpecMethods.create() for the new module.
+        # By default, defer to default semantics for the new module.
         return None
 
     # We don't define exec_module() here since that would break
diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py
--- a/Lib/importlib/util.py
+++ b/Lib/importlib/util.py
@@ -3,6 +3,7 @@
 from ._bootstrap import MAGIC_NUMBER
 from ._bootstrap import cache_from_source
 from ._bootstrap import decode_source
+from ._bootstrap import module_from_spec
 from ._bootstrap import source_from_cache
 from ._bootstrap import spec_from_loader
 from ._bootstrap import spec_from_file_location
diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py
--- a/Lib/pkgutil.py
+++ b/Lib/pkgutil.py
@@ -616,7 +616,7 @@
         return None
     # XXX needs test
     mod = (sys.modules.get(package) or
-           importlib._bootstrap._SpecMethods(spec).load())
+           importlib._bootstrap._load(spec))
     if mod is None or not hasattr(mod, '__file__'):
         return None
 
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -263,9 +263,8 @@
             # XXX We probably don't need to pass in the loader here.
             spec = importlib.util.spec_from_file_location('__temp__', filename,
                                                           loader=loader)
-            _spec = importlib._bootstrap._SpecMethods(spec)
             try:
-                module = _spec.load()
+                module = importlib._bootstrap._load(spec)
             except:
                 return None
             del sys.modules['__temp__']
@@ -297,9 +296,8 @@
         loader = importlib._bootstrap.SourceFileLoader(name, path)
     # XXX We probably don't need to pass in the loader here.
     spec = importlib.util.spec_from_file_location(name, path, loader=loader)
-    _spec = importlib._bootstrap._SpecMethods(spec)
     try:
-        return _spec.load()
+        return importlib._bootstrap._load(spec)
     except:
         raise ErrorDuringImport(path, sys.exc_info())
 
@@ -2057,9 +2055,8 @@
                     else:
                         path = None
                 else:
-                    _spec = importlib._bootstrap._SpecMethods(spec)
                     try:
-                        module = _spec.load()
+                        module = importlib._bootstrap._load(spec)
                     except ImportError:
                         if onerror:
                             onerror(modname)
diff --git a/Lib/runpy.py b/Lib/runpy.py
--- a/Lib/runpy.py
+++ b/Lib/runpy.py
@@ -58,7 +58,7 @@
         self.value = self._sentinel
         sys.argv[0] = self._saved_value
 
-# TODO: Replace these helpers with importlib._bootstrap._SpecMethods
+# TODO: Replace these helpers with importlib._bootstrap functions
 def _run_code(code, run_globals, init_globals=None,
               mod_name=None, mod_spec=None,
               pkg_name=None, script_name=None):
diff --git a/Lib/test/test_importlib/test_spec.py b/Lib/test/test_importlib/test_spec.py
--- a/Lib/test/test_importlib/test_spec.py
+++ b/Lib/test/test_importlib/test_spec.py
@@ -242,152 +242,14 @@
                                                   origin=self.path)
         self.loc_spec._set_fileattr = True
 
-    # init_module_attrs
-
-    def test_init_module_attrs(self):
-        module = type(sys)(self.name)
-        spec = self.machinery.ModuleSpec(self.name, self.loader)
-        self.bootstrap._SpecMethods(spec).init_module_attrs(module)
-
-        self.assertEqual(module.__name__, spec.name)
-        self.assertIs(module.__loader__, spec.loader)
-        self.assertEqual(module.__package__, spec.parent)
-        self.assertIs(module.__spec__, spec)
-        self.assertFalse(hasattr(module, '__path__'))
-        self.assertFalse(hasattr(module, '__file__'))
-        self.assertFalse(hasattr(module, '__cached__'))
-
-    def test_init_module_attrs_package(self):
-        module = type(sys)(self.name)
-        spec = self.machinery.ModuleSpec(self.name, self.loader)
-        spec.submodule_search_locations = ['spam', 'ham']
-        self.bootstrap._SpecMethods(spec).init_module_attrs(module)
-
-        self.assertEqual(module.__name__, spec.name)
-        self.assertIs(module.__loader__, spec.loader)
-        self.assertEqual(module.__package__, spec.parent)
-        self.assertIs(module.__spec__, spec)
-        self.assertIs(module.__path__, spec.submodule_search_locations)
-        self.assertFalse(hasattr(module, '__file__'))
-        self.assertFalse(hasattr(module, '__cached__'))
-
-    def test_init_module_attrs_location(self):
-        module = type(sys)(self.name)
-        spec = self.loc_spec
-        self.bootstrap._SpecMethods(spec).init_module_attrs(module)
-
-        self.assertEqual(module.__name__, spec.name)
-        self.assertIs(module.__loader__, spec.loader)
-        self.assertEqual(module.__package__, spec.parent)
-        self.assertIs(module.__spec__, spec)
-        self.assertFalse(hasattr(module, '__path__'))
-        self.assertEqual(module.__file__, spec.origin)
-        self.assertEqual(module.__cached__,
-                         self.util.cache_from_source(spec.origin))
-
-    def test_init_module_attrs_different_name(self):
-        module = type(sys)('eggs')
-        spec = self.machinery.ModuleSpec(self.name, self.loader)
-        self.bootstrap._SpecMethods(spec).init_module_attrs(module)
-
-        self.assertEqual(module.__name__, spec.name)
-
-    def test_init_module_attrs_different_spec(self):
-        module = type(sys)(self.name)
-        module.__spec__ = self.machinery.ModuleSpec('eggs', object())
-        spec = self.machinery.ModuleSpec(self.name, self.loader)
-        self.bootstrap._SpecMethods(spec).init_module_attrs(module)
-
-        self.assertEqual(module.__name__, spec.name)
-        self.assertIs(module.__loader__, spec.loader)
-        self.assertEqual(module.__package__, spec.parent)
-        self.assertIs(module.__spec__, spec)
-
-    def test_init_module_attrs_already_set(self):
-        module = type(sys)('ham.eggs')
-        module.__loader__ = object()
-        module.__package__ = 'ham'
-        module.__path__ = ['eggs']
-        module.__file__ = 'ham/eggs/__init__.py'
-        module.__cached__ = self.util.cache_from_source(module.__file__)
-        original = vars(module).copy()
-        spec = self.loc_spec
-        spec.submodule_search_locations = ['']
-        self.bootstrap._SpecMethods(spec).init_module_attrs(module)
-
-        self.assertIs(module.__loader__, original['__loader__'])
-        self.assertEqual(module.__package__, original['__package__'])
-        self.assertIs(module.__path__, original['__path__'])
-        self.assertEqual(module.__file__, original['__file__'])
-        self.assertEqual(module.__cached__, original['__cached__'])
-
-    def test_init_module_attrs_immutable(self):
-        module = object()
-        spec = self.loc_spec
-        spec.submodule_search_locations = ['']
-        self.bootstrap._SpecMethods(spec).init_module_attrs(module)
-
-        self.assertFalse(hasattr(module, '__name__'))
-        self.assertFalse(hasattr(module, '__loader__'))
-        self.assertFalse(hasattr(module, '__package__'))
-        self.assertFalse(hasattr(module, '__spec__'))
-        self.assertFalse(hasattr(module, '__path__'))
-        self.assertFalse(hasattr(module, '__file__'))
-        self.assertFalse(hasattr(module, '__cached__'))
-
-    # create()
-
-    def test_create(self):
-        created = self.bootstrap._SpecMethods(self.spec).create()
-
-        self.assertEqual(created.__name__, self.spec.name)
-        self.assertIs(created.__loader__, self.spec.loader)
-        self.assertEqual(created.__package__, self.spec.parent)
-        self.assertIs(created.__spec__, self.spec)
-        self.assertFalse(hasattr(created, '__path__'))
-        self.assertFalse(hasattr(created, '__file__'))
-        self.assertFalse(hasattr(created, '__cached__'))
-
-    def test_create_from_loader(self):
-        module = type(sys.implementation)()
-        class CreatingLoader(TestLoader):
-            def create_module(self, spec):
-                return module
-        self.spec.loader = CreatingLoader()
-        created = self.bootstrap._SpecMethods(self.spec).create()
-
-        self.assertIs(created, module)
-        self.assertEqual(created.__name__, self.spec.name)
-        self.assertIs(created.__loader__, self.spec.loader)
-        self.assertEqual(created.__package__, self.spec.parent)
-        self.assertIs(created.__spec__, self.spec)
-        self.assertFalse(hasattr(created, '__path__'))
-        self.assertFalse(hasattr(created, '__file__'))
-        self.assertFalse(hasattr(created, '__cached__'))
-
-    def test_create_from_loader_not_handled(self):
-        class CreatingLoader(TestLoader):
-            def create_module(self, spec):
-                return None
-        self.spec.loader = CreatingLoader()
-        created = self.bootstrap._SpecMethods(self.spec).create()
-
-        self.assertEqual(created.__name__, self.spec.name)
-        self.assertIs(created.__loader__, self.spec.loader)
-        self.assertEqual(created.__package__, self.spec.parent)
-        self.assertIs(created.__spec__, self.spec)
-        self.assertFalse(hasattr(created, '__path__'))
-        self.assertFalse(hasattr(created, '__file__'))
-        self.assertFalse(hasattr(created, '__cached__'))
-
     # exec()
 
     def test_exec(self):
         self.spec.loader = NewLoader()
-        module = self.bootstrap._SpecMethods(self.spec).create()
+        module = self.util.module_from_spec(self.spec)
         sys.modules[self.name] = module
         self.assertFalse(hasattr(module, 'eggs'))
-        self.bootstrap._SpecMethods(self.spec).exec(module)
+        self.bootstrap._exec(self.spec, module)
 
         self.assertEqual(module.eggs, 1)
 
@@ -396,7 +258,7 @@
     def test_load(self):
         self.spec.loader = NewLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded = self.bootstrap._load(self.spec)
             installed = sys.modules[self.spec.name]
 
         self.assertEqual(loaded.eggs, 1)
@@ -409,7 +271,7 @@
                 sys.modules[module.__name__] = replacement
         self.spec.loader = ReplacingLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded = self.bootstrap._load(self.spec)
             installed = sys.modules[self.spec.name]
 
         self.assertIs(loaded, replacement)
@@ -422,7 +284,7 @@
         self.spec.loader = FailedLoader()
         with CleanImport(self.spec.name):
             with self.assertRaises(RuntimeError):
-                loaded = self.bootstrap._SpecMethods(self.spec).load()
+                loaded = self.bootstrap._load(self.spec)
             self.assertNotIn(self.spec.name, sys.modules)
 
     def test_load_failed_removed(self):
@@ -433,20 +295,20 @@
         self.spec.loader = FailedLoader()
         with CleanImport(self.spec.name):
             with self.assertRaises(RuntimeError):
-                loaded = self.bootstrap._SpecMethods(self.spec).load()
+                loaded = self.bootstrap._load(self.spec)
             self.assertNotIn(self.spec.name, sys.modules)
 
     def test_load_legacy(self):
         self.spec.loader = LegacyLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded = self.bootstrap._load(self.spec)
 
         self.assertEqual(loaded.ham, -1)
 
     def test_load_legacy_attributes(self):
         self.spec.loader = LegacyLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded = self.bootstrap._load(self.spec)
 
         self.assertIs(loaded.__loader__, self.spec.loader)
         self.assertEqual(loaded.__package__, self.spec.parent)
@@ -460,7 +322,7 @@
                 return module
         self.spec.loader = ImmutableLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded = self.bootstrap._load(self.spec)
 
             self.assertIs(sys.modules[self.spec.name], module)
 
@@ -469,8 +331,8 @@
     def test_reload(self):
         self.spec.loader = NewLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
-            reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
+            loaded = self.bootstrap._load(self.spec)
+            reloaded = self.bootstrap._exec(self.spec, loaded)
             installed = sys.modules[self.spec.name]
 
         self.assertEqual(loaded.eggs, 1)
@@ -480,9 +342,9 @@
     def test_reload_modified(self):
         self.spec.loader = NewLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded = self.bootstrap._load(self.spec)
             loaded.eggs = 2
-            reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
+            reloaded = self.bootstrap._exec(self.spec, loaded)
 
         self.assertEqual(loaded.eggs, 1)
         self.assertIs(reloaded, loaded)
@@ -490,9 +352,9 @@
     def test_reload_extra_attributes(self):
         self.spec.loader = NewLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded = self.bootstrap._load(self.spec)
             loaded.available = False
-            reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
+            reloaded = self.bootstrap._exec(self.spec, loaded)
 
         self.assertFalse(loaded.available)
         self.assertIs(reloaded, loaded)
@@ -500,12 +362,12 @@
     def test_reload_init_module_attrs(self):
         self.spec.loader = NewLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded = self.bootstrap._load(self.spec)
             loaded.__name__ = 'ham'
             del loaded.__loader__
             del loaded.__package__
             del loaded.__spec__
-            self.bootstrap._SpecMethods(self.spec).exec(loaded)
+            self.bootstrap._exec(self.spec, loaded)
 
         self.assertEqual(loaded.__name__, self.spec.name)
         self.assertIs(loaded.__loader__, self.spec.loader)
@@ -518,8 +380,8 @@
     def test_reload_legacy(self):
         self.spec.loader = LegacyLoader()
         with CleanImport(self.spec.name):
-            loaded = self.bootstrap._SpecMethods(self.spec).load()
-            reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
+            loaded = self.bootstrap._load(self.spec)
+            reloaded = self.bootstrap._exec(self.spec, loaded)
             installed = sys.modules[self.spec.name]
 
         self.assertEqual(loaded.ham, -1)
@@ -778,13 +640,14 @@
     # spec_from_file_location()
 
     def test_spec_from_file_location_default(self):
-        if self.machinery is machinery['Source']:
-            raise unittest.SkipTest('not sure why this is breaking...')
         spec = self.util.spec_from_file_location(self.name, self.path)
 
         self.assertEqual(spec.name, self.name)
+        # Need to use a circuitous route to get at importlib.machinery to make
+        # sure the same class object is used in the isinstance() check as
+        # would have been used to create the loader.
         self.assertIsInstance(spec.loader,
-                              self.machinery.SourceFileLoader)
+                              self.util.abc.machinery.SourceFileLoader)
         self.assertEqual(spec.loader.name, self.name)
         self.assertEqual(spec.loader.path, self.path)
         self.assertEqual(spec.origin, self.path)
@@ -941,3 +804,7 @@
 (Frozen_FactoryTests,
  Source_FactoryTests
  ) = test_util.test_both(FactoryTests, util=util, machinery=machinery)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -1,8 +1,8 @@
-import importlib.util
-from . import util as test_util
-init = test_util.import_importlib('importlib')
-machinery = test_util.import_importlib('importlib.machinery')
-util = test_util.import_importlib('importlib.util')
+from . import util
+abc = util.import_importlib('importlib.abc')
+init = util.import_importlib('importlib')
+machinery = util.import_importlib('importlib.machinery')
+importlib_util = util.import_importlib('importlib.util')
 
 import os
 import sys
@@ -35,7 +35,85 @@
 
 (Frozen_DecodeSourceBytesTests,
  Source_DecodeSourceBytesTests
- ) = test_util.test_both(DecodeSourceBytesTests, util=util)
+ ) = util.test_both(DecodeSourceBytesTests, util=importlib_util)
+
+
+class ModuleFromSpecTests:
+
+    def test_no_create_module(self):
+        class Loader(self.abc.Loader):
+            pass
+        spec = self.machinery.ModuleSpec('test', Loader())
+        module = self.util.module_from_spec(spec)
+        self.assertIsInstance(module, types.ModuleType)
+        self.assertEqual(module.__name__, spec.name)
+
+    def test_create_module_returns_None(self):
+        class Loader(self.abc.Loader):
+            def create_module(self, spec):
+                return None
+        spec = self.machinery.ModuleSpec('test', Loader())
+        module = self.util.module_from_spec(spec)
+        self.assertIsInstance(module, types.ModuleType)
+        self.assertEqual(module.__name__, spec.name)
+
+    def test_create_module(self):
+        name = 'already set'
+        class CustomModule(types.ModuleType):
+            pass
+        class Loader(self.abc.Loader):
+            def create_module(self, spec):
+                module = CustomModule(spec.name)
+                module.__name__ = name
+                return module
+        spec = self.machinery.ModuleSpec('test', Loader())
+        module = self.util.module_from_spec(spec)
+        self.assertIsInstance(module, CustomModule)
+        self.assertEqual(module.__name__, name)
+
+    def test___name__(self):
+        spec = self.machinery.ModuleSpec('test', object())
+        module = self.util.module_from_spec(spec)
+        self.assertEqual(module.__name__, spec.name)
+
+    def test___spec__(self):
+        spec = self.machinery.ModuleSpec('test', object())
+        module = self.util.module_from_spec(spec)
+        self.assertEqual(module.__spec__, spec)
+
+    def test___loader__(self):
+        loader = object()
+        spec = self.machinery.ModuleSpec('test', loader)
+        module = self.util.module_from_spec(spec)
+        self.assertIs(module.__loader__, loader)
+
+    def test___package__(self):
+        spec = self.machinery.ModuleSpec('test.pkg', object())
+        module = self.util.module_from_spec(spec)
+        self.assertEqual(module.__package__, spec.parent)
+
+    def test___path__(self):
+        spec = self.machinery.ModuleSpec('test', object(), is_package=True)
+        module = self.util.module_from_spec(spec)
+        self.assertEqual(module.__path__, spec.submodule_search_locations)
+
+    def test___file__(self):
+        spec = self.machinery.ModuleSpec('test', object(), origin='some/path')
+        spec.has_location = True
+        module = self.util.module_from_spec(spec)
+        self.assertEqual(module.__file__, spec.origin)
+
+    def test___cached__(self):
+        spec = self.machinery.ModuleSpec('test', object())
+        spec.cached = 'some/path'
+        spec.has_location = True
+        module = self.util.module_from_spec(spec)
+        self.assertEqual(module.__cached__, spec.cached)
+
+(Frozen_ModuleFromSpecTests,
+ Source_ModuleFromSpecTests
+) = util.test_both(ModuleFromSpecTests, abc=abc, machinery=machinery,
+                   util=importlib_util)
 
 
 class ModuleForLoaderTests:
@@ -72,7 +150,7 @@
         # Test that when no module exists in sys.modules a new module is
         # created.
         module_name = 'a.b.c'
-        with test_util.uncache(module_name):
+        with util.uncache(module_name):
             module = self.return_module(module_name)
             self.assertIn(module_name, sys.modules)
         self.assertIsInstance(module, types.ModuleType)
@@ -90,7 +168,7 @@
         module = types.ModuleType('a.b.c')
         module.__loader__ = 42
         module.__package__ = 42
-        with test_util.uncache(name):
+        with util.uncache(name):
             sys.modules[name] = module
             loader = FakeLoader()
             returned_module = loader.load_module(name)
@@ -102,7 +180,7 @@
         # Test that a module is removed from sys.modules if added but an
         # exception is raised.
         name = 'a.b.c'
-        with test_util.uncache(name):
+        with util.uncache(name):
             self.raise_exception(name)
             self.assertNotIn(name, sys.modules)
 
@@ -110,7 +188,7 @@
         # Test that a failure on reload leaves the module in-place.
         name = 'a.b.c'
         module = types.ModuleType(name)
-        with test_util.uncache(name):
+        with util.uncache(name):
             sys.modules[name] = module
             self.raise_exception(name)
             self.assertIs(module, sys.modules[name])
@@ -129,7 +207,7 @@
 
         name = 'mod'
         module = FalseModule(name)
-        with test_util.uncache(name):
+        with util.uncache(name):
             self.assertFalse(module)
             sys.modules[name] = module
             given = self.return_module(name)
@@ -148,7 +226,7 @@
                 return module
 
         name = 'pkg.mod'
-        with test_util.uncache(name):
+        with util.uncache(name):
             loader = FakeLoader(False)
             module = loader.load_module(name)
             self.assertEqual(module.__name__, name)
@@ -156,7 +234,7 @@
             self.assertEqual(module.__package__, 'pkg')
 
         name = 'pkg.sub'
-        with test_util.uncache(name):
+        with util.uncache(name):
             loader = FakeLoader(True)
             module = loader.load_module(name)
             self.assertEqual(module.__name__, name)
@@ -166,7 +244,7 @@
 
 (Frozen_ModuleForLoaderTests,
  Source_ModuleForLoaderTests
- ) = test_util.test_both(ModuleForLoaderTests, util=util)
+ ) = util.test_both(ModuleForLoaderTests, util=importlib_util)
 
 
 class SetPackageTests:
@@ -229,7 +307,7 @@
 
 (Frozen_SetPackageTests,
  Source_SetPackageTests
- ) = test_util.test_both(SetPackageTests, util=util)
+ ) = util.test_both(SetPackageTests, util=importlib_util)
 
 
 class SetLoaderTests:
@@ -276,7 +354,7 @@
 
 (Frozen_SetLoaderTests,
  Source_SetLoaderTests
- ) = test_util.test_both(SetLoaderTests, util=util)
+ ) = util.test_both(SetLoaderTests, util=importlib_util)
 
 
 class ResolveNameTests:
@@ -314,7 +392,7 @@
 
 (Frozen_ResolveNameTests,
  Source_ResolveNameTests
- ) = test_util.test_both(ResolveNameTests, util=util)
+ ) = util.test_both(ResolveNameTests, util=importlib_util)
 
 
 class FindSpecTests:
@@ -325,7 +403,7 @@
 
     def test_sys_modules(self):
         name = 'some_mod'
-        with test_util.uncache(name):
+        with util.uncache(name):
             module = types.ModuleType(name)
             loader = 'a loader!'
             spec = self.machinery.ModuleSpec(name, loader)
@@ -337,7 +415,7 @@
 
     def test_sys_modules_without___loader__(self):
         name = 'some_mod'
-        with test_util.uncache(name):
+        with util.uncache(name):
             module = types.ModuleType(name)
             del module.__loader__
             loader = 'a loader!'
@@ -349,7 +427,7 @@
 
     def test_sys_modules_spec_is_None(self):
         name = 'some_mod'
-        with test_util.uncache(name):
+        with util.uncache(name):
             module = types.ModuleType(name)
             module.__spec__ = None
             sys.modules[name] = module
@@ -358,7 +436,7 @@
 
     def test_sys_modules_loader_is_None(self):
         name = 'some_mod'
-        with test_util.uncache(name):
+        with util.uncache(name):
             module = types.ModuleType(name)
             spec = self.machinery.ModuleSpec(name, None)
             module.__spec__ = spec
@@ -368,7 +446,7 @@
 
     def test_sys_modules_spec_is_not_set(self):
         name = 'some_mod'
-        with test_util.uncache(name):
+        with util.uncache(name):
             module = types.ModuleType(name)
             try:
                 del module.__spec__
@@ -380,8 +458,8 @@
 
     def test_success(self):
         name = 'some_mod'
-        with test_util.uncache(name):
-            with test_util.import_state(meta_path=[self.FakeMetaFinder]):
+        with util.uncache(name):
+            with util.import_state(meta_path=[self.FakeMetaFinder]):
                 self.assertEqual((name, None, None),
                                  self.util.find_spec(name))
 
@@ -389,8 +467,8 @@
 #        # Searching on a path should work.
 #        name = 'some_mod'
 #        path = 'path to some place'
-#        with test_util.uncache(name):
-#            with test_util.import_state(meta_path=[self.FakeMetaFinder]):
+#        with util.uncache(name):
+#            with util.import_state(meta_path=[self.FakeMetaFinder]):
 #                self.assertEqual((name, path, None),
 #                                 self.util.find_spec(name, path))
 
@@ -401,8 +479,8 @@
     def test_find_submodule(self):
         name = 'spam'
         subname = 'ham'
-        with test_util.temp_module(name, pkg=True) as pkg_dir:
-            fullname, _ = test_util.submodule(name, subname, pkg_dir)
+        with util.temp_module(name, pkg=True) as pkg_dir:
+            fullname, _ = util.submodule(name, subname, pkg_dir)
             spec = self.util.find_spec(fullname)
             self.assertIsNot(spec, None)
             self.assertIn(name, sorted(sys.modules))
@@ -414,9 +492,9 @@
     def test_find_submodule_parent_already_imported(self):
         name = 'spam'
         subname = 'ham'
-        with test_util.temp_module(name, pkg=True) as pkg_dir:
+        with util.temp_module(name, pkg=True) as pkg_dir:
             self.init.import_module(name)
-            fullname, _ = test_util.submodule(name, subname, pkg_dir)
+            fullname, _ = util.submodule(name, subname, pkg_dir)
             spec = self.util.find_spec(fullname)
             self.assertIsNot(spec, None)
             self.assertIn(name, sorted(sys.modules))
@@ -428,8 +506,8 @@
     def test_find_relative_module(self):
         name = 'spam'
         subname = 'ham'
-        with test_util.temp_module(name, pkg=True) as pkg_dir:
-            fullname, _ = test_util.submodule(name, subname, pkg_dir)
+        with util.temp_module(name, pkg=True) as pkg_dir:
+            fullname, _ = util.submodule(name, subname, pkg_dir)
             relname = '.' + subname
             spec = self.util.find_spec(relname, name)
             self.assertIsNot(spec, None)
@@ -442,8 +520,8 @@
     def test_find_relative_module_missing_package(self):
         name = 'spam'
         subname = 'ham'
-        with test_util.temp_module(name, pkg=True) as pkg_dir:
-            fullname, _ = test_util.submodule(name, subname, pkg_dir)
+        with util.temp_module(name, pkg=True) as pkg_dir:
+            fullname, _ = util.submodule(name, subname, pkg_dir)
             relname = '.' + subname
             with self.assertRaises(ValueError):
                 self.util.find_spec(relname)
@@ -453,7 +531,7 @@
 
 (Frozen_FindSpecTests,
  Source_FindSpecTests
- ) = test_util.test_both(FindSpecTests, init=init, util=util,
+ ) = util.test_both(FindSpecTests, init=init, util=importlib_util,
                          machinery=machinery)
 
 
@@ -470,7 +548,7 @@
 
 (Frozen_MagicNumberTests,
  Source_MagicNumberTests
- ) = test_util.test_both(MagicNumberTests, util=util)
+ ) = util.test_both(MagicNumberTests, util=importlib_util)
 
 
 class PEP3147Tests:
@@ -588,7 +666,7 @@
 
 (Frozen_PEP3147Tests,
  Source_PEP3147Tests
- ) = test_util.test_both(PEP3147Tests, util=util)
+ ) = util.test_both(PEP3147Tests, util=importlib_util)
 
 
 if __name__ == '__main__':
diff --git a/Lib/test/test_tools.py b/Lib/test/test_tools.py
--- a/Lib/test/test_tools.py
+++ b/Lib/test/test_tools.py
@@ -407,7 +407,7 @@
     def setUpClass(self):
         path = os.path.join(scriptsdir, 'pdeps.py')
         spec = importlib.util.spec_from_file_location('pdeps', path)
-        self.pdeps = importlib._bootstrap._SpecMethods(spec).load()
+        self.pdeps = importlib._bootstrap._load(spec)
 
     @classmethod
     def tearDownClass(self):
@@ -432,7 +432,7 @@
     def setUp(self):
         path = os.path.join(scriptsdir, 'gprof2html.py')
         spec = importlib.util.spec_from_file_location('gprof2html', path)
-        self.gprof = importlib._bootstrap._SpecMethods(spec).load()
+        self.gprof = importlib._bootstrap._load(spec)
         oldargv = sys.argv
         def fixup():
             sys.argv = oldargv
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -89,6 +89,9 @@
 Library
 -------
 
+- Issue #20383: Introduce importlib.util.module_from_spec() as the preferred way
+  to create a new module.
+
 - Issue #21552: Fixed possible integer overflow of too long string lengths in
   the tkinter module on 64-bit platforms.
 
diff --git a/Python/importlib.h b/Python/importlib.h
--- a/Python/importlib.h
+++ b/Python/importlib.h
[stripped]
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -342,7 +342,7 @@
         spec = importlib.util.spec_from_file_location(ext.name, ext_filename,
                                                       loader=loader)
         try:
-            importlib._bootstrap._SpecMethods(spec).load()
+            importlib._bootstrap._load(spec)
         except ImportError as why:
             self.failed_on_import.append(ext.name)
             self.announce('*** WARNING: renaming "%s" since importing it'

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


More information about the Python-checkins mailing list