summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2014-11-15 20:58:26 +0100
committerAymeric Augustin <aymeric.augustin@m4x.org>2014-11-16 21:41:44 +0100
commit9eeb788cfb70d07d87b3d07434d6a149ab2d7471 (patch)
tree10045a7dde41390e86c4e9c471f6955b69a8ce09
parent1851dcf377d05a5d167cf482674e4c4a4e2502a5 (diff)
Refactored getting the list of template loaders.
This provides the opportunity to move utility functions specific to the Django Template Language outside of django.template.loader.
-rw-r--r--django/template/loader.py60
-rw-r--r--django/template/loaders/cached.py3
-rw-r--r--django/template/loaders/utils.py57
-rw-r--r--django/test/signals.py4
-rw-r--r--django/views/debug.py14
-rw-r--r--tests/template_tests/test_loaders.py3
-rw-r--r--tests/template_tests/tests.py41
-rw-r--r--tests/test_client_regress/tests.py13
8 files changed, 102 insertions, 93 deletions
diff --git a/django/template/loader.py b/django/template/loader.py
index c62c383322..093d7d172b 100644
--- a/django/template/loader.py
+++ b/django/template/loader.py
@@ -1,13 +1,9 @@
import warnings
-from django.core.exceptions import ImproperlyConfigured
-from django.template.base import Origin, Template, Context, TemplateDoesNotExist
from django.conf import settings
+from django.template.base import Origin, Template, Context, TemplateDoesNotExist
+from django.template.loaders.utils import get_template_loaders
from django.utils.deprecation import RemovedInDjango20Warning
-from django.utils.module_loading import import_string
-from django.utils import six
-
-template_source_loaders = None
class LoaderOrigin(Origin):
@@ -26,58 +22,8 @@ def make_origin(display_name, loader, name, dirs):
return None
-def find_template_loader(loader):
- if isinstance(loader, (tuple, list)):
- loader, args = loader[0], loader[1:]
- else:
- args = []
- if isinstance(loader, six.string_types):
- TemplateLoader = import_string(loader)
-
- if hasattr(TemplateLoader, 'load_template_source'):
- func = TemplateLoader(*args)
- else:
- warnings.warn(
- "Function-based template loaders are deprecated. Please use "
- "class-based template loaders instead. Inherit base.Loader "
- "and provide a load_template_source() method.",
- RemovedInDjango20Warning, stacklevel=2)
-
- # Try loading module the old way - string is full path to callable
- if args:
- raise ImproperlyConfigured(
- "Error importing template source loader %s - can't pass "
- "arguments to function-based loader." % loader
- )
- func = TemplateLoader
-
- if not func.is_usable:
- import warnings
- warnings.warn(
- "Your TEMPLATE_LOADERS setting includes %r, but your Python "
- "installation doesn't support that type of template loading. "
- "Consider removing that line from TEMPLATE_LOADERS." % loader
- )
- return None
- else:
- return func
- else:
- raise ImproperlyConfigured('Loader does not define a "load_template" callable template source loader')
-
-
def find_template(name, dirs=None):
- # Calculate template_source_loaders the first time the function is executed
- # because putting this logic in the module-level namespace may cause
- # circular import errors. See Django ticket #1292.
- global template_source_loaders
- if template_source_loaders is None:
- loaders = []
- for loader_name in settings.TEMPLATE_LOADERS:
- loader = find_template_loader(loader_name)
- if loader is not None:
- loaders.append(loader)
- template_source_loaders = tuple(loaders)
- for loader in template_source_loaders:
+ for loader in get_template_loaders():
try:
source, display_name = loader(name, dirs)
return (source, make_origin(display_name, loader, name, dirs))
diff --git a/django/template/loaders/cached.py b/django/template/loaders/cached.py
index e217efc93f..180ed9900d 100644
--- a/django/template/loaders/cached.py
+++ b/django/template/loaders/cached.py
@@ -5,7 +5,8 @@ to load templates from them in order, caching the result.
import hashlib
from django.template.base import TemplateDoesNotExist
-from django.template.loader import get_template_from_string, find_template_loader, make_origin
+from django.template.loader import get_template_from_string, make_origin
+from django.template.loaders.utils import find_template_loader
from django.utils.encoding import force_bytes
from .base import Loader as BaseLoader
diff --git a/django/template/loaders/utils.py b/django/template/loaders/utils.py
new file mode 100644
index 0000000000..4850c9f1aa
--- /dev/null
+++ b/django/template/loaders/utils.py
@@ -0,0 +1,57 @@
+import warnings
+
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
+from django.utils import lru_cache
+from django.utils import six
+from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.module_loading import import_string
+
+
+@lru_cache.lru_cache()
+def get_template_loaders():
+ loaders = []
+ for loader_name in settings.TEMPLATE_LOADERS:
+ loader = find_template_loader(loader_name)
+ if loader is not None:
+ loaders.append(loader)
+ # Immutable return value because it will be cached and shared by callers.
+ return tuple(loaders)
+
+
+def find_template_loader(loader):
+ if isinstance(loader, (tuple, list)):
+ loader, args = loader[0], loader[1:]
+ else:
+ args = []
+ if isinstance(loader, six.string_types):
+ TemplateLoader = import_string(loader)
+
+ if hasattr(TemplateLoader, 'load_template_source'):
+ func = TemplateLoader(*args)
+ else:
+ warnings.warn(
+ "Function-based template loaders are deprecated. "
+ "Please use class-based template loaders instead. "
+ "Inherit django.template.loaders.base.Loader "
+ "and provide a load_template_source() method.",
+ RemovedInDjango20Warning, stacklevel=2)
+
+ # Try loading module the old way - string is full path to callable
+ if args:
+ raise ImproperlyConfigured(
+ "Error importing template source loader %s - can't pass "
+ "arguments to function-based loader." % loader)
+ func = TemplateLoader
+
+ if not func.is_usable:
+ warnings.warn(
+ "Your TEMPLATE_LOADERS setting includes %r, but your Python "
+ "installation doesn't support that type of template loading. "
+ "Consider removing that line from TEMPLATE_LOADERS." % loader)
+ return None
+ else:
+ return func
+ else:
+ raise ImproperlyConfigured(
+ "Invalid value in TEMPLATE_LOADERS: %r" % loader)
diff --git a/django/test/signals.py b/django/test/signals.py
index e080d976ee..f065e155b8 100644
--- a/django/test/signals.py
+++ b/django/test/signals.py
@@ -87,8 +87,8 @@ def clear_context_processors_cache(**kwargs):
@receiver(setting_changed)
def clear_template_loaders_cache(**kwargs):
if kwargs['setting'] == 'TEMPLATE_LOADERS':
- from django.template import loader
- loader.template_source_loaders = None
+ from django.template.loaders.utils import get_template_loaders
+ get_template_loaders.cache_clear()
@receiver(setting_changed)
diff --git a/django/views/debug.py b/django/views/debug.py
index 95264afbd5..59136ce90c 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -12,6 +12,7 @@ from django.http import (HttpResponse, HttpResponseNotFound, HttpRequest,
build_request_repr)
from django.template import Template, Context, TemplateDoesNotExist
from django.template.defaultfilters import force_escape, pprint
+from django.template.loaders.utils import get_template_loaders
from django.utils.datastructures import MultiValueDict
from django.utils.html import escape
from django.utils.encoding import force_bytes, smart_text
@@ -279,14 +280,15 @@ class ExceptionReporter(object):
"""Return a dictionary containing traceback information."""
if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
- from django.template.loader import template_source_loaders
self.template_does_not_exist = True
self.loader_debug_info = []
- # If the template_source_loaders haven't been populated yet, you need
- # to provide an empty list for this for loop to not fail.
- if template_source_loaders is None:
- template_source_loaders = []
- for loader in template_source_loaders:
+ # If Django fails in get_template_loaders, provide an empty list
+ # for the following loop to not fail.
+ try:
+ template_loaders = get_template_loaders()
+ except Exception:
+ template_loaders = []
+ for loader in template_loaders:
try:
source_list_func = loader.get_template_sources
# NOTE: This assumes exc_value is the name of the template that
diff --git a/tests/template_tests/test_loaders.py b/tests/template_tests/test_loaders.py
index e83b8060b1..c3c086f381 100644
--- a/tests/template_tests/test_loaders.py
+++ b/tests/template_tests/test_loaders.py
@@ -22,6 +22,7 @@ except ImportError:
from django.template import TemplateDoesNotExist, Context
from django.template.loaders.eggs import Loader as EggLoader
+from django.template.loaders.utils import find_template_loader
from django.template import loader
from django.test import TestCase, override_settings
from django.utils import six
@@ -127,7 +128,7 @@ class CachedLoader(TestCase):
def test_missing_template_is_cached(self):
"#19949 -- Check that the missing template is cached."
- template_loader = loader.find_template_loader(settings.TEMPLATE_LOADERS[0])
+ template_loader = find_template_loader(settings.TEMPLATE_LOADERS[0])
# Empty cache, which may be filled from previous tests.
template_loader.reset()
# Check that 'missing.html' isn't already in cache before 'missing.html' is loaded
diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py
index 334dd31f7b..b8097fbde1 100644
--- a/tests/template_tests/tests.py
+++ b/tests/template_tests/tests.py
@@ -14,7 +14,8 @@ from django.contrib.auth.models import Group
from django.core import urlresolvers
from django.template import (base as template_base, loader, Context,
RequestContext, Template, TemplateSyntaxError)
-from django.template.loaders import app_directories, filesystem, cached
+from django.template.loaders import app_directories, filesystem
+from django.template.loaders.utils import get_template_loaders
from django.test import RequestFactory, TestCase
from django.test.utils import override_settings, extend_sys_path
from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning
@@ -218,21 +219,27 @@ class TemplateLoaderTests(TestCase):
@override_settings(TEMPLATE_DEBUG=True)
def test_loader_debug_origin(self):
# We rely on the fact that runtests.py sets up TEMPLATE_DIRS to
- # point to a directory containing a login.html file. Also that
- # the file system and app directories loaders both inherit the
- # load_template method from the base Loader class, so we only need
- # to test one of them.
+ # point to a directory containing a login.html file.
load_name = 'login.html'
+
+ # We also rely on the fact the file system and app directories loaders
+ # both inherit the load_template method from the base Loader class, so
+ # we only need to test one of them.
template = loader.get_template(load_name)
template_name = template.nodelist[0].source[0].name
self.assertTrue(template_name.endswith(load_name),
'Template loaded by filesystem loader has incorrect name for debug page: %s' % template_name)
- # Also test the cached loader, since it overrides load_template
- cache_loader = cached.Loader(('',))
- cache_loader._cached_loaders = loader.template_source_loaders
- loader.template_source_loaders = (cache_loader,)
+ @override_settings(TEMPLATE_LOADERS=[
+ ('django.template.loaders.cached.Loader',
+ ['django.template.loaders.filesystem.Loader']),
+ ])
+ @override_settings(TEMPLATE_DEBUG=True)
+ def test_cached_loader_debug_origin(self):
+ # Same comment as in test_loader_debug_origin.
+ load_name = 'login.html'
+ # Test the cached loader separately since it overrides load_template.
template = loader.get_template(load_name)
template_name = template.nodelist[0].source[0].name
self.assertTrue(template_name.endswith(load_name),
@@ -243,15 +250,15 @@ class TemplateLoaderTests(TestCase):
self.assertTrue(template_name.endswith(load_name),
'Cached template loaded through cached loader has incorrect name for debug page: %s' % template_name)
+ @override_settings(TEMPLATE_DEBUG=True)
def test_loader_origin(self):
- with self.settings(TEMPLATE_DEBUG=True):
- template = loader.get_template('login.html')
- self.assertEqual(template.origin.loadname, 'login.html')
+ template = loader.get_template('login.html')
+ self.assertEqual(template.origin.loadname, 'login.html')
+ @override_settings(TEMPLATE_DEBUG=True)
def test_string_origin(self):
- with self.settings(TEMPLATE_DEBUG=True):
- template = Template('string template')
- self.assertEqual(template.origin.source, 'string template')
+ template = Template('string template')
+ self.assertEqual(template.origin.source, 'string template')
def test_debug_false_origin(self):
template = loader.get_template('login.html')
@@ -613,7 +620,9 @@ class TemplateTests(TestCase):
if output != result:
failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Expected %r, got %r" % (is_cached, invalid_str, template_debug, name, result, output))
- loader.template_source_loaders[0].reset()
+ # This relies on get_template_loaders() memoizing its
+ # result. All callers get the same iterable of loaders.
+ get_template_loaders()[0].reset()
if template_base.invalid_var_format_string:
expected_invalid_str = 'INVALID'
diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py
index 9ceba4992f..f7dcae15cf 100644
--- a/tests/test_client_regress/tests.py
+++ b/tests/test_client_regress/tests.py
@@ -8,8 +8,7 @@ import os
import itertools
from django.core.urlresolvers import reverse, NoReverseMatch
-from django.template import (TemplateSyntaxError,
- Context, Template, loader)
+from django.template import TemplateSyntaxError, Context, Template
import django.template.context
from django.test import Client, TestCase, override_settings
from django.test.client import encode_file, RequestFactory
@@ -902,13 +901,6 @@ class ExceptionTests(TestCase):
@override_settings(ROOT_URLCONF='test_client_regress.urls')
class TemplateExceptionTests(TestCase):
- def setUp(self):
- # Reset the loaders so they don't try to render cached templates.
- if loader.template_source_loaders is not None:
- for template_loader in loader.template_source_loaders:
- if hasattr(template_loader, 'reset'):
- template_loader.reset()
-
@override_settings(
TEMPLATE_DIRS=(os.path.join(os.path.dirname(upath(__file__)), 'bad_templates'),)
)
@@ -916,9 +908,10 @@ class TemplateExceptionTests(TestCase):
"Errors found when rendering 404 error templates are re-raised"
try:
self.client.get("/no_such_view/")
- self.fail("Should get error about syntax error in template")
except TemplateSyntaxError:
pass
+ else:
+ self.fail("Should get error about syntax error in template")
# We need two different tests to check URLconf substitution - one to check