issue 14660: Implement PEP 420, namespace packages.
diff --git a/Lib/test/test_module.py b/Lib/test/test_module.py
index 5617789..e5a2525 100644
--- a/Lib/test/test_module.py
+++ b/Lib/test/test_module.py
@@ -5,6 +5,15 @@
 import sys
 ModuleType = type(sys)
 
+class FullLoader:
+    @classmethod
+    def module_repr(cls, m):
+        return "<module '{}' (crafted)>".format(m.__name__)
+
+class BareLoader:
+    pass
+
+
 class ModuleTests(unittest.TestCase):
     def test_uninitialized(self):
         # An uninitialized module has no __dict__ or __name__,
@@ -80,8 +89,90 @@
         gc_collect()
         self.assertEqual(destroyed, [1])
 
+    def test_module_repr_minimal(self):
+        # reprs when modules have no __file__, __name__, or __loader__
+        m = ModuleType('foo')
+        del m.__name__
+        self.assertEqual(repr(m), "<module '?'>")
+
+    def test_module_repr_with_name(self):
+        m = ModuleType('foo')
+        self.assertEqual(repr(m), "<module 'foo'>")
+
+    def test_module_repr_with_name_and_filename(self):
+        m = ModuleType('foo')
+        m.__file__ = '/tmp/foo.py'
+        self.assertEqual(repr(m), "<module 'foo' from '/tmp/foo.py'>")
+
+    def test_module_repr_with_filename_only(self):
+        m = ModuleType('foo')
+        del m.__name__
+        m.__file__ = '/tmp/foo.py'
+        self.assertEqual(repr(m), "<module '?' from '/tmp/foo.py'>")
+
+    def test_module_repr_with_bare_loader_but_no_name(self):
+        m = ModuleType('foo')
+        del m.__name__
+        # Yes, a class not an instance.
+        m.__loader__ = BareLoader
+        self.assertEqual(
+            repr(m), "<module '?' (<class 'test.test_module.BareLoader'>)>")
+
+    def test_module_repr_with_full_loader_but_no_name(self):
+        # m.__loader__.module_repr() will fail because the module has no
+        # m.__name__.  This exception will get suppressed and instead the
+        # loader's repr will be used.
+        m = ModuleType('foo')
+        del m.__name__
+        # Yes, a class not an instance.
+        m.__loader__ = FullLoader
+        self.assertEqual(
+            repr(m), "<module '?' (<class 'test.test_module.FullLoader'>)>")
+
+    def test_module_repr_with_bare_loader(self):
+        m = ModuleType('foo')
+        # Yes, a class not an instance.
+        m.__loader__ = BareLoader
+        self.assertEqual(
+            repr(m), "<module 'foo' (<class 'test.test_module.BareLoader'>)>")
+
+    def test_module_repr_with_full_loader(self):
+        m = ModuleType('foo')
+        # Yes, a class not an instance.
+        m.__loader__ = FullLoader
+        self.assertEqual(
+            repr(m), "<module 'foo' (crafted)>")
+
+    def test_module_repr_with_bare_loader_and_filename(self):
+        # Because the loader has no module_repr(), use the file name.
+        m = ModuleType('foo')
+        # Yes, a class not an instance.
+        m.__loader__ = BareLoader
+        m.__file__ = '/tmp/foo.py'
+        self.assertEqual(repr(m), "<module 'foo' from '/tmp/foo.py'>")
+
+    def test_module_repr_with_full_loader_and_filename(self):
+        # Even though the module has an __file__, use __loader__.module_repr()
+        m = ModuleType('foo')
+        # Yes, a class not an instance.
+        m.__loader__ = FullLoader
+        m.__file__ = '/tmp/foo.py'
+        self.assertEqual(repr(m), "<module 'foo' (crafted)>")
+
+    def test_module_repr_builtin(self):
+        self.assertEqual(repr(sys), "<module 'sys' (built-in)>")
+
+    def test_module_repr_source(self):
+        r = repr(unittest)
+        self.assertEqual(r[:25], "<module 'unittest' from '")
+        self.assertEqual(r[-13:], "__init__.py'>")
+
+    # frozen and namespace module reprs are tested in importlib.
+
+
 def test_main():
     run_unittest(ModuleTests)
 
+
 if __name__ == '__main__':
     test_main()