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.
This commit is contained in:
parent
1851dcf377
commit
9eeb788cfb
|
@ -1,13 +1,9 @@
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
from django.template.base import Origin, Template, Context, TemplateDoesNotExist
|
|
||||||
from django.conf import settings
|
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.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.module_loading import import_string
|
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
template_source_loaders = None
|
|
||||||
|
|
||||||
|
|
||||||
class LoaderOrigin(Origin):
|
class LoaderOrigin(Origin):
|
||||||
|
@ -26,58 +22,8 @@ def make_origin(display_name, loader, name, dirs):
|
||||||
return None
|
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):
|
def find_template(name, dirs=None):
|
||||||
# Calculate template_source_loaders the first time the function is executed
|
for loader in get_template_loaders():
|
||||||
# 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:
|
|
||||||
try:
|
try:
|
||||||
source, display_name = loader(name, dirs)
|
source, display_name = loader(name, dirs)
|
||||||
return (source, make_origin(display_name, loader, name, dirs))
|
return (source, make_origin(display_name, loader, name, dirs))
|
||||||
|
|
|
@ -5,7 +5,8 @@ to load templates from them in order, caching the result.
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
from django.template.base import TemplateDoesNotExist
|
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 django.utils.encoding import force_bytes
|
||||||
|
|
||||||
from .base import Loader as BaseLoader
|
from .base import Loader as BaseLoader
|
||||||
|
|
|
@ -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)
|
|
@ -87,8 +87,8 @@ def clear_context_processors_cache(**kwargs):
|
||||||
@receiver(setting_changed)
|
@receiver(setting_changed)
|
||||||
def clear_template_loaders_cache(**kwargs):
|
def clear_template_loaders_cache(**kwargs):
|
||||||
if kwargs['setting'] == 'TEMPLATE_LOADERS':
|
if kwargs['setting'] == 'TEMPLATE_LOADERS':
|
||||||
from django.template import loader
|
from django.template.loaders.utils import get_template_loaders
|
||||||
loader.template_source_loaders = None
|
get_template_loaders.cache_clear()
|
||||||
|
|
||||||
|
|
||||||
@receiver(setting_changed)
|
@receiver(setting_changed)
|
||||||
|
|
|
@ -12,6 +12,7 @@ from django.http import (HttpResponse, HttpResponseNotFound, HttpRequest,
|
||||||
build_request_repr)
|
build_request_repr)
|
||||||
from django.template import Template, Context, TemplateDoesNotExist
|
from django.template import Template, Context, TemplateDoesNotExist
|
||||||
from django.template.defaultfilters import force_escape, pprint
|
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.datastructures import MultiValueDict
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.encoding import force_bytes, smart_text
|
from django.utils.encoding import force_bytes, smart_text
|
||||||
|
@ -279,14 +280,15 @@ class ExceptionReporter(object):
|
||||||
"""Return a dictionary containing traceback information."""
|
"""Return a dictionary containing traceback information."""
|
||||||
|
|
||||||
if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
|
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.template_does_not_exist = True
|
||||||
self.loader_debug_info = []
|
self.loader_debug_info = []
|
||||||
# If the template_source_loaders haven't been populated yet, you need
|
# If Django fails in get_template_loaders, provide an empty list
|
||||||
# to provide an empty list for this for loop to not fail.
|
# for the following loop to not fail.
|
||||||
if template_source_loaders is None:
|
try:
|
||||||
template_source_loaders = []
|
template_loaders = get_template_loaders()
|
||||||
for loader in template_source_loaders:
|
except Exception:
|
||||||
|
template_loaders = []
|
||||||
|
for loader in template_loaders:
|
||||||
try:
|
try:
|
||||||
source_list_func = loader.get_template_sources
|
source_list_func = loader.get_template_sources
|
||||||
# NOTE: This assumes exc_value is the name of the template that
|
# NOTE: This assumes exc_value is the name of the template that
|
||||||
|
|
|
@ -22,6 +22,7 @@ except ImportError:
|
||||||
|
|
||||||
from django.template import TemplateDoesNotExist, Context
|
from django.template import TemplateDoesNotExist, Context
|
||||||
from django.template.loaders.eggs import Loader as EggLoader
|
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.template import loader
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
@ -127,7 +128,7 @@ class CachedLoader(TestCase):
|
||||||
|
|
||||||
def test_missing_template_is_cached(self):
|
def test_missing_template_is_cached(self):
|
||||||
"#19949 -- Check that the missing template is cached."
|
"#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.
|
# Empty cache, which may be filled from previous tests.
|
||||||
template_loader.reset()
|
template_loader.reset()
|
||||||
# Check that 'missing.html' isn't already in cache before 'missing.html' is loaded
|
# Check that 'missing.html' isn't already in cache before 'missing.html' is loaded
|
||||||
|
|
|
@ -14,7 +14,8 @@ from django.contrib.auth.models import Group
|
||||||
from django.core import urlresolvers
|
from django.core import urlresolvers
|
||||||
from django.template import (base as template_base, loader, Context,
|
from django.template import (base as template_base, loader, Context,
|
||||||
RequestContext, Template, TemplateSyntaxError)
|
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 import RequestFactory, TestCase
|
||||||
from django.test.utils import override_settings, extend_sys_path
|
from django.test.utils import override_settings, extend_sys_path
|
||||||
from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning
|
from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning
|
||||||
|
@ -218,21 +219,27 @@ class TemplateLoaderTests(TestCase):
|
||||||
@override_settings(TEMPLATE_DEBUG=True)
|
@override_settings(TEMPLATE_DEBUG=True)
|
||||||
def test_loader_debug_origin(self):
|
def test_loader_debug_origin(self):
|
||||||
# We rely on the fact that runtests.py sets up TEMPLATE_DIRS to
|
# We rely on the fact that runtests.py sets up TEMPLATE_DIRS to
|
||||||
# point to a directory containing a login.html file. Also that
|
# point to a directory containing a login.html file.
|
||||||
# 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.
|
|
||||||
load_name = 'login.html'
|
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 = loader.get_template(load_name)
|
||||||
template_name = template.nodelist[0].source[0].name
|
template_name = template.nodelist[0].source[0].name
|
||||||
self.assertTrue(template_name.endswith(load_name),
|
self.assertTrue(template_name.endswith(load_name),
|
||||||
'Template loaded by filesystem loader has incorrect name for debug page: %s' % template_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
|
@override_settings(TEMPLATE_LOADERS=[
|
||||||
cache_loader = cached.Loader(('',))
|
('django.template.loaders.cached.Loader',
|
||||||
cache_loader._cached_loaders = loader.template_source_loaders
|
['django.template.loaders.filesystem.Loader']),
|
||||||
loader.template_source_loaders = (cache_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 = loader.get_template(load_name)
|
||||||
template_name = template.nodelist[0].source[0].name
|
template_name = template.nodelist[0].source[0].name
|
||||||
self.assertTrue(template_name.endswith(load_name),
|
self.assertTrue(template_name.endswith(load_name),
|
||||||
|
@ -243,15 +250,15 @@ class TemplateLoaderTests(TestCase):
|
||||||
self.assertTrue(template_name.endswith(load_name),
|
self.assertTrue(template_name.endswith(load_name),
|
||||||
'Cached template loaded through cached loader has incorrect name for debug page: %s' % template_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):
|
def test_loader_origin(self):
|
||||||
with self.settings(TEMPLATE_DEBUG=True):
|
template = loader.get_template('login.html')
|
||||||
template = loader.get_template('login.html')
|
self.assertEqual(template.origin.loadname, 'login.html')
|
||||||
self.assertEqual(template.origin.loadname, 'login.html')
|
|
||||||
|
|
||||||
|
@override_settings(TEMPLATE_DEBUG=True)
|
||||||
def test_string_origin(self):
|
def test_string_origin(self):
|
||||||
with self.settings(TEMPLATE_DEBUG=True):
|
template = Template('string template')
|
||||||
template = Template('string template')
|
self.assertEqual(template.origin.source, 'string template')
|
||||||
self.assertEqual(template.origin.source, 'string template')
|
|
||||||
|
|
||||||
def test_debug_false_origin(self):
|
def test_debug_false_origin(self):
|
||||||
template = loader.get_template('login.html')
|
template = loader.get_template('login.html')
|
||||||
|
@ -613,7 +620,9 @@ class TemplateTests(TestCase):
|
||||||
if output != result:
|
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))
|
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:
|
if template_base.invalid_var_format_string:
|
||||||
expected_invalid_str = 'INVALID'
|
expected_invalid_str = 'INVALID'
|
||||||
|
|
|
@ -8,8 +8,7 @@ import os
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||||
from django.template import (TemplateSyntaxError,
|
from django.template import TemplateSyntaxError, Context, Template
|
||||||
Context, Template, loader)
|
|
||||||
import django.template.context
|
import django.template.context
|
||||||
from django.test import Client, TestCase, override_settings
|
from django.test import Client, TestCase, override_settings
|
||||||
from django.test.client import encode_file, RequestFactory
|
from django.test.client import encode_file, RequestFactory
|
||||||
|
@ -902,13 +901,6 @@ class ExceptionTests(TestCase):
|
||||||
@override_settings(ROOT_URLCONF='test_client_regress.urls')
|
@override_settings(ROOT_URLCONF='test_client_regress.urls')
|
||||||
class TemplateExceptionTests(TestCase):
|
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(
|
@override_settings(
|
||||||
TEMPLATE_DIRS=(os.path.join(os.path.dirname(upath(__file__)), 'bad_templates'),)
|
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"
|
"Errors found when rendering 404 error templates are re-raised"
|
||||||
try:
|
try:
|
||||||
self.client.get("/no_such_view/")
|
self.client.get("/no_such_view/")
|
||||||
self.fail("Should get error about syntax error in template")
|
|
||||||
except TemplateSyntaxError:
|
except TemplateSyntaxError:
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
|
self.fail("Should get error about syntax error in template")
|
||||||
|
|
||||||
|
|
||||||
# We need two different tests to check URLconf substitution - one to check
|
# We need two different tests to check URLconf substitution - one to check
|
||||||
|
|
Loading…
Reference in New Issue