test_module_loading.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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.core.exceptions import ImproperlyConfigured
  8. from django.test import SimpleTestCase, modify_settings
  9. from django.test.utils import extend_sys_path
  10. from django.utils import six
  11. from django.utils.module_loading import autodiscover_modules, import_by_path, 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_by_path(self):
  87. cls = import_by_path(
  88. 'django.utils.module_loading.import_by_path')
  89. self.assertEqual(cls, import_by_path)
  90. # Test exceptions raised
  91. for path in ('no_dots_in_path', 'unexistent.path',
  92. 'utils_tests.unexistent'):
  93. self.assertRaises(ImproperlyConfigured, import_by_path, path)
  94. with self.assertRaises(ImproperlyConfigured) as cm:
  95. import_by_path('unexistent.module.path', error_prefix="Foo")
  96. self.assertTrue(str(cm.exception).startswith('Foo'))
  97. def test_import_error_traceback(self):
  98. """Test preserving the original traceback on an ImportError."""
  99. try:
  100. import_by_path('test_module.bad_module.content')
  101. except ImproperlyConfigured:
  102. traceback = sys.exc_info()[2]
  103. self.assertIsNotNone(traceback.tb_next.tb_next,
  104. 'Should have more than the calling frame in the traceback.')
  105. @modify_settings(INSTALLED_APPS={'append': 'utils_tests.test_module'})
  106. class AutodiscoverModulesTestCase(SimpleTestCase):
  107. def test_autodiscover_modules_found(self):
  108. autodiscover_modules('good_module')
  109. def test_autodiscover_modules_not_found(self):
  110. autodiscover_modules('missing_module')
  111. def test_autodiscover_modules_found_but_bad_module(self):
  112. with six.assertRaisesRegex(self, ImportError, "No module named '?a_package_name_that_does_not_exist'?"):
  113. autodiscover_modules('bad_module')
  114. def test_autodiscover_modules_several_one_bad_module(self):
  115. with six.assertRaisesRegex(self, ImportError, "No module named '?a_package_name_that_does_not_exist'?"):
  116. autodiscover_modules('good_module', 'bad_module')
  117. def test_autodiscover_modules_several_found(self):
  118. autodiscover_modules('good_module', 'another_good_module')
  119. def test_validate_registry_keeps_intact(self):
  120. from .test_module import site
  121. with six.assertRaisesRegex(self, Exception, "Some random exception."):
  122. autodiscover_modules('another_bad_module', register_to=site)
  123. self.assertEqual(site._registry, {})
  124. class ProxyFinder(object):
  125. def __init__(self):
  126. self._cache = {}
  127. def find_module(self, fullname, path=None):
  128. tail = fullname.rsplit('.', 1)[-1]
  129. try:
  130. fd, fn, info = imp.find_module(tail, path)
  131. if fullname in self._cache:
  132. old_fd = self._cache[fullname][0]
  133. if old_fd:
  134. old_fd.close()
  135. self._cache[fullname] = (fd, fn, info)
  136. except ImportError:
  137. return None
  138. else:
  139. return self # this is a loader as well
  140. def load_module(self, fullname):
  141. if fullname in sys.modules:
  142. return sys.modules[fullname]
  143. fd, fn, info = self._cache[fullname]
  144. try:
  145. return imp.load_module(fullname, fd, fn, info)
  146. finally:
  147. if fd:
  148. fd.close()
  149. class TestFinder(object):
  150. def __init__(self, *args, **kwargs):
  151. self.importer = zipimporter(*args, **kwargs)
  152. def find_module(self, path):
  153. importer = self.importer.find_module(path)
  154. if importer is None:
  155. return
  156. return TestLoader(importer)
  157. class TestLoader(object):
  158. def __init__(self, importer):
  159. self.importer = importer
  160. def load_module(self, name):
  161. mod = self.importer.load_module(name)
  162. mod.__loader__ = self
  163. return mod
  164. class CustomLoader(EggLoader):
  165. """The Custom Loader test is exactly the same as the EggLoader, but
  166. it uses a custom defined Loader and Finder that is intentionally
  167. split into two classes. Although the EggLoader combines both functions
  168. into one class, this isn't required.
  169. """
  170. def setUp(self):
  171. super(CustomLoader, self).setUp()
  172. sys.path_hooks.insert(0, TestFinder)
  173. sys.path_importer_cache.clear()
  174. def tearDown(self):
  175. super(CustomLoader, self).tearDown()
  176. sys.path_hooks.pop(0)