test_loaders.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. """
  2. Test cases for the template loaders
  3. Note: This test requires setuptools!
  4. """
  5. import os.path
  6. import sys
  7. import types
  8. import unittest
  9. try:
  10. import pkg_resources
  11. except ImportError:
  12. pkg_resources = None
  13. from django.template import TemplateDoesNotExist, Context
  14. from django.template.loaders import cached, eggs
  15. from django.template.engine import Engine
  16. from django.template import loader
  17. from django.test import SimpleTestCase, override_settings
  18. from django.test.utils import IgnorePendingDeprecationWarningsMixin
  19. from django.utils import six
  20. from django.utils._os import upath
  21. from django.utils.six import StringIO
  22. TEMPLATES_DIR = os.path.join(os.path.dirname(upath(__file__)), 'templates')
  23. GLOBAL_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.dirname(upath(__file__))), 'templates')
  24. # Mock classes and objects for pkg_resources functions.
  25. class MockLoader(object):
  26. pass
  27. def create_egg(name, resources):
  28. """
  29. Creates a mock egg with a list of resources.
  30. name: The name of the module.
  31. resources: A dictionary of resources. Keys are the names and values the data.
  32. """
  33. egg = types.ModuleType(name)
  34. egg.__loader__ = MockLoader()
  35. egg.__path__ = ['/some/bogus/path/']
  36. egg.__file__ = '/some/bogus/path/__init__.pyc'
  37. egg._resources = resources
  38. sys.modules[name] = egg
  39. @unittest.skipUnless(pkg_resources, 'setuptools is not installed')
  40. class EggLoaderTest(SimpleTestCase):
  41. def setUp(self):
  42. self.loader = eggs.Loader(Engine.get_default())
  43. # Defined here b/c at module scope we may not have pkg_resources
  44. class MockProvider(pkg_resources.NullProvider):
  45. def __init__(self, module):
  46. pkg_resources.NullProvider.__init__(self, module)
  47. self.module = module
  48. def _has(self, path):
  49. return path in self.module._resources
  50. def _isdir(self, path):
  51. return False
  52. def get_resource_stream(self, manager, resource_name):
  53. return self.module._resources[resource_name]
  54. def _get(self, path):
  55. return self.module._resources[path].read()
  56. def _fn(self, base, resource_name):
  57. return os.path.normcase(resource_name)
  58. pkg_resources._provider_factories[MockLoader] = MockProvider
  59. self.empty_egg = create_egg("egg_empty", {})
  60. self.egg_1 = create_egg("egg_1", {
  61. os.path.normcase('templates/y.html'): StringIO("y"),
  62. os.path.normcase('templates/x.txt'): StringIO("x"),
  63. })
  64. @override_settings(INSTALLED_APPS=['egg_empty'])
  65. def test_empty(self):
  66. "Loading any template on an empty egg should fail"
  67. with self.assertRaises(TemplateDoesNotExist):
  68. self.loader.load_template_source("not-existing.html")
  69. @override_settings(INSTALLED_APPS=['egg_1'])
  70. def test_non_existing(self):
  71. "Template loading fails if the template is not in the egg"
  72. with self.assertRaises(TemplateDoesNotExist):
  73. self.loader.load_template_source("not-existing.html")
  74. @override_settings(INSTALLED_APPS=['egg_1'])
  75. def test_existing(self):
  76. "A template can be loaded from an egg"
  77. contents, template_name = self.loader.load_template_source("y.html")
  78. self.assertEqual(contents, "y")
  79. self.assertEqual(template_name, "egg:egg_1:templates/y.html")
  80. def test_not_installed(self):
  81. "Loading an existent template from an egg not included in any app should fail"
  82. with self.assertRaises(TemplateDoesNotExist):
  83. self.loader.load_template_source("y.html")
  84. class CachedLoader(SimpleTestCase):
  85. def setUp(self):
  86. self.loader = cached.Loader(Engine.get_default(), [
  87. 'django.template.loaders.filesystem.Loader',
  88. ])
  89. def test_templatedir_caching(self):
  90. "Check that the template directories form part of the template cache key. Refs #13573"
  91. # Retrieve a template specifying a template directory to check
  92. t1, name = self.loader.find_template('test.html', (os.path.join(TEMPLATES_DIR, 'first'),))
  93. # Now retrieve the same template name, but from a different directory
  94. t2, name = self.loader.find_template('test.html', (os.path.join(TEMPLATES_DIR, 'second'),))
  95. # The two templates should not have the same content
  96. self.assertNotEqual(t1.render(Context({})), t2.render(Context({})))
  97. def test_missing_template_is_cached(self):
  98. "#19949 -- Check that the missing template is cached."
  99. # Check that 'missing.html' isn't already in cache before 'missing.html' is loaded
  100. with self.assertRaises(KeyError):
  101. self.loader.template_cache["missing.html"]
  102. # Try to load it, it should fail
  103. with self.assertRaises(TemplateDoesNotExist):
  104. self.loader.load_template("missing.html")
  105. # Verify that the fact that the missing template, which hasn't been found, has actually
  106. # been cached:
  107. cached_miss = self.loader.template_cache["missing.html"]
  108. self.assertEqual(cached_miss, TemplateDoesNotExist,
  109. "Cached template loader doesn't cache file lookup misses. It should.")
  110. @override_settings(TEMPLATES=[{
  111. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  112. 'DIRS': [TEMPLATES_DIR],
  113. }])
  114. class RenderToStringTest(SimpleTestCase):
  115. def test_basic(self):
  116. self.assertEqual(loader.render_to_string('test_context.html'), 'obj:\n')
  117. def test_basic_context(self):
  118. self.assertEqual(loader.render_to_string('test_context.html',
  119. {'obj': 'test'}), 'obj:test\n')
  120. def test_empty_list(self):
  121. six.assertRaisesRegex(self, TemplateDoesNotExist,
  122. 'No template names provided$',
  123. loader.render_to_string, [])
  124. def test_select_templates_from_empty_list(self):
  125. six.assertRaisesRegex(self, TemplateDoesNotExist,
  126. 'No template names provided$',
  127. loader.select_template, [])
  128. @override_settings(TEMPLATES=[{
  129. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  130. 'DIRS': [TEMPLATES_DIR],
  131. }])
  132. class DeprecatedRenderToStringTest(IgnorePendingDeprecationWarningsMixin, SimpleTestCase):
  133. def test_existing_context_kept_clean(self):
  134. context = Context({'obj': 'before'})
  135. output = loader.render_to_string('test_context.html', {'obj': 'after'},
  136. context_instance=context)
  137. self.assertEqual(output, 'obj:after\n')
  138. self.assertEqual(context['obj'], 'before')
  139. def test_no_empty_dict_pushed_to_stack(self):
  140. """
  141. No empty dict should be pushed to the context stack when render_to_string
  142. is called without any argument (#21741).
  143. """
  144. # The stack should have a length of 1, corresponding to the builtins
  145. self.assertEqual('1',
  146. loader.render_to_string('test_context_stack.html').strip())
  147. self.assertEqual('1',
  148. loader.render_to_string('test_context_stack.html', context_instance=Context()).strip())
  149. @override_settings(TEMPLATES=[{
  150. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  151. }])
  152. class TemplateDirsOverrideTest(IgnorePendingDeprecationWarningsMixin, SimpleTestCase):
  153. dirs_tuple = (os.path.join(os.path.dirname(upath(__file__)), 'other_templates'),)
  154. dirs_list = list(dirs_tuple)
  155. dirs_iter = (dirs_tuple, dirs_list)
  156. def test_render_to_string(self):
  157. for dirs in self.dirs_iter:
  158. self.assertEqual(loader.render_to_string('test_dirs.html', dirs=dirs), 'spam eggs\n')
  159. def test_get_template(self):
  160. for dirs in self.dirs_iter:
  161. template = loader.get_template('test_dirs.html', dirs=dirs)
  162. self.assertEqual(template.render(Context({})), 'spam eggs\n')
  163. def test_select_template(self):
  164. for dirs in self.dirs_iter:
  165. template = loader.select_template(['test_dirs.html'], dirs=dirs)
  166. self.assertEqual(template.render(Context({})), 'spam eggs\n')
  167. @override_settings(TEMPLATES=[{
  168. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  169. 'DIRS': [GLOBAL_TEMPLATES_DIR],
  170. 'OPTIONS': {
  171. 'loaders': [
  172. ('django.template.loaders.cached.Loader', [
  173. 'django.template.loaders.filesystem.Loader',
  174. 'django.template.loaders.app_directories.Loader',
  175. ]),
  176. ],
  177. },
  178. }])
  179. class PriorityCacheLoader(SimpleTestCase):
  180. def test_basic(self):
  181. """
  182. Check that the order of template loader works. Refs #21460.
  183. """
  184. t1 = loader.get_template('priority/foo.html')
  185. self.assertEqual(t1.render(Context({})), 'priority\n')
  186. @override_settings(TEMPLATES=[{
  187. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  188. 'DIRS': [GLOBAL_TEMPLATES_DIR],
  189. 'OPTIONS': {
  190. 'loaders': [
  191. 'django.template.loaders.filesystem.Loader',
  192. 'django.template.loaders.app_directories.Loader',
  193. ],
  194. },
  195. }])
  196. class PriorityLoader(SimpleTestCase):
  197. def test_basic(self):
  198. """
  199. Check that the order of template loader works. Refs #21460.
  200. """
  201. t1 = loader.get_template('priority/foo.html')
  202. self.assertEqual(t1.render(Context({})), 'priority\n')