1. import os
    
  2. import sys
    
  3. import unittest
    
  4. from importlib import import_module
    
  5. from zipimport import zipimporter
    
  6. 
    
  7. from django.test import SimpleTestCase, modify_settings
    
  8. from django.test.utils import extend_sys_path
    
  9. from django.utils.module_loading import (
    
  10.     autodiscover_modules,
    
  11.     import_string,
    
  12.     module_has_submodule,
    
  13. )
    
  14. from django.utils.version import PY310
    
  15. 
    
  16. 
    
  17. class DefaultLoader(unittest.TestCase):
    
  18.     def test_loader(self):
    
  19.         "Normal module existence can be tested"
    
  20.         test_module = import_module("utils_tests.test_module")
    
  21.         test_no_submodule = import_module("utils_tests.test_no_submodule")
    
  22. 
    
  23.         # An importable child
    
  24.         self.assertTrue(module_has_submodule(test_module, "good_module"))
    
  25.         mod = import_module("utils_tests.test_module.good_module")
    
  26.         self.assertEqual(mod.content, "Good Module")
    
  27. 
    
  28.         # A child that exists, but will generate an import error if loaded
    
  29.         self.assertTrue(module_has_submodule(test_module, "bad_module"))
    
  30.         with self.assertRaises(ImportError):
    
  31.             import_module("utils_tests.test_module.bad_module")
    
  32. 
    
  33.         # A child that doesn't exist
    
  34.         self.assertFalse(module_has_submodule(test_module, "no_such_module"))
    
  35.         with self.assertRaises(ImportError):
    
  36.             import_module("utils_tests.test_module.no_such_module")
    
  37. 
    
  38.         # A child that doesn't exist, but is the name of a package on the path
    
  39.         self.assertFalse(module_has_submodule(test_module, "django"))
    
  40.         with self.assertRaises(ImportError):
    
  41.             import_module("utils_tests.test_module.django")
    
  42. 
    
  43.         # Don't be confused by caching of import misses
    
  44.         import types  # NOQA: causes attempted import of utils_tests.types
    
  45. 
    
  46.         self.assertFalse(module_has_submodule(sys.modules["utils_tests"], "types"))
    
  47. 
    
  48.         # A module which doesn't have a __path__ (so no submodules)
    
  49.         self.assertFalse(module_has_submodule(test_no_submodule, "anything"))
    
  50.         with self.assertRaises(ImportError):
    
  51.             import_module("utils_tests.test_no_submodule.anything")
    
  52. 
    
  53.     def test_has_sumbodule_with_dotted_path(self):
    
  54.         """Nested module existence can be tested."""
    
  55.         test_module = import_module("utils_tests.test_module")
    
  56.         # A grandchild that exists.
    
  57.         self.assertIs(
    
  58.             module_has_submodule(test_module, "child_module.grandchild_module"), True
    
  59.         )
    
  60.         # A grandchild that doesn't exist.
    
  61.         self.assertIs(
    
  62.             module_has_submodule(test_module, "child_module.no_such_module"), False
    
  63.         )
    
  64.         # A grandchild whose parent doesn't exist.
    
  65.         self.assertIs(
    
  66.             module_has_submodule(test_module, "no_such_module.grandchild_module"), False
    
  67.         )
    
  68.         # A grandchild whose parent is not a package.
    
  69.         self.assertIs(
    
  70.             module_has_submodule(test_module, "good_module.no_such_module"), False
    
  71.         )
    
  72. 
    
  73. 
    
  74. class EggLoader(unittest.TestCase):
    
  75.     def setUp(self):
    
  76.         self.egg_dir = "%s/eggs" % os.path.dirname(__file__)
    
  77. 
    
  78.     def tearDown(self):
    
  79.         sys.path_importer_cache.clear()
    
  80. 
    
  81.         sys.modules.pop("egg_module.sub1.sub2.bad_module", None)
    
  82.         sys.modules.pop("egg_module.sub1.sub2.good_module", None)
    
  83.         sys.modules.pop("egg_module.sub1.sub2", None)
    
  84.         sys.modules.pop("egg_module.sub1", None)
    
  85.         sys.modules.pop("egg_module.bad_module", None)
    
  86.         sys.modules.pop("egg_module.good_module", None)
    
  87.         sys.modules.pop("egg_module", None)
    
  88. 
    
  89.     def test_shallow_loader(self):
    
  90.         "Module existence can be tested inside eggs"
    
  91.         egg_name = "%s/test_egg.egg" % self.egg_dir
    
  92.         with extend_sys_path(egg_name):
    
  93.             egg_module = import_module("egg_module")
    
  94. 
    
  95.             # An importable child
    
  96.             self.assertTrue(module_has_submodule(egg_module, "good_module"))
    
  97.             mod = import_module("egg_module.good_module")
    
  98.             self.assertEqual(mod.content, "Good Module")
    
  99. 
    
  100.             # A child that exists, but will generate an import error if loaded
    
  101.             self.assertTrue(module_has_submodule(egg_module, "bad_module"))
    
  102.             with self.assertRaises(ImportError):
    
  103.                 import_module("egg_module.bad_module")
    
  104. 
    
  105.             # A child that doesn't exist
    
  106.             self.assertFalse(module_has_submodule(egg_module, "no_such_module"))
    
  107.             with self.assertRaises(ImportError):
    
  108.                 import_module("egg_module.no_such_module")
    
  109. 
    
  110.     def test_deep_loader(self):
    
  111.         "Modules deep inside an egg can still be tested for existence"
    
  112.         egg_name = "%s/test_egg.egg" % self.egg_dir
    
  113.         with extend_sys_path(egg_name):
    
  114.             egg_module = import_module("egg_module.sub1.sub2")
    
  115. 
    
  116.             # An importable child
    
  117.             self.assertTrue(module_has_submodule(egg_module, "good_module"))
    
  118.             mod = import_module("egg_module.sub1.sub2.good_module")
    
  119.             self.assertEqual(mod.content, "Deep Good Module")
    
  120. 
    
  121.             # A child that exists, but will generate an import error if loaded
    
  122.             self.assertTrue(module_has_submodule(egg_module, "bad_module"))
    
  123.             with self.assertRaises(ImportError):
    
  124.                 import_module("egg_module.sub1.sub2.bad_module")
    
  125. 
    
  126.             # A child that doesn't exist
    
  127.             self.assertFalse(module_has_submodule(egg_module, "no_such_module"))
    
  128.             with self.assertRaises(ImportError):
    
  129.                 import_module("egg_module.sub1.sub2.no_such_module")
    
  130. 
    
  131. 
    
  132. class ModuleImportTests(SimpleTestCase):
    
  133.     def test_import_string(self):
    
  134.         cls = import_string("django.utils.module_loading.import_string")
    
  135.         self.assertEqual(cls, import_string)
    
  136. 
    
  137.         # Test exceptions raised
    
  138.         with self.assertRaises(ImportError):
    
  139.             import_string("no_dots_in_path")
    
  140.         msg = 'Module "utils_tests" does not define a "unexistent" attribute'
    
  141.         with self.assertRaisesMessage(ImportError, msg):
    
  142.             import_string("utils_tests.unexistent")
    
  143. 
    
  144. 
    
  145. @modify_settings(INSTALLED_APPS={"append": "utils_tests.test_module"})
    
  146. class AutodiscoverModulesTestCase(SimpleTestCase):
    
  147.     def tearDown(self):
    
  148.         sys.path_importer_cache.clear()
    
  149. 
    
  150.         sys.modules.pop("utils_tests.test_module.another_bad_module", None)
    
  151.         sys.modules.pop("utils_tests.test_module.another_good_module", None)
    
  152.         sys.modules.pop("utils_tests.test_module.bad_module", None)
    
  153.         sys.modules.pop("utils_tests.test_module.good_module", None)
    
  154.         sys.modules.pop("utils_tests.test_module", None)
    
  155. 
    
  156.     def test_autodiscover_modules_found(self):
    
  157.         autodiscover_modules("good_module")
    
  158. 
    
  159.     def test_autodiscover_modules_not_found(self):
    
  160.         autodiscover_modules("missing_module")
    
  161. 
    
  162.     def test_autodiscover_modules_found_but_bad_module(self):
    
  163.         with self.assertRaisesMessage(
    
  164.             ImportError, "No module named 'a_package_name_that_does_not_exist'"
    
  165.         ):
    
  166.             autodiscover_modules("bad_module")
    
  167. 
    
  168.     def test_autodiscover_modules_several_one_bad_module(self):
    
  169.         with self.assertRaisesMessage(
    
  170.             ImportError, "No module named 'a_package_name_that_does_not_exist'"
    
  171.         ):
    
  172.             autodiscover_modules("good_module", "bad_module")
    
  173. 
    
  174.     def test_autodiscover_modules_several_found(self):
    
  175.         autodiscover_modules("good_module", "another_good_module")
    
  176. 
    
  177.     def test_autodiscover_modules_several_found_with_registry(self):
    
  178.         from .test_module import site
    
  179. 
    
  180.         autodiscover_modules("good_module", "another_good_module", register_to=site)
    
  181.         self.assertEqual(site._registry, {"lorem": "ipsum"})
    
  182. 
    
  183.     def test_validate_registry_keeps_intact(self):
    
  184.         from .test_module import site
    
  185. 
    
  186.         with self.assertRaisesMessage(Exception, "Some random exception."):
    
  187.             autodiscover_modules("another_bad_module", register_to=site)
    
  188.         self.assertEqual(site._registry, {})
    
  189. 
    
  190.     def test_validate_registry_resets_after_erroneous_module(self):
    
  191.         from .test_module import site
    
  192. 
    
  193.         with self.assertRaisesMessage(Exception, "Some random exception."):
    
  194.             autodiscover_modules(
    
  195.                 "another_good_module", "another_bad_module", register_to=site
    
  196.             )
    
  197.         self.assertEqual(site._registry, {"lorem": "ipsum"})
    
  198. 
    
  199.     def test_validate_registry_resets_after_missing_module(self):
    
  200.         from .test_module import site
    
  201. 
    
  202.         autodiscover_modules(
    
  203.             "does_not_exist", "another_good_module", "does_not_exist2", register_to=site
    
  204.         )
    
  205.         self.assertEqual(site._registry, {"lorem": "ipsum"})
    
  206. 
    
  207. 
    
  208. if PY310:
    
  209. 
    
  210.     class TestFinder:
    
  211.         def __init__(self, *args, **kwargs):
    
  212.             self.importer = zipimporter(*args, **kwargs)
    
  213. 
    
  214.         def find_spec(self, path, target=None):
    
  215.             return self.importer.find_spec(path, target)
    
  216. 
    
  217. else:
    
  218. 
    
  219.     class TestFinder:
    
  220.         def __init__(self, *args, **kwargs):
    
  221.             self.importer = zipimporter(*args, **kwargs)
    
  222. 
    
  223.         def find_module(self, path):
    
  224.             importer = self.importer.find_module(path)
    
  225.             if importer is None:
    
  226.                 return
    
  227.             return TestLoader(importer)
    
  228. 
    
  229.     class TestLoader:
    
  230.         def __init__(self, importer):
    
  231.             self.importer = importer
    
  232. 
    
  233.         def load_module(self, name):
    
  234.             mod = self.importer.load_module(name)
    
  235.             mod.__loader__ = self
    
  236.             return mod
    
  237. 
    
  238. 
    
  239. class CustomLoader(EggLoader):
    
  240.     """The Custom Loader test is exactly the same as the EggLoader, but
    
  241.     it uses a custom defined Loader class. Although the EggLoader combines both
    
  242.     functions into one class, this isn't required.
    
  243.     """
    
  244. 
    
  245.     def setUp(self):
    
  246.         super().setUp()
    
  247.         sys.path_hooks.insert(0, TestFinder)
    
  248.         sys.path_importer_cache.clear()
    
  249. 
    
  250.     def tearDown(self):
    
  251.         super().tearDown()
    
  252.         sys.path_hooks.pop(0)