Implement PEP 451 (ModuleSpec).
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 4c2aec3..5aa4b0a 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -2259,7 +2259,7 @@
         minstance.b = 2
         minstance.a = 1
         default_attributes = ['__name__', '__doc__', '__package__',
-                              '__loader__']
+                              '__loader__', '__spec__']
         names = [x for x in dir(minstance) if x not in default_attributes]
         self.assertEqual(names, ['a', 'b'])
 
diff --git a/Lib/test/test_frozen.py b/Lib/test/test_frozen.py
deleted file mode 100644
index 4c50cb7..0000000
--- a/Lib/test/test_frozen.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Test the frozen module defined in frozen.c.
-
-from test.support import captured_stdout, run_unittest
-import unittest
-import sys
-
-class FrozenTests(unittest.TestCase):
-
-    module_attrs = frozenset(['__builtins__', '__cached__', '__doc__',
-                              '__loader__', '__name__',
-                              '__package__'])
-    package_attrs = frozenset(list(module_attrs) + ['__path__'])
-
-    def test_frozen(self):
-        with captured_stdout() as stdout:
-            try:
-                import __hello__
-            except ImportError as x:
-                self.fail("import __hello__ failed:" + str(x))
-            self.assertEqual(__hello__.initialized, True)
-            expect = set(self.module_attrs)
-            expect.add('initialized')
-            self.assertEqual(set(dir(__hello__)), expect)
-            self.assertEqual(stdout.getvalue(), 'Hello world!\n')
-
-        with captured_stdout() as stdout:
-            try:
-                import __phello__
-            except ImportError as x:
-                self.fail("import __phello__ failed:" + str(x))
-            self.assertEqual(__phello__.initialized, True)
-            expect = set(self.package_attrs)
-            expect.add('initialized')
-            if not "__phello__.spam" in sys.modules:
-                self.assertEqual(set(dir(__phello__)), expect)
-            else:
-                expect.add('spam')
-                self.assertEqual(set(dir(__phello__)), expect)
-            self.assertEqual(__phello__.__path__, [])
-            self.assertEqual(stdout.getvalue(), 'Hello world!\n')
-
-        with captured_stdout() as stdout:
-            try:
-                import __phello__.spam
-            except ImportError as x:
-                self.fail("import __phello__.spam failed:" + str(x))
-            self.assertEqual(__phello__.spam.initialized, True)
-            spam_expect = set(self.module_attrs)
-            spam_expect.add('initialized')
-            self.assertEqual(set(dir(__phello__.spam)), spam_expect)
-            phello_expect = set(self.package_attrs)
-            phello_expect.add('initialized')
-            phello_expect.add('spam')
-            self.assertEqual(set(dir(__phello__)), phello_expect)
-            self.assertEqual(stdout.getvalue(), 'Hello world!\n')
-
-        try:
-            import __phello__.foo
-        except ImportError:
-            pass
-        else:
-            self.fail("import __phello__.foo should have failed")
-
-            try:
-                import __phello__.foo
-            except ImportError:
-                pass
-            else:
-                self.fail("import __phello__.foo should have failed")
-
-        del sys.modules['__hello__']
-        del sys.modules['__phello__']
-        del sys.modules['__phello__.spam']
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py
index ae8e160..bda1541 100644
--- a/Lib/test/test_import.py
+++ b/Lib/test/test_import.py
@@ -1036,11 +1036,14 @@
         # away from the traceback.
         self.create_module("foo", "")
         importlib = sys.modules['_frozen_importlib']
-        old_load_module = importlib.SourceLoader.load_module
+        if 'load_module' in vars(importlib.SourceLoader):
+            old_exec_module = importlib.SourceLoader.exec_module
+        else:
+            old_exec_module = None
         try:
-            def load_module(*args):
+            def exec_module(*args):
                 1/0
-            importlib.SourceLoader.load_module = load_module
+            importlib.SourceLoader.exec_module = exec_module
             try:
                 import foo
             except ZeroDivisionError as e:
@@ -1049,7 +1052,10 @@
                 self.fail("ZeroDivisionError should have been raised")
             self.assert_traceback(tb, [__file__, '<frozen importlib', __file__])
         finally:
-            importlib.SourceLoader.load_module = old_load_module
+            if old_exec_module is None:
+                del importlib.SourceLoader.exec_module
+            else:
+                importlib.SourceLoader.exec_module = old_exec_module
 
     @unittest.skipUnless(TESTFN_UNENCODABLE, 'need TESTFN_UNENCODABLE')
     def test_unencodable_filename(self):
diff --git a/Lib/test/test_importlib/abc.py b/Lib/test/test_importlib/abc.py
index b9ff344..2070dad 100644
--- a/Lib/test/test_importlib/abc.py
+++ b/Lib/test/test_importlib/abc.py
@@ -81,11 +81,6 @@
         pass
 
     @abc.abstractmethod
-    def test_module_reuse(self):
-        """If a module is already in sys.modules, it should be reused."""
-        pass
-
-    @abc.abstractmethod
     def test_state_after_failure(self):
         """If a module is already in sys.modules and a reload fails
         (e.g. a SyntaxError), the module should be in the state it was before
diff --git a/Lib/test/test_importlib/builtin/test_finder.py b/Lib/test/test_importlib/builtin/test_finder.py
index aa1ea42..934562f 100644
--- a/Lib/test/test_importlib/builtin/test_finder.py
+++ b/Lib/test/test_importlib/builtin/test_finder.py
@@ -8,6 +8,46 @@
 import unittest
 
 
+class FindSpecTests(abc.FinderTests):
+
+    """Test find_spec() for built-in modules."""
+
+    def test_module(self):
+        # Common case.
+        with util.uncache(builtin_util.NAME):
+            found = self.machinery.BuiltinImporter.find_spec(builtin_util.NAME)
+            self.assertTrue(found)
+            self.assertEqual(found.origin, 'built-in')
+
+    # Built-in modules cannot be a package.
+    test_package = None
+
+    # Built-in modules cannobt be in a package.
+    test_module_in_package = None
+
+    # Built-in modules cannot be a package.
+    test_package_in_package = None
+
+    # Built-in modules cannot be a package.
+    test_package_over_module = None
+
+    def test_failure(self):
+        name = 'importlib'
+        assert name not in sys.builtin_module_names
+        spec = self.machinery.BuiltinImporter.find_spec(name)
+        self.assertIsNone(spec)
+
+    def test_ignore_path(self):
+        # The value for 'path' should always trigger a failed import.
+        with util.uncache(builtin_util.NAME):
+            spec = self.machinery.BuiltinImporter.find_spec(builtin_util.NAME,
+                                                            ['pkg'])
+            self.assertIsNone(spec)
+
+Frozen_FindSpecTests, Source_FindSpecTests = util.test_both(FindSpecTests,
+        machinery=[frozen_machinery, source_machinery])
+
+
 class FinderTests(abc.FinderTests):
 
     """Test find_module() for built-in modules."""
@@ -17,22 +57,13 @@
         with util.uncache(builtin_util.NAME):
             found = self.machinery.BuiltinImporter.find_module(builtin_util.NAME)
             self.assertTrue(found)
+            self.assertTrue(hasattr(found, 'load_module'))
 
-    def test_package(self):
-        # Built-in modules cannot be a package.
-        pass
+    # Built-in modules cannot be a package.
+    test_package = test_package_in_package = test_package_over_module = None
 
-    def test_module_in_package(self):
-        # Built-in modules cannobt be in a package.
-        pass
-
-    def test_package_in_package(self):
-        # Built-in modules cannot be a package.
-        pass
-
-    def test_package_over_module(self):
-        # Built-in modules cannot be a package.
-        pass
+    # Built-in modules cannot be in a package.
+    test_module_in_package = None
 
     def test_failure(self):
         assert 'importlib' not in sys.builtin_module_names
diff --git a/Lib/test/test_importlib/builtin/test_loader.py b/Lib/test/test_importlib/builtin/test_loader.py
index 4ee8dc4..200188f 100644
--- a/Lib/test/test_importlib/builtin/test_loader.py
+++ b/Lib/test/test_importlib/builtin/test_loader.py
@@ -9,6 +9,70 @@
 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.
+        assert hasattr(unittest, '__file__')  # Not a built-in.
+        with self.assertRaises(ImportError) as cm:
+            self.load_spec('unittest')
+        self.assertEqual(cm.exception.name, 'unittest')
+
+
+Frozen_ExecModTests, Source_ExecModTests = util.test_both(ExecModTests,
+        machinery=[frozen_machinery, source_machinery])
+
+
 class LoaderTests(abc.LoaderTests):
 
     """Test load_module() for built-in modules."""
@@ -33,17 +97,11 @@
             module = self.load_module(builtin_util.NAME)
             self.verify(module)
 
-    def test_package(self):
-        # Built-in modules cannot be a package.
-        pass
+    # Built-in modules cannot be a package.
+    test_package = test_lacking_parent = None
 
-    def test_lacking_parent(self):
-        # Built-in modules cannot be a package.
-        pass
-
-    def test_state_after_failure(self):
-        # Not way to force an imoprt failure.
-        pass
+    # No way to force an import failure.
+    test_state_after_failure = None
 
     def test_module_reuse(self):
         # Test that the same module is used in a reload.
diff --git a/Lib/test/test_importlib/extension/test_case_sensitivity.py b/Lib/test/test_importlib/extension/test_case_sensitivity.py
index bb74321..bb2528e 100644
--- a/Lib/test/test_importlib/extension/test_case_sensitivity.py
+++ b/Lib/test/test_importlib/extension/test_case_sensitivity.py
@@ -9,6 +9,8 @@
 frozen_machinery, source_machinery = util.import_importlib('importlib.machinery')
 
 
+# XXX find_spec tests
+
 @unittest.skipIf(ext_util.FILENAME is None, '_testcapi not available')
 @util.case_insensitive_tests
 class ExtensionModuleCaseSensitivityTest:
diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py
index 10e78cc..d0c8bb0 100644
--- a/Lib/test/test_importlib/extension/test_finder.py
+++ b/Lib/test/test_importlib/extension/test_finder.py
@@ -6,6 +6,7 @@
 
 import unittest
 
+# XXX find_spec tests
 
 class FinderTests(abc.FinderTests):
 
@@ -20,21 +21,14 @@
     def test_module(self):
         self.assertTrue(self.find_module(util.NAME))
 
-    def test_package(self):
-        # No extension module as an __init__ available for testing.
-        pass
+    # No extension module as an __init__ available for testing.
+    test_package = test_package_in_package = None
 
-    def test_module_in_package(self):
-        # No extension module in a package available for testing.
-        pass
+    # No extension module in a package available for testing.
+    test_module_in_package = None
 
-    def test_package_in_package(self):
-        # No extension module as an __init__ available for testing.
-        pass
-
-    def test_package_over_module(self):
-        # Extension modules cannot be an __init__ for a package.
-        pass
+    # Extension modules cannot be an __init__ for a package.
+    test_package_over_module = None
 
     def test_failure(self):
         self.assertIsNone(self.find_module('asdfjkl;'))
diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py
index 1e8afba..58bd09f 100644
--- a/Lib/test/test_importlib/extension/test_loader.py
+++ b/Lib/test/test_importlib/extension/test_loader.py
@@ -6,9 +6,75 @@
 
 import os.path
 import sys
+import types
 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."""
@@ -39,13 +105,11 @@
             self.assertIsInstance(module.__loader__,
                                   self.machinery.ExtensionFileLoader)
 
-    def test_package(self):
-        # No extension module as __init__ available for testing.
-        pass
+    # No extension module as __init__ available for testing.
+    test_package = None
 
-    def test_lacking_parent(self):
-        # No extension module in a package available for testing.
-        pass
+    # No extension module in a package available for testing.
+    test_lacking_parent = None
 
     def test_module_reuse(self):
         with util.uncache(ext_util.NAME):
@@ -53,9 +117,8 @@
             module2 = self.load_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
+    # No easy way to trigger a failure after a successful import.
+    test_state_after_failure = None
 
     def test_unloadable(self):
         name = 'asdfjkl;'
diff --git a/Lib/test/test_importlib/frozen/test_finder.py b/Lib/test/test_importlib/frozen/test_finder.py
index 9e629bf..f9f97f3 100644
--- a/Lib/test/test_importlib/frozen/test_finder.py
+++ b/Lib/test/test_importlib/frozen/test_finder.py
@@ -6,6 +6,41 @@
 import unittest
 
 
+class FindSpecTests(abc.FinderTests):
+
+    """Test finding frozen modules."""
+
+    def find(self, name, path=None):
+        finder = self.machinery.FrozenImporter
+        return finder.find_spec(name, path)
+
+    def test_module(self):
+        name = '__hello__'
+        spec = self.find(name)
+        self.assertEqual(spec.origin, 'frozen')
+
+    def test_package(self):
+        spec = self.find('__phello__')
+        self.assertIsNotNone(spec)
+
+    def test_module_in_package(self):
+        spec = self.find('__phello__.spam', ['__phello__'])
+        self.assertIsNotNone(spec)
+
+    # No frozen package within another package to test with.
+    test_package_in_package = None
+
+    # No easy way to test.
+    test_package_over_module = None
+
+    def test_failure(self):
+        spec = self.find('<not real>')
+        self.assertIsNone(spec)
+
+Frozen_FindSpecTests, Source_FindSpecTests = util.test_both(FindSpecTests,
+                                                            machinery=machinery)
+
+
 class FinderTests(abc.FinderTests):
 
     """Test finding frozen modules."""
@@ -27,13 +62,11 @@
         loader = self.find('__phello__.spam', ['__phello__'])
         self.assertTrue(hasattr(loader, 'load_module'))
 
-    def test_package_in_package(self):
-        # No frozen package within another package to test with.
-        pass
+    # No frozen package within another package to test with.
+    test_package_in_package = None
 
-    def test_package_over_module(self):
-        # No easy way to test.
-        pass
+    # No easy way to test.
+    test_package_over_module = None
 
     def test_failure(self):
         loader = self.find('<not real>')
diff --git a/Lib/test/test_importlib/frozen/test_loader.py b/Lib/test/test_importlib/frozen/test_loader.py
index be8dc3c..2abbe92 100644
--- a/Lib/test/test_importlib/frozen/test_loader.py
+++ b/Lib/test/test_importlib/frozen/test_loader.py
@@ -3,9 +3,81 @@
 
 machinery = util.import_importlib('importlib.machinery')
 
-import unittest
+
+import sys
 from test.support import captured_stdout
 import types
+import unittest
+
+
+class ExecModuleTests(abc.LoaderTests):
+
+    def exec_module(self, name):
+        with util.uncache(name), captured_stdout() as stdout:
+            spec = self.machinery.ModuleSpec(
+                    name, self.machinery.FrozenImporter, origin='frozen',
+                    is_package=self.machinery.FrozenImporter.is_package(name))
+            module = types.ModuleType(name)
+            module.__spec__ = spec
+            assert not hasattr(module, 'initialized')
+            self.machinery.FrozenImporter.exec_module(module)
+            self.assertTrue(module.initialized)
+            self.assertTrue(hasattr(module, '__spec__'))
+            self.assertEqual(module.__spec__.origin, 'frozen')
+            return module, stdout.getvalue()
+
+    def test_module(self):
+        name = '__hello__'
+        module, output = self.exec_module(name)
+        check = {'__name__': name}
+        for attr, value in check.items():
+            self.assertEqual(getattr(module, attr), value)
+        self.assertEqual(output, 'Hello world!\n')
+        self.assertTrue(hasattr(module, '__spec__'))
+
+    def test_package(self):
+        name = '__phello__'
+        module, output = self.exec_module(name)
+        check = {'__name__': name}
+        for attr, value in check.items():
+            attr_value = getattr(module, attr)
+            self.assertEqual(attr_value, value,
+                        'for {name}.{attr}, {given!r} != {expected!r}'.format(
+                                 name=name, attr=attr, given=attr_value,
+                                 expected=value))
+        self.assertEqual(output, 'Hello world!\n')
+
+    def test_lacking_parent(self):
+        name = '__phello__.spam'
+        with util.uncache('__phello__'):
+            module, output = self.exec_module(name)
+            check = {'__name__': name}
+            for attr, value in check.items():
+                attr_value = getattr(module, attr)
+                self.assertEqual(attr_value, value,
+                        'for {name}.{attr}, {given} != {expected!r}'.format(
+                                 name=name, attr=attr, given=attr_value,
+                                 expected=value))
+            self.assertEqual(output, 'Hello world!\n')
+
+
+    def test_module_repr(self):
+        name = '__hello__'
+        module, output = self.exec_module(name)
+        self.assertEqual(repr(module),
+                         "<module '__hello__' (frozen)>")
+
+    # No way to trigger an error in a frozen module.
+    test_state_after_failure = None
+
+    def test_unloadable(self):
+        assert self.machinery.FrozenImporter.find_module('_not_real') is None
+        with self.assertRaises(ImportError) as cm:
+            self.exec_module('_not_real')
+        self.assertEqual(cm.exception.name, '_not_real')
+
+Frozen_ExecModuleTests, Source_ExecModuleTests = util.test_both(ExecModuleTests,
+                                                        machinery=machinery)
 
 
 class LoaderTests(abc.LoaderTests):
@@ -68,9 +140,8 @@
             self.assertEqual(repr(module),
                              "<module '__hello__' (frozen)>")
 
-    def test_state_after_failure(self):
-        # No way to trigger an error in a frozen module.
-        pass
+    # No way to trigger an error in a frozen module.
+    test_state_after_failure = None
 
     def test_unloadable(self):
         assert self.machinery.FrozenImporter.find_module('_not_real') is None
diff --git a/Lib/test/test_importlib/import_/test_meta_path.py b/Lib/test/test_importlib/import_/test_meta_path.py
index 74afd1b..a56253d 100644
--- a/Lib/test/test_importlib/import_/test_meta_path.py
+++ b/Lib/test/test_importlib/import_/test_meta_path.py
@@ -46,8 +46,8 @@
         with util.import_state(meta_path=[]):
             with warnings.catch_warnings(record=True) as w:
                 warnings.simplefilter('always')
-                self.assertIsNone(importlib._bootstrap._find_module('nothing',
-                                                                    None))
+                self.assertIsNone(importlib._bootstrap._find_spec('nothing',
+                                                                  None))
                 self.assertEqual(len(w), 1)
                 self.assertTrue(issubclass(w[-1].category, ImportWarning))
 
diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py
index 979a481..ba8d605 100644
--- a/Lib/test/test_importlib/test_abc.py
+++ b/Lib/test/test_importlib/test_abc.py
@@ -284,22 +284,6 @@
 tests = make_return_value_tests(ExecutionLoader, InspectLoaderDefaultsTests)
 Frozen_ELDefaultTests, Source_ELDefaultsTests = tests
 
-##### Loader concrete methods ##################################################
-class LoaderConcreteMethodTests:
-
-    def test_init_module_attrs(self):
-        loader = self.LoaderSubclass()
-        module = types.ModuleType('blah')
-        loader.init_module_attrs(module)
-        self.assertEqual(module.__loader__, loader)
-
-
-class Frozen_LoaderConcreateMethodTests(LoaderConcreteMethodTests, unittest.TestCase):
-    LoaderSubclass = Frozen_L
-
-class Source_LoaderConcreateMethodTests(LoaderConcreteMethodTests, unittest.TestCase):
-    LoaderSubclass = Source_L
-
 
 ##### InspectLoader concrete methods ###########################################
 class InspectLoaderSourceToCodeTests:
@@ -385,60 +369,6 @@
     InspectLoaderSubclass = Source_IL
 
 
-class InspectLoaderInitModuleTests:
-
-    def mock_is_package(self, return_value):
-        return mock.patch.object(self.InspectLoaderSubclass, 'is_package',
-                                 return_value=return_value)
-
-    def init_module_attrs(self, name):
-        loader = self.InspectLoaderSubclass()
-        module = types.ModuleType(name)
-        loader.init_module_attrs(module)
-        self.assertEqual(module.__loader__, loader)
-        return module
-
-    def test_package(self):
-        # If a package, then __package__ == __name__, __path__ == []
-        with self.mock_is_package(True):
-            name = 'blah'
-            module = self.init_module_attrs(name)
-            self.assertEqual(module.__package__, name)
-            self.assertEqual(module.__path__, [])
-
-    def test_toplevel(self):
-        # If a module is top-level, __package__ == ''
-        with self.mock_is_package(False):
-            name = 'blah'
-            module = self.init_module_attrs(name)
-            self.assertEqual(module.__package__, '')
-
-    def test_submodule(self):
-        # If a module is contained within a package then set __package__ to the
-        # package name.
-        with self.mock_is_package(False):
-            name = 'pkg.mod'
-            module = self.init_module_attrs(name)
-            self.assertEqual(module.__package__, 'pkg')
-
-    def test_is_package_ImportError(self):
-        # If is_package() raises ImportError, __package__ should be None and
-        # __path__ should not be set.
-        with self.mock_is_package(False) as mocked_method:
-            mocked_method.side_effect = ImportError
-            name = 'mod'
-            module = self.init_module_attrs(name)
-            self.assertIsNone(module.__package__)
-            self.assertFalse(hasattr(module, '__path__'))
-
-
-class Frozen_ILInitModuleTests(InspectLoaderInitModuleTests, unittest.TestCase):
-    InspectLoaderSubclass = Frozen_IL
-
-class Source_ILInitModuleTests(InspectLoaderInitModuleTests, unittest.TestCase):
-    InspectLoaderSubclass = Source_IL
-
-
 class InspectLoaderLoadModuleTests:
 
     """Test InspectLoader.load_module()."""
@@ -550,80 +480,6 @@
     ExecutionLoaderSubclass = Source_EL
 
 
-class ExecutionLoaderInitModuleTests:
-
-    def mock_is_package(self, return_value):
-        return mock.patch.object(self.ExecutionLoaderSubclass, 'is_package',
-                                 return_value=return_value)
-
-    @contextlib.contextmanager
-    def mock_methods(self, is_package, filename):
-        is_package_manager = self.mock_is_package(is_package)
-        get_filename_manager = mock.patch.object(self.ExecutionLoaderSubclass,
-                'get_filename', return_value=filename)
-        with is_package_manager as mock_is_package:
-            with get_filename_manager as mock_get_filename:
-                yield {'is_package': mock_is_package,
-                       'get_filename': mock_get_filename}
-
-    def test_toplevel(self):
-        # Verify __loader__, __file__, and __package__; no __path__.
-        name = 'blah'
-        path = os.path.join('some', 'path', '{}.py'.format(name))
-        with self.mock_methods(False, path):
-            loader = self.ExecutionLoaderSubclass()
-            module = types.ModuleType(name)
-            loader.init_module_attrs(module)
-        self.assertIs(module.__loader__, loader)
-        self.assertEqual(module.__file__, path)
-        self.assertEqual(module.__package__, '')
-        self.assertFalse(hasattr(module, '__path__'))
-
-    def test_package(self):
-        # Verify __loader__, __file__, __package__, and __path__.
-        name = 'pkg'
-        path = os.path.join('some', 'pkg', '__init__.py')
-        with self.mock_methods(True, path):
-            loader = self.ExecutionLoaderSubclass()
-            module = types.ModuleType(name)
-            loader.init_module_attrs(module)
-        self.assertIs(module.__loader__, loader)
-        self.assertEqual(module.__file__, path)
-        self.assertEqual(module.__package__, 'pkg')
-        self.assertEqual(module.__path__, [os.path.dirname(path)])
-
-    def test_submodule(self):
-        # Verify __package__ and not __path__; test_toplevel() takes care of
-        # other attributes.
-        name = 'pkg.submodule'
-        path = os.path.join('some', 'pkg', 'submodule.py')
-        with self.mock_methods(False, path):
-            loader = self.ExecutionLoaderSubclass()
-            module = types.ModuleType(name)
-            loader.init_module_attrs(module)
-        self.assertEqual(module.__package__, 'pkg')
-        self.assertEqual(module.__file__, path)
-        self.assertFalse(hasattr(module, '__path__'))
-
-    def test_get_filename_ImportError(self):
-        # If get_filename() raises ImportError, don't set __file__.
-        name = 'blah'
-        path = 'blah.py'
-        with self.mock_methods(False, path) as mocked_methods:
-            mocked_methods['get_filename'].side_effect = ImportError
-            loader = self.ExecutionLoaderSubclass()
-            module = types.ModuleType(name)
-            loader.init_module_attrs(module)
-        self.assertFalse(hasattr(module, '__file__'))
-
-
-class Frozen_ELInitModuleTests(ExecutionLoaderInitModuleTests, unittest.TestCase):
-    ExecutionLoaderSubclass = Frozen_EL
-
-class Source_ELInitModuleTests(ExecutionLoaderInitModuleTests, unittest.TestCase):
-    ExecutionLoaderSubclass = Source_EL
-
-
 ##### SourceLoader concrete methods ############################################
 class SourceLoader:
 
@@ -952,58 +808,5 @@
     SourceOnlyLoaderMock = Source_SourceOnlyL
 
 
-class SourceLoaderInitModuleAttrTests:
-
-    """Tests for importlib.abc.SourceLoader.init_module_attrs()."""
-
-    def test_init_module_attrs(self):
-        # If __file__ set, __cached__ == importlib.util.cached_from_source(__file__).
-        name = 'blah'
-        path = 'blah.py'
-        loader = self.SourceOnlyLoaderMock(path)
-        module = types.ModuleType(name)
-        loader.init_module_attrs(module)
-        self.assertEqual(module.__loader__, loader)
-        self.assertEqual(module.__package__, '')
-        self.assertEqual(module.__file__, path)
-        self.assertEqual(module.__cached__, self.util.cache_from_source(path))
-
-    def test_no_get_filename(self):
-        # No __file__, no __cached__.
-        with mock.patch.object(self.SourceOnlyLoaderMock, 'get_filename') as mocked:
-            mocked.side_effect = ImportError
-            name = 'blah'
-            loader = self.SourceOnlyLoaderMock('blah.py')
-            module = types.ModuleType(name)
-            loader.init_module_attrs(module)
-        self.assertFalse(hasattr(module, '__file__'))
-        self.assertFalse(hasattr(module, '__cached__'))
-
-
-class Frozen_SLInitModuleAttrTests(SourceLoaderInitModuleAttrTests, unittest.TestCase):
-    SourceOnlyLoaderMock = Frozen_SourceOnlyL
-    util = frozen_util
-
-    # Difficult to test under source thanks to cross-module mocking needs.
-    @mock.patch('importlib._bootstrap.cache_from_source')
-    def test_cache_from_source_NotImplementedError(self, mock_cache_from_source):
-        # If importlib.util.cache_from_source() raises NotImplementedError don't set
-        # __cached__.
-        mock_cache_from_source.side_effect = NotImplementedError
-        name = 'blah'
-        path = 'blah.py'
-        loader = self.SourceOnlyLoaderMock(path)
-        module = types.ModuleType(name)
-        loader.init_module_attrs(module)
-        self.assertEqual(module.__file__, path)
-        self.assertFalse(hasattr(module, '__cached__'))
-
-
-class Source_SLInitModuleAttrTests(SourceLoaderInitModuleAttrTests, unittest.TestCase):
-    SourceOnlyLoaderMock = Source_SourceOnlyL
-    util = source_util
-
-
-
 if __name__ == '__main__':
     unittest.main()
diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py
index 5126634..c6c2d47 100644
--- a/Lib/test/test_importlib/test_api.py
+++ b/Lib/test/test_importlib/test_api.py
@@ -165,6 +165,96 @@
     init = source_init
 
 
+class FindSpecTests:
+
+    class FakeMetaFinder:
+        @staticmethod
+        def find_spec(name, path=None, target=None): return name, path, target
+
+    def test_sys_modules(self):
+        name = 'some_mod'
+        with util.uncache(name):
+            module = types.ModuleType(name)
+            loader = 'a loader!'
+            spec = self.machinery.ModuleSpec(name, loader)
+            module.__loader__ = loader
+            module.__spec__ = spec
+            sys.modules[name] = module
+            found = self.init.find_spec(name)
+            self.assertEqual(found, spec)
+
+    def test_sys_modules_without___loader__(self):
+        name = 'some_mod'
+        with util.uncache(name):
+            module = types.ModuleType(name)
+            del module.__loader__
+            loader = 'a loader!'
+            spec = self.machinery.ModuleSpec(name, loader)
+            module.__spec__ = spec
+            sys.modules[name] = module
+            found = self.init.find_spec(name)
+            self.assertEqual(found, spec)
+
+    def test_sys_modules_spec_is_None(self):
+        name = 'some_mod'
+        with util.uncache(name):
+            module = types.ModuleType(name)
+            module.__spec__ = None
+            sys.modules[name] = module
+            with self.assertRaises(ValueError):
+                self.init.find_spec(name)
+
+    def test_sys_modules_loader_is_None(self):
+        name = 'some_mod'
+        with util.uncache(name):
+            module = types.ModuleType(name)
+            spec = self.machinery.ModuleSpec(name, None)
+            module.__spec__ = spec
+            sys.modules[name] = module
+            found = self.init.find_spec(name)
+            self.assertEqual(found, spec)
+
+    def test_sys_modules_spec_is_not_set(self):
+        name = 'some_mod'
+        with util.uncache(name):
+            module = types.ModuleType(name)
+            try:
+                del module.__spec__
+            except AttributeError:
+                pass
+            sys.modules[name] = module
+            with self.assertRaises(ValueError):
+                self.init.find_spec(name)
+
+    def test_success(self):
+        name = 'some_mod'
+        with util.uncache(name):
+            with util.import_state(meta_path=[self.FakeMetaFinder]):
+                self.assertEqual((name, None, None),
+                                 self.init.find_spec(name))
+
+    def test_success_path(self):
+        # Searching on a path should work.
+        name = 'some_mod'
+        path = 'path to some place'
+        with util.uncache(name):
+            with util.import_state(meta_path=[self.FakeMetaFinder]):
+                self.assertEqual((name, path, None),
+                                 self.init.find_spec(name, path))
+
+    def test_nothing(self):
+        # None is returned upon failure to find a loader.
+        self.assertIsNone(self.init.find_spec('nevergoingtofindthismodule'))
+
+class Frozen_FindSpecTests(FindSpecTests, unittest.TestCase):
+    init = frozen_init
+    machinery = frozen_machinery
+
+class Source_FindSpecTests(FindSpecTests, unittest.TestCase):
+    init = source_init
+    machinery = source_machinery
+
+
 class ReloadTests:
 
     """Test module reloading for builtin and extension modules."""
@@ -219,6 +309,7 @@
         with support.temp_cwd(None) as cwd:
             with util.uncache('spam'):
                 with support.DirsOnSysPath(cwd):
+                    # Start as a plain module.
                     self.init.invalidate_caches()
                     path = os.path.join(cwd, name + '.py')
                     cached = self.util.cache_from_source(path)
@@ -232,11 +323,14 @@
                     support.create_empty_file(path)
                     module = self.init.import_module(name)
                     ns = vars(module)
-                    del ns['__initializing__']
                     loader = ns.pop('__loader__')
+                    spec = ns.pop('__spec__')
+                    self.assertEqual(spec.name, name)
+                    self.assertEqual(spec.loader, loader)
                     self.assertEqual(loader.path, path)
                     self.assertEqual(ns, expected)
 
+                    # Change to a package.
                     self.init.invalidate_caches()
                     init_path = os.path.join(cwd, name, '__init__.py')
                     cached = self.util.cache_from_source(init_path)
@@ -252,18 +346,21 @@
                     os.rename(path, init_path)
                     reloaded = self.init.reload(module)
                     ns = vars(reloaded)
-                    del ns['__initializing__']
                     loader = ns.pop('__loader__')
+                    spec = ns.pop('__spec__')
+                    self.assertEqual(spec.name, name)
+                    self.assertEqual(spec.loader, loader)
                     self.assertIs(reloaded, module)
                     self.assertEqual(loader.path, init_path)
+                    self.maxDiff = None
                     self.assertEqual(ns, expected)
 
     def test_reload_namespace_changed(self):
-        self.maxDiff = None
         name = 'spam'
         with support.temp_cwd(None) as cwd:
             with util.uncache('spam'):
                 with support.DirsOnSysPath(cwd):
+                    # Start as a namespace package.
                     self.init.invalidate_caches()
                     bad_path = os.path.join(cwd, name, '__init.py')
                     cached = self.util.cache_from_source(bad_path)
@@ -276,9 +373,12 @@
                         init_file.write('eggs = None')
                     module = self.init.import_module(name)
                     ns = vars(module)
-                    del ns['__initializing__']
                     loader = ns.pop('__loader__')
                     path = ns.pop('__path__')
+                    spec = ns.pop('__spec__')
+                    self.assertEqual(spec.name, name)
+                    self.assertIs(spec.loader, None)
+                    self.assertIsNot(loader, None)
                     self.assertEqual(set(path),
                                      set([os.path.dirname(bad_path)]))
                     with self.assertRaises(AttributeError):
@@ -286,6 +386,7 @@
                         loader.path
                     self.assertEqual(ns, expected)
 
+                    # Change to a regular package.
                     self.init.invalidate_caches()
                     init_path = os.path.join(cwd, name, '__init__.py')
                     cached = self.util.cache_from_source(init_path)
@@ -301,8 +402,10 @@
                     os.rename(bad_path, init_path)
                     reloaded = self.init.reload(module)
                     ns = vars(reloaded)
-                    del ns['__initializing__']
                     loader = ns.pop('__loader__')
+                    spec = ns.pop('__spec__')
+                    self.assertEqual(spec.name, name)
+                    self.assertEqual(spec.loader, loader)
                     self.assertIs(reloaded, module)
                     self.assertEqual(loader.path, init_path)
                     self.assertEqual(ns, expected)
@@ -371,12 +474,23 @@
         # Issue #17098: all modules should have __loader__ defined.
         for name, module in sys.modules.items():
             if isinstance(module, types.ModuleType):
-                self.assertTrue(hasattr(module, '__loader__'),
-                                '{!r} lacks a __loader__ attribute'.format(name))
-                if self.machinery.BuiltinImporter.find_module(name):
-                    self.assertIsNot(module.__loader__, None)
-                elif self.machinery.FrozenImporter.find_module(name):
-                    self.assertIsNot(module.__loader__, None)
+                with self.subTest(name=name):
+                    self.assertTrue(hasattr(module, '__loader__'),
+                                    '{!r} lacks a __loader__ attribute'.format(name))
+                    if self.machinery.BuiltinImporter.find_module(name):
+                        self.assertIsNot(module.__loader__, None)
+                    elif self.machinery.FrozenImporter.find_module(name):
+                        self.assertIsNot(module.__loader__, None)
+
+    def test_everyone_has___spec__(self):
+        for name, module in sys.modules.items():
+            if isinstance(module, types.ModuleType):
+                with self.subTest(name=name):
+                    self.assertTrue(hasattr(module, '__spec__'))
+                    if self.machinery.BuiltinImporter.find_module(name):
+                        self.assertIsNot(module.__spec__, None)
+                    elif self.machinery.FrozenImporter.find_module(name):
+                        self.assertIsNot(module.__spec__, None)
 
 class Frozen_StartupTests(StartupTests, unittest.TestCase):
     machinery = frozen_machinery
diff --git a/Lib/test/test_importlib/test_spec.py b/Lib/test/test_importlib/test_spec.py
new file mode 100644
index 0000000..e4ef39f
--- /dev/null
+++ b/Lib/test/test_importlib/test_spec.py
@@ -0,0 +1,968 @@
+from . import util
+
+frozen_init, source_init = util.import_importlib('importlib')
+frozen_bootstrap = frozen_init._bootstrap
+source_bootstrap = source_init._bootstrap
+frozen_machinery, source_machinery = util.import_importlib('importlib.machinery')
+frozen_util, source_util = util.import_importlib('importlib.util')
+
+import os.path
+from test.support import CleanImport
+import unittest
+import sys
+
+
+
+class TestLoader:
+
+    def __init__(self, path=None, is_package=None):
+#        if path:
+#            if is_package:
+#                if not path.endswith('.py'):
+#                    path = os.path.join(path, '__init__.py')
+#            elif is_package is None:
+#                is_package = path.endswith('__init__.py')
+
+        self.path = path
+        self.package = is_package
+
+    def __repr__(self):
+        return '<TestLoader object>'
+
+    def __getattr__(self, name):
+        if name == 'get_filename' and self.path is not None:
+            return self._get_filename
+        if name == 'is_package':
+            return self._is_package
+        raise AttributeError(name)
+
+    def _get_filename(self, name):
+        return self.path
+
+    def _is_package(self, name):
+        return self.package
+
+
+class NewLoader(TestLoader):
+
+    EGGS = 1
+
+    def exec_module(self, module):
+        module.eggs = self.EGGS
+
+
+class LegacyLoader(TestLoader):
+
+    HAM = -1
+
+    @frozen_util.module_for_loader
+    def load_module(self, module):
+        module.ham = self.HAM
+        return module
+
+
+class ModuleSpecTests:
+
+    def setUp(self):
+        self.name = 'spam'
+        self.path = 'spam.py'
+        self.cached = self.util.cache_from_source(self.path)
+        self.loader = TestLoader()
+        self.spec = self.machinery.ModuleSpec(self.name, self.loader)
+        self.loc_spec = self.machinery.ModuleSpec(self.name, self.loader,
+                                                  origin=self.path)
+        self.loc_spec._set_fileattr = True
+
+    def test_default(self):
+        spec = self.machinery.ModuleSpec(self.name, self.loader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.loader)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_default_no_loader(self):
+        spec = self.machinery.ModuleSpec(self.name, None)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertIs(spec.loader, None)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_default_is_package_false(self):
+        spec = self.machinery.ModuleSpec(self.name, self.loader,
+                                         is_package=False)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.loader)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_default_is_package_true(self):
+        spec = self.machinery.ModuleSpec(self.name, self.loader,
+                                         is_package=True)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.loader)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertEqual(spec.submodule_search_locations, [])
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_equality(self):
+        other = type(sys.implementation)(name=self.name,
+                                         loader=self.loader,
+                                         origin=None,
+                                         submodule_search_locations=None,
+                                         has_location=False,
+                                         cached=None,
+                                         )
+
+        self.assertTrue(self.spec == other)
+
+    def test_equality_location(self):
+        other = type(sys.implementation)(name=self.name,
+                                         loader=self.loader,
+                                         origin=self.path,
+                                         submodule_search_locations=None,
+                                         has_location=True,
+                                         cached=self.cached,
+                                         )
+
+        self.assertEqual(self.loc_spec, other)
+
+    def test_inequality(self):
+        other = type(sys.implementation)(name='ham',
+                                         loader=self.loader,
+                                         origin=None,
+                                         submodule_search_locations=None,
+                                         has_location=False,
+                                         cached=None,
+                                         )
+
+        self.assertNotEqual(self.spec, other)
+
+    def test_inequality_incomplete(self):
+        other = type(sys.implementation)(name=self.name,
+                                         loader=self.loader,
+                                         )
+
+        self.assertNotEqual(self.spec, other)
+
+    def test_package(self):
+        spec = self.machinery.ModuleSpec('spam.eggs', self.loader)
+
+        self.assertEqual(spec.parent, 'spam')
+
+    def test_package_is_package(self):
+        spec = self.machinery.ModuleSpec('spam.eggs', self.loader,
+                                         is_package=True)
+
+        self.assertEqual(spec.parent, 'spam.eggs')
+
+    # cached
+
+    def test_cached_set(self):
+        before = self.spec.cached
+        self.spec.cached = 'there'
+        after = self.spec.cached
+
+        self.assertIs(before, None)
+        self.assertEqual(after, 'there')
+
+    def test_cached_no_origin(self):
+        spec = self.machinery.ModuleSpec(self.name, self.loader)
+
+        self.assertIs(spec.cached, None)
+
+    def test_cached_with_origin_not_location(self):
+        spec = self.machinery.ModuleSpec(self.name, self.loader,
+                                         origin=self.path)
+
+        self.assertIs(spec.cached, None)
+
+    def test_cached_source(self):
+        expected = self.util.cache_from_source(self.path)
+
+        self.assertEqual(self.loc_spec.cached, expected)
+
+    def test_cached_source_unknown_suffix(self):
+        self.loc_spec.origin = 'spam.spamspamspam'
+
+        self.assertIs(self.loc_spec.cached, None)
+
+    def test_cached_source_missing_cache_tag(self):
+        original = sys.implementation.cache_tag
+        sys.implementation.cache_tag = None
+        try:
+            cached = self.loc_spec.cached
+        finally:
+            sys.implementation.cache_tag = original
+
+        self.assertIs(cached, None)
+
+    def test_cached_sourceless(self):
+        self.loc_spec.origin = 'spam.pyc'
+
+        self.assertEqual(self.loc_spec.cached, 'spam.pyc')
+
+
+class Frozen_ModuleSpecTests(ModuleSpecTests, unittest.TestCase):
+    util = frozen_util
+    machinery = frozen_machinery
+
+
+class Source_ModuleSpecTests(ModuleSpecTests, unittest.TestCase):
+    util = source_util
+    machinery = source_machinery
+
+
+class ModuleSpecMethodsTests:
+
+    def setUp(self):
+        self.name = 'spam'
+        self.path = 'spam.py'
+        self.cached = self.util.cache_from_source(self.path)
+        self.loader = TestLoader()
+        self.spec = self.machinery.ModuleSpec(self.name, self.loader)
+        self.loc_spec = self.machinery.ModuleSpec(self.name, self.loader,
+                                                  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()
+        sys.modules[self.name] = module
+        self.assertFalse(hasattr(module, 'eggs'))
+        self.bootstrap._SpecMethods(self.spec).exec(module)
+
+        self.assertEqual(module.eggs, 1)
+
+    # load()
+
+    def test_load(self):
+        self.spec.loader = NewLoader()
+        with CleanImport(self.spec.name):
+            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            installed = sys.modules[self.spec.name]
+
+        self.assertEqual(loaded.eggs, 1)
+        self.assertIs(loaded, installed)
+
+    def test_load_replaced(self):
+        replacement = object()
+        class ReplacingLoader(TestLoader):
+            def exec_module(self, module):
+                sys.modules[module.__name__] = replacement
+        self.spec.loader = ReplacingLoader()
+        with CleanImport(self.spec.name):
+            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            installed = sys.modules[self.spec.name]
+
+        self.assertIs(loaded, replacement)
+        self.assertIs(installed, replacement)
+
+    def test_load_failed(self):
+        class FailedLoader(TestLoader):
+            def exec_module(self, module):
+                raise RuntimeError
+        self.spec.loader = FailedLoader()
+        with CleanImport(self.spec.name):
+            with self.assertRaises(RuntimeError):
+                loaded = self.bootstrap._SpecMethods(self.spec).load()
+            self.assertNotIn(self.spec.name, sys.modules)
+
+    def test_load_failed_removed(self):
+        class FailedLoader(TestLoader):
+            def exec_module(self, module):
+                del sys.modules[module.__name__]
+                raise RuntimeError
+        self.spec.loader = FailedLoader()
+        with CleanImport(self.spec.name):
+            with self.assertRaises(RuntimeError):
+                loaded = self.bootstrap._SpecMethods(self.spec).load()
+            self.assertNotIn(self.spec.name, sys.modules)
+
+    def test_load_existing(self):
+        existing = type(sys)('ham')
+        existing.count = 5
+        self.spec.loader = NewLoader()
+        with CleanImport(self.name):
+            sys.modules[self.name] = existing
+            assert self.spec.name == self.name
+            loaded = self.bootstrap._SpecMethods(self.spec).load()
+
+        self.assertEqual(loaded.eggs, 1)
+        self.assertFalse(hasattr(loaded, 'ham'))
+
+    def test_load_legacy(self):
+        self.spec.loader = LegacyLoader()
+        with CleanImport(self.spec.name):
+            loaded = self.bootstrap._SpecMethods(self.spec).load()
+
+        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()
+
+        self.assertIs(loaded.__loader__, self.spec.loader)
+        self.assertEqual(loaded.__package__, self.spec.parent)
+        self.assertIs(loaded.__spec__, self.spec)
+
+    def test_load_legacy_attributes_immutable(self):
+        module = object()
+        class ImmutableLoader(TestLoader):
+            def load_module(self, name):
+                sys.modules[name] = module
+                return module
+        self.spec.loader = ImmutableLoader()
+        with CleanImport(self.spec.name):
+            loaded = self.bootstrap._SpecMethods(self.spec).load()
+
+            self.assertIs(sys.modules[self.spec.name], module)
+
+    # reload()
+
+    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)
+            installed = sys.modules[self.spec.name]
+
+        self.assertEqual(loaded.eggs, 1)
+        self.assertIs(reloaded, loaded)
+        self.assertIs(installed, loaded)
+
+    def test_reload_modified(self):
+        self.spec.loader = NewLoader()
+        with CleanImport(self.spec.name):
+            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded.eggs = 2
+            reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
+
+        self.assertEqual(loaded.eggs, 1)
+        self.assertIs(reloaded, loaded)
+
+    def test_reload_extra_attributes(self):
+        self.spec.loader = NewLoader()
+        with CleanImport(self.spec.name):
+            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded.available = False
+            reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
+
+        self.assertFalse(loaded.available)
+        self.assertIs(reloaded, loaded)
+
+    def test_reload_init_module_attrs(self):
+        self.spec.loader = NewLoader()
+        with CleanImport(self.spec.name):
+            loaded = self.bootstrap._SpecMethods(self.spec).load()
+            loaded.__name__ = 'ham'
+            del loaded.__loader__
+            del loaded.__package__
+            del loaded.__spec__
+            self.bootstrap._SpecMethods(self.spec).exec(loaded)
+
+        self.assertEqual(loaded.__name__, self.spec.name)
+        self.assertIs(loaded.__loader__, self.spec.loader)
+        self.assertEqual(loaded.__package__, self.spec.parent)
+        self.assertIs(loaded.__spec__, self.spec)
+        self.assertFalse(hasattr(loaded, '__path__'))
+        self.assertFalse(hasattr(loaded, '__file__'))
+        self.assertFalse(hasattr(loaded, '__cached__'))
+
+    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)
+            installed = sys.modules[self.spec.name]
+
+        self.assertEqual(loaded.ham, -1)
+        self.assertIs(reloaded, loaded)
+        self.assertIs(installed, loaded)
+
+
+class Frozen_ModuleSpecMethodsTests(ModuleSpecMethodsTests, unittest.TestCase):
+    bootstrap = frozen_bootstrap
+    machinery = frozen_machinery
+    util = frozen_util
+
+
+class Source_ModuleSpecMethodsTests(ModuleSpecMethodsTests, unittest.TestCase):
+    bootstrap = source_bootstrap
+    machinery = source_machinery
+    util = source_util
+
+
+class ModuleReprTests:
+
+    # XXX Add more tests for repr(module) once ModuleSpec._module_repr()
+    # is in place?
+
+    def setUp(self):
+        self.module = type(os)('spam')
+        self.spec = self.machinery.ModuleSpec('spam', TestLoader())
+
+    def test_module___loader___module_repr(self):
+        class Loader:
+            def module_repr(self, module):
+                return '<delicious {}>'.format(module.__name__)
+        self.module.__loader__ = Loader()
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr, '<delicious spam>')
+
+    def test_module___loader___module_repr_bad(self):
+        class Loader(TestLoader):
+            def module_repr(self, module):
+                raise Exception
+        self.module.__loader__ = Loader()
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr,
+                         '<module {!r} (<TestLoader object>)>'.format('spam'))
+
+    def test_module___spec__(self):
+        origin = 'in a hole, in the ground'
+        self.spec.origin = origin
+        self.module.__spec__ = self.spec
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr, '<module {!r} ({})>'.format('spam', origin))
+
+    def test_module___spec___location(self):
+        location = 'in_a_galaxy_far_far_away.py'
+        self.spec.origin = location
+        self.spec._set_fileattr = True
+        self.module.__spec__ = self.spec
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr,
+                         '<module {!r} from {!r}>'.format('spam', location))
+
+    def test_module___spec___no_origin(self):
+        self.spec.loader = TestLoader()
+        self.module.__spec__ = self.spec
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr,
+                         '<module {!r} (<TestLoader object>)>'.format('spam'))
+
+    def test_module___spec___no_origin_no_loader(self):
+        self.spec.loader = None
+        self.module.__spec__ = self.spec
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr, '<module {!r}>'.format('spam'))
+
+    def test_module_no_name(self):
+        del self.module.__name__
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr, '<module {!r}>'.format('?'))
+
+    def test_module_with_file(self):
+        filename = 'e/i/e/i/o/spam.py'
+        self.module.__file__ = filename
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr,
+                         '<module {!r} from {!r}>'.format('spam', filename))
+
+    def test_module_no_file(self):
+        self.module.__loader__ = TestLoader()
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr,
+                         '<module {!r} (<TestLoader object>)>'.format('spam'))
+
+    def test_module_no_file_no_loader(self):
+        modrepr = self.bootstrap._module_repr(self.module)
+
+        self.assertEqual(modrepr, '<module {!r}>'.format('spam'))
+
+
+class Frozen_ModuleReprTests(ModuleReprTests, unittest.TestCase):
+    bootstrap = frozen_bootstrap
+    machinery = frozen_machinery
+    util = frozen_util
+
+
+class Source_ModuleReprTests(ModuleReprTests, unittest.TestCase):
+    bootstrap = source_bootstrap
+    machinery = source_machinery
+    util = source_util
+
+
+class FactoryTests:
+
+    def setUp(self):
+        self.name = 'spam'
+        self.path = 'spam.py'
+        self.cached = self.util.cache_from_source(self.path)
+        self.loader = TestLoader()
+        self.fileloader = TestLoader(self.path)
+        self.pkgloader = TestLoader(self.path, True)
+
+    # spec_from_loader()
+
+    def test_spec_from_loader_default(self):
+        spec = self.util.spec_from_loader(self.name, self.loader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.loader)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_spec_from_loader_default_with_bad_is_package(self):
+        class Loader:
+            def is_package(self, name):
+                raise ImportError
+        loader = Loader()
+        spec = self.util.spec_from_loader(self.name, loader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, loader)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_spec_from_loader_origin(self):
+        origin = 'somewhere over the rainbow'
+        spec = self.util.spec_from_loader(self.name, self.loader,
+                                          origin=origin)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.loader)
+        self.assertIs(spec.origin, origin)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_spec_from_loader_is_package_false(self):
+        spec = self.util.spec_from_loader(self.name, self.loader,
+                                          is_package=False)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.loader)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_spec_from_loader_is_package_true(self):
+        spec = self.util.spec_from_loader(self.name, self.loader,
+                                          is_package=True)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.loader)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertEqual(spec.submodule_search_locations, [])
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_spec_from_loader_origin_and_is_package(self):
+        origin = 'where the streets have no name'
+        spec = self.util.spec_from_loader(self.name, self.loader,
+                                          origin=origin, is_package=True)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.loader)
+        self.assertIs(spec.origin, origin)
+        self.assertIs(spec.loader_state, None)
+        self.assertEqual(spec.submodule_search_locations, [])
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_spec_from_loader_is_package_with_loader_false(self):
+        loader = TestLoader(is_package=False)
+        spec = self.util.spec_from_loader(self.name, loader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, loader)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_spec_from_loader_is_package_with_loader_true(self):
+        loader = TestLoader(is_package=True)
+        spec = self.util.spec_from_loader(self.name, loader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, loader)
+        self.assertIs(spec.origin, None)
+        self.assertIs(spec.loader_state, None)
+        self.assertEqual(spec.submodule_search_locations, [])
+        self.assertIs(spec.cached, None)
+        self.assertFalse(spec.has_location)
+
+    def test_spec_from_loader_default_with_file_loader(self):
+        spec = self.util.spec_from_loader(self.name, self.fileloader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.fileloader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_loader_is_package_false_with_fileloader(self):
+        spec = self.util.spec_from_loader(self.name, self.fileloader,
+                                          is_package=False)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.fileloader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_loader_is_package_true_with_fileloader(self):
+        spec = self.util.spec_from_loader(self.name, self.fileloader,
+                                          is_package=True)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.fileloader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertEqual(spec.submodule_search_locations, [''])
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    # spec_from_file_location()
+
+    def test_spec_from_file_location_default(self):
+        if self.machinery is source_machinery:
+            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)
+        self.assertIsInstance(spec.loader,
+                              self.machinery.SourceFileLoader)
+        self.assertEqual(spec.loader.name, self.name)
+        self.assertEqual(spec.loader.path, self.path)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_default_without_location(self):
+        spec = self.util.spec_from_file_location(self.name)
+
+        self.assertIs(spec, None)
+
+    def test_spec_from_file_location_default_bad_suffix(self):
+        spec = self.util.spec_from_file_location(self.name, 'spam.eggs')
+
+        self.assertIs(spec, None)
+
+    def test_spec_from_file_location_loader_no_location(self):
+        spec = self.util.spec_from_file_location(self.name,
+                                                 loader=self.fileloader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.fileloader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_loader_no_location_no_get_filename(self):
+        spec = self.util.spec_from_file_location(self.name,
+                                                 loader=self.loader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.loader)
+        self.assertEqual(spec.origin, '<unknown>')
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_loader_no_location_bad_get_filename(self):
+        class Loader:
+            def get_filename(self, name):
+                raise ImportError
+        loader = Loader()
+        spec = self.util.spec_from_file_location(self.name, loader=loader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, loader)
+        self.assertEqual(spec.origin, '<unknown>')
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertIs(spec.cached, None)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_smsl_none(self):
+        spec = self.util.spec_from_file_location(self.name, self.path,
+                                       loader=self.fileloader,
+                                       submodule_search_locations=None)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.fileloader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_smsl_empty(self):
+        spec = self.util.spec_from_file_location(self.name, self.path,
+                                       loader=self.fileloader,
+                                       submodule_search_locations=[])
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.fileloader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertEqual(spec.submodule_search_locations, [''])
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_smsl_not_empty(self):
+        spec = self.util.spec_from_file_location(self.name, self.path,
+                                       loader=self.fileloader,
+                                       submodule_search_locations=['eggs'])
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.fileloader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertEqual(spec.submodule_search_locations, ['eggs'])
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_smsl_default(self):
+        spec = self.util.spec_from_file_location(self.name, self.path,
+                                       loader=self.pkgloader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.pkgloader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertEqual(spec.submodule_search_locations, [''])
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_smsl_default_not_package(self):
+        class Loader:
+            def is_package(self, name):
+                return False
+        loader = Loader()
+        spec = self.util.spec_from_file_location(self.name, self.path,
+                                                 loader=loader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, loader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_smsl_default_no_is_package(self):
+        spec = self.util.spec_from_file_location(self.name, self.path,
+                                       loader=self.fileloader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, self.fileloader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+    def test_spec_from_file_location_smsl_default_bad_is_package(self):
+        class Loader:
+            def is_package(self, name):
+                raise ImportError
+        loader = Loader()
+        spec = self.util.spec_from_file_location(self.name, self.path,
+                                                 loader=loader)
+
+        self.assertEqual(spec.name, self.name)
+        self.assertEqual(spec.loader, loader)
+        self.assertEqual(spec.origin, self.path)
+        self.assertIs(spec.loader_state, None)
+        self.assertIs(spec.submodule_search_locations, None)
+        self.assertEqual(spec.cached, self.cached)
+        self.assertTrue(spec.has_location)
+
+
+class Frozen_FactoryTests(FactoryTests, unittest.TestCase):
+    util = frozen_util
+    machinery = frozen_machinery
+
+
+class Source_FactoryTests(FactoryTests, unittest.TestCase):
+    util = source_util
+    machinery = source_machinery
diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py
index 2ac57df..604e44d 100644
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -34,71 +34,6 @@
         DecodeSourceBytesTests, util=[frozen_util, source_util])
 
 
-class ModuleToLoadTests:
-
-    module_name = 'ModuleManagerTest_module'
-
-    def setUp(self):
-        support.unload(self.module_name)
-        self.addCleanup(support.unload, self.module_name)
-
-    def test_new_module(self):
-        # Test a new module is created, inserted into sys.modules, has
-        # __initializing__ set to True after entering the context manager,
-        # and __initializing__ set to False after exiting.
-        with self.util.module_to_load(self.module_name) as module:
-            self.assertIn(self.module_name, sys.modules)
-            self.assertIs(sys.modules[self.module_name], module)
-            self.assertTrue(module.__initializing__)
-        self.assertFalse(module.__initializing__)
-
-    def test_new_module_failed(self):
-        # Test the module is removed from sys.modules.
-        try:
-            with self.util.module_to_load(self.module_name) as module:
-                self.assertIn(self.module_name, sys.modules)
-                raise exception
-        except Exception:
-            self.assertNotIn(self.module_name, sys.modules)
-        else:
-            self.fail('importlib.util.module_to_load swallowed an exception')
-
-    def test_reload(self):
-        # Test that the same module is in sys.modules.
-        created_module = types.ModuleType(self.module_name)
-        sys.modules[self.module_name] = created_module
-        with self.util.module_to_load(self.module_name) as module:
-            self.assertIs(module, created_module)
-
-    def test_reload_failed(self):
-        # Test that the module was left in sys.modules.
-        created_module = types.ModuleType(self.module_name)
-        sys.modules[self.module_name] = created_module
-        try:
-            with self.util.module_to_load(self.module_name) as module:
-                raise Exception
-        except Exception:
-            self.assertIn(self.module_name, sys.modules)
-        else:
-            self.fail('importlib.util.module_to_load swallowed an exception')
-
-    def test_reset_name(self):
-        # If reset_name is true then module.__name__ = name, else leave it be.
-        odd_name = 'not your typical name'
-        created_module = types.ModuleType(self.module_name)
-        created_module.__name__ = odd_name
-        sys.modules[self.module_name] = created_module
-        with self.util.module_to_load(self.module_name) as module:
-            self.assertEqual(module.__name__, self.module_name)
-        created_module.__name__ = odd_name
-        with self.util.module_to_load(self.module_name, reset_name=False) as module:
-            self.assertEqual(module.__name__, odd_name)
-
-Frozen_ModuleToLoadTests, Source_ModuleToLoadTests = test_util.test_both(
-        ModuleToLoadTests,
-        util=[frozen_util, source_util])
-
-
 class ModuleForLoaderTests:
 
     """Tests for importlib.util.module_for_loader."""
diff --git a/Lib/test/test_module.py b/Lib/test/test_module.py
index 5a9b503..536f7cf 100644
--- a/Lib/test/test_module.py
+++ b/Lib/test/test_module.py
@@ -37,8 +37,10 @@
         self.assertEqual(foo.__doc__, None)
         self.assertIs(foo.__loader__, None)
         self.assertIs(foo.__package__, None)
+        self.assertIs(foo.__spec__, None)
         self.assertEqual(foo.__dict__, {"__name__": "foo", "__doc__": None,
-                                        "__loader__": None, "__package__": None})
+                                        "__loader__": None, "__package__": None,
+                                        "__spec__": None})
 
     def test_ascii_docstring(self):
         # ASCII docstring
@@ -47,7 +49,8 @@
         self.assertEqual(foo.__doc__, "foodoc")
         self.assertEqual(foo.__dict__,
                          {"__name__": "foo", "__doc__": "foodoc",
-                          "__loader__": None, "__package__": None})
+                          "__loader__": None, "__package__": None,
+                          "__spec__": None})
 
     def test_unicode_docstring(self):
         # Unicode docstring
@@ -56,7 +59,8 @@
         self.assertEqual(foo.__doc__, "foodoc\u1234")
         self.assertEqual(foo.__dict__,
                          {"__name__": "foo", "__doc__": "foodoc\u1234",
-                          "__loader__": None, "__package__": None})
+                          "__loader__": None, "__package__": None,
+                          "__spec__": None})
 
     def test_reinit(self):
         # Reinitialization should not replace the __dict__
@@ -69,7 +73,7 @@
         self.assertEqual(foo.bar, 42)
         self.assertEqual(foo.__dict__,
               {"__name__": "foo", "__doc__": "foodoc", "bar": 42,
-               "__loader__": None, "__package__": None})
+               "__loader__": None, "__package__": None, "__spec__": None})
         self.assertTrue(foo.__dict__ is d)
 
     def test_dont_clear_dict(self):
diff --git a/Lib/test/test_namespace_pkgs.py b/Lib/test/test_namespace_pkgs.py
index 4570bee..6639612 100644
--- a/Lib/test/test_namespace_pkgs.py
+++ b/Lib/test/test_namespace_pkgs.py
@@ -1,5 +1,4 @@
 import contextlib
-from importlib._bootstrap import NamespaceLoader
 import importlib.abc
 import importlib.machinery
 import os
@@ -290,24 +289,5 @@
         self.assertEqual(a_test.attr, 'in module')
 
 
-class ABCTests(unittest.TestCase):
-
-    def setUp(self):
-        self.loader = NamespaceLoader('foo', ['pkg'],
-                                      importlib.machinery.PathFinder)
-
-    def test_is_package(self):
-        self.assertTrue(self.loader.is_package('foo'))
-
-    def test_get_code(self):
-        self.assertTrue(isinstance(self.loader.get_code('foo'), types.CodeType))
-
-    def test_get_source(self):
-        self.assertEqual(self.loader.get_source('foo'), '')
-
-    def test_abc_isinstance(self):
-        self.assertTrue(isinstance(self.loader, importlib.abc.InspectLoader))
-
-
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/test/test_pkg.py b/Lib/test/test_pkg.py
index 355efe7..9883000 100644
--- a/Lib/test/test_pkg.py
+++ b/Lib/test/test_pkg.py
@@ -199,14 +199,14 @@
         import t5
         self.assertEqual(fixdir(dir(t5)),
                          ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__path__', 'foo',
-                          'string', 't5'])
+                          '__name__', '__package__', '__path__', '__spec__',
+                          'foo', 'string', 't5'])
         self.assertEqual(fixdir(dir(t5.foo)),
                          ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', 'string'])
+                          '__name__', '__package__', '__spec__', 'string'])
         self.assertEqual(fixdir(dir(t5.string)),
                          ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', 'spam'])
+                          '__name__', '__package__', '__spec__', 'spam'])
 
     def test_6(self):
         hier = [
@@ -222,14 +222,15 @@
         import t6
         self.assertEqual(fixdir(dir(t6)),
                          ['__all__', '__cached__', '__doc__', '__file__',
-                          '__loader__', '__name__', '__package__', '__path__'])
+                          '__loader__', '__name__', '__package__', '__path__',
+                          '__spec__'])
         s = """
             import t6
             from t6 import *
             self.assertEqual(fixdir(dir(t6)),
                              ['__all__', '__cached__', '__doc__', '__file__',
                               '__loader__', '__name__', '__package__',
-                              '__path__', 'eggs', 'ham', 'spam'])
+                              '__path__', '__spec__', 'eggs', 'ham', 'spam'])
             self.assertEqual(dir(), ['eggs', 'ham', 'self', 'spam', 't6'])
             """
         self.run_code(s)
@@ -256,18 +257,19 @@
         import t7 as tas
         self.assertEqual(fixdir(dir(tas)),
                          ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__path__'])
+                          '__name__', '__package__', '__path__', '__spec__'])
         self.assertFalse(t7)
         from t7 import sub as subpar
         self.assertEqual(fixdir(dir(subpar)),
                          ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__path__'])
+                          '__name__', '__package__', '__path__', '__spec__'])
         self.assertFalse(t7)
         self.assertFalse(sub)
         from t7.sub import subsub as subsubsub
         self.assertEqual(fixdir(dir(subsubsub)),
                          ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__path__', 'spam'])
+                          '__name__', '__package__', '__path__', '__spec__',
+                          'spam'])
         self.assertFalse(t7)
         self.assertFalse(sub)
         self.assertFalse(subsub)
diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py
index 1f48853..52dd0bc 100644
--- a/Lib/test/test_pkgutil.py
+++ b/Lib/test/test_pkgutil.py
@@ -208,9 +208,16 @@
             importers = list(iter_importers(fullname))
             expected_importer = get_importer(pathitem)
             for finder in importers:
+                loader = finder.find_module(fullname)
+                try:
+                    loader = loader.loader
+                except AttributeError:
+                    # For now we still allow raw loaders from
+                    # find_module().
+                    pass
                 self.assertIsInstance(finder, importlib.machinery.FileFinder)
                 self.assertEqual(finder, expected_importer)
-                self.assertIsInstance(finder.find_module(fullname),
+                self.assertIsInstance(loader,
                                       importlib.machinery.SourceFileLoader)
                 self.assertIsNone(finder.find_module(pkgname))
 
@@ -222,8 +229,11 @@
         finally:
             shutil.rmtree(dirname)
             del sys.path[0]
-            del sys.modules['spam']
-            del sys.modules['spam.eggs']
+            try:
+                del sys.modules['spam']
+                del sys.modules['spam.eggs']
+            except KeyError:
+                pass
 
 
     def test_mixed_namespace(self):
diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py
index 104e3b5..a504bde 100644
--- a/Lib/test/test_reprlib.py
+++ b/Lib/test/test_reprlib.py
@@ -253,6 +253,7 @@
             print("cached_path_len =", cached_path_len)
 
     def test_module(self):
+        self.maxDiff = None
         self._check_path_limitations(self.pkgname)
         create_empty_file(os.path.join(self.subpkgname, self.pkgname + '.py'))
         importlib.invalidate_caches()
diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py
index 16e2e12..f19c4ab 100644
--- a/Lib/test/test_runpy.py
+++ b/Lib/test/test_runpy.py
@@ -47,6 +47,7 @@
     "__cached__": None,
     "__package__": None,
     "__doc__": None,
+#    "__spec__": None,  # XXX Uncomment.
 }
 example_namespace =  {
     "sys": sys,
@@ -56,7 +57,7 @@
     "run_name_in_sys_modules": False,
     "module_in_sys_modules": False,
     "nested": dict(implicit_namespace,
-                   x=1, __name__="<run>", __loader__=None),
+                   x=1, __name__="<run>", __loader__=None, __spec__=None),
 }
 example_namespace.update(implicit_namespace)
 
@@ -243,6 +244,7 @@
             "__name__": mod_name,
             "__file__": mod_fname,
             "__package__": mod_name.rpartition(".")[0],
+#            "__spec__": None,  # XXX Needs to be set.
         })
         if alter_sys:
             expected_ns.update({
@@ -279,6 +281,7 @@
             "__name__": mod_name,
             "__file__": mod_fname,
             "__package__": pkg_name,
+#            "__spec__": None,  # XXX Needs to be set.
         })
         if alter_sys:
             expected_ns.update({