test_module_loading.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import imp
  2. from importlib import import_module
  3. import os
  4. import sys
  5. import unittest
  6. from zipimport import zipimporter
  7. from django.test import SimpleTestCase, modify_settings
  8. from django.test.utils import extend_sys_path
  9. from django.utils import six
  10. from django.utils.module_loading import (autodiscover_modules, import_string,
  11. module_has_submodule)
  12. from django.utils._os import upath
  13. class DefaultLoader(unittest.TestCase):
  14. def setUp(self):
  15. sys.meta_path.insert(0, ProxyFinder())
  16. def tearDown(self):
  17. sys.meta_path.pop(0)
  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(
  22. 'utils_tests.test_no_submodule')
  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. # A child that exists, but will generate an import error if loaded
  28. self.assertTrue(module_has_submodule(test_module, 'bad_module'))
  29. self.assertRaises(ImportError, import_module, 'utils_tests.test_module.bad_module')
  30. # A child that doesn't exist
  31. self.assertFalse(module_has_submodule(test_module, 'no_such_module'))
  32. self.assertRaises(ImportError, import_module, 'utils_tests.test_module.no_such_module')
  33. # A child that doesn't exist, but is the name of a package on the path
  34. self.assertFalse(module_has_submodule(test_module, 'django'))
  35. self.assertRaises(ImportError, import_module, 'utils_tests.test_module.django')
  36. # Don't be confused by caching of import misses
  37. import types # NOQA: causes attempted import of utils_tests.types
  38. self.assertFalse(module_has_submodule(sys.modules['utils_tests'], 'types'))
  39. # A module which doesn't have a __path__ (so no submodules)
  40. self.assertFalse(module_has_submodule(test_no_submodule, 'anything'))
  41. self.assertRaises(ImportError, import_module,
  42. 'utils_tests.test_no_submodule.anything')
  43. class EggLoader(unittest.TestCase):
  44. def setUp(self):
  45. self.egg_dir = '%s/eggs' % os.path.dirname(upath(__file__))
  46. def tearDown(self):
  47. sys.path_importer_cache.clear()
  48. sys.modules.pop('egg_module.sub1.sub2.bad_module', None)
  49. sys.modules.pop('egg_module.sub1.sub2.good_module', None)
  50. sys.modules.pop('egg_module.sub1.sub2', None)
  51. sys.modules.pop('egg_module.sub1', None)
  52. sys.modules.pop('egg_module.bad_module', None)
  53. sys.modules.pop('egg_module.good_module', None)
  54. sys.modules.pop('egg_module', None)
  55. def test_shallow_loader(self):
  56. "Module existence can be tested inside eggs"
  57. egg_name = '%s/test_egg.egg' % self.egg_dir
  58. with extend_sys_path(egg_name):
  59. egg_module = import_module('egg_module')
  60. # An importable child
  61. self.assertTrue(module_has_submodule(egg_module, 'good_module'))
  62. mod = import_module('egg_module.good_module')
  63. self.assertEqual(mod.content, 'Good Module')
  64. # A child that exists, but will generate an import error if loaded
  65. self.assertTrue(module_has_submodule(egg_module, 'bad_module'))
  66. self.assertRaises(ImportError, import_module, 'egg_module.bad_module')
  67. # A child that doesn't exist
  68. self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
  69. self.assertRaises(ImportError, import_module, 'egg_module.no_such_module')
  70. def test_deep_loader(self):
  71. "Modules deep inside an egg can still be tested for existence"
  72. egg_name = '%s/test_egg.egg' % self.egg_dir
  73. with extend_sys_path(egg_name):
  74. egg_module = import_module('egg_module.sub1.sub2')
  75. # An importable child
  76. self.assertTrue(module_has_submodule(egg_module, 'good_module'))
  77. mod = import_module('egg_module.sub1.sub2.good_module')
  78. self.assertEqual(mod.content, 'Deep Good Module')
  79. # A child that exists, but will generate an import error if loaded
  80. self.assertTrue(module_has_submodule(egg_module, 'bad_module'))
  81. self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.bad_module')
  82. # A child that doesn't exist
  83. self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
  84. self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.no_such_module')
  85. class ModuleImportTestCase(unittest.TestCase):
  86. def test_import_string(self):
  87. cls = import_string('django.utils.module_loading.import_string')
  88. self.assertEqual(cls, import_string)
  89. # Test exceptions raised
  90. self.assertRaises(ImportError, import_string, 'no_dots_in_path')
  91. self.assertRaises(ImportError, import_string, 'utils_tests.unexistent')
  92. @modify_settings(INSTALLED_APPS={'append': 'utils_tests.test_module'})
  93. class AutodiscoverModulesTestCase(SimpleTestCase):
  94. def tearDown(self):
  95. sys.path_importer_cache.clear()
  96. sys.modules.pop('utils_tests.test_module.another_bad_module', None)
  97. sys.modules.pop('utils_tests.test_module.another_good_module', None)
  98. sys.modules.pop('utils_tests.test_module.bad_module', None)
  99. sys.modules.pop('utils_tests.test_module.good_module', None)
  100. sys.modules.pop('utils_tests.test_module', None)
  101. def test_autodiscover_modules_found(self):
  102. autodiscover_modules('good_module')
  103. def test_autodiscover_modules_not_found(self):
  104. autodiscover_modules('missing_module')
  105. def test_autodiscover_modules_found_but_bad_module(self):
  106. with six.assertRaisesRegex(self, ImportError, "No module named '?a_package_name_that_does_not_exist'?"):
  107. autodiscover_modules('bad_module')
  108. def test_autodiscover_modules_several_one_bad_module(self):
  109. with six.assertRaisesRegex(self, ImportError, "No module named '?a_package_name_that_does_not_exist'?"):
  110. autodiscover_modules('good_module', 'bad_module')
  111. def test_autodiscover_modules_several_found(self):
  112. autodiscover_modules('good_module', 'another_good_module')
  113. def test_autodiscover_modules_several_found_with_registry(self):
  114. from .test_module import site
  115. autodiscover_modules('good_module', 'another_good_module', register_to=site)
  116. self.assertEqual(site._registry, {'lorem': 'ipsum'})
  117. def test_validate_registry_keeps_intact(self):
  118. from .test_module import site
  119. with six.assertRaisesRegex(self, Exception, "Some random exception."):
  120. autodiscover_modules('another_bad_module', register_to=site)
  121. self.assertEqual(site._registry, {})
  122. def test_validate_registry_resets_after_erroneous_module(self):
  123. from .test_module import site
  124. with six.assertRaisesRegex(self, Exception, "Some random exception."):
  125. autodiscover_modules('another_good_module', 'another_bad_module', register_to=site)
  126. self.assertEqual(site._registry, {'lorem': 'ipsum'})
  127. def test_validate_registry_resets_after_missing_module(self):
  128. from .test_module import site
  129. autodiscover_modules('does_not_exist', 'another_good_module', 'does_not_exist2', register_to=site)
  130. self.assertEqual(site._registry, {'lorem': 'ipsum'})
  131. class ProxyFinder(object):
  132. def __init__(self):
  133. self._cache = {}
  134. def find_module(self, fullname, path=None):
  135. tail = fullname.rsplit('.', 1)[-1]
  136. try:
  137. fd, fn, info = imp.find_module(tail, path)
  138. if fullname in self._cache:
  139. old_fd = self._cache[fullname][0]
  140. if old_fd:
  141. old_fd.close()
  142. self._cache[fullname] = (fd, fn, info)
  143. except ImportError:
  144. return None
  145. else:
  146. return self # this is a loader as well
  147. def load_module(self, fullname):
  148. if fullname in sys.modules:
  149. return sys.modules[fullname]
  150. fd, fn, info = self._cache[fullname]
  151. try:
  152. return imp.load_module(fullname, fd, fn, info)
  153. finally:
  154. if fd:
  155. fd.close()
  156. class TestFinder(object):
  157. def __init__(self, *args, **kwargs):
  158. self.importer = zipimporter(*args, **kwargs)
  159. def find_module(self, path):
  160. importer = self.importer.find_module(path)
  161. if importer is None:
  162. return
  163. return TestLoader(importer)
  164. class TestLoader(object):
  165. def __init__(self, importer):
  166. self.importer = importer
  167. def load_module(self, name):
  168. mod = self.importer.load_module(name)
  169. mod.__loader__ = self
  170. return mod
  171. class CustomLoader(EggLoader):
  172. """The Custom Loader test is exactly the same as the EggLoader, but
  173. it uses a custom defined Loader and Finder that is intentionally
  174. split into two classes. Although the EggLoader combines both functions
  175. into one class, this isn't required.
  176. """
  177. def setUp(self):
  178. super(CustomLoader, self).setUp()
  179. sys.path_hooks.insert(0, TestFinder)
  180. sys.path_importer_cache.clear()
  181. def tearDown(self):
  182. super(CustomLoader, self).tearDown()
  183. sys.path_hooks.pop(0)