Moved template loaders management in Engine.
Passed the engine instance to loaders. This is a prerequisite for looking up configuration on the engine instance instead of global settings. This is backwards incompatible for custom template loaders that override __init__. However the documentation doesn't talk about __init__ and the way to pass arguments to custom template loaders isn't specified. I'm considering it a private API.
This commit is contained in:
parent
544a716da8
commit
29a977ab14
|
@ -2,12 +2,13 @@ import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
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.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils import lru_cache
|
from django.utils.module_loading import import_string
|
||||||
|
|
||||||
from .base import Context, Template, TemplateDoesNotExist
|
from .base import Context, Template, TemplateDoesNotExist
|
||||||
from .loaders.utils import get_template_loaders
|
|
||||||
|
|
||||||
|
|
||||||
_dirs_undefined = object()
|
_dirs_undefined = object()
|
||||||
|
@ -54,7 +55,49 @@ class Engine(object):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def template_loaders(self):
|
def template_loaders(self):
|
||||||
return get_template_loaders(self.loaders)
|
return self.get_template_loaders(self.loaders)
|
||||||
|
|
||||||
|
def get_template_loaders(self, template_loaders):
|
||||||
|
loaders = []
|
||||||
|
for template_loader in template_loaders:
|
||||||
|
loader = self.find_template_loader(template_loader)
|
||||||
|
if loader is not None:
|
||||||
|
loaders.append(loader)
|
||||||
|
return loaders
|
||||||
|
|
||||||
|
def find_template_loader(self, loader):
|
||||||
|
if isinstance(loader, (tuple, list)):
|
||||||
|
args = list(loader[1:])
|
||||||
|
loader = loader[0]
|
||||||
|
else:
|
||||||
|
args = []
|
||||||
|
|
||||||
|
if isinstance(loader, six.string_types):
|
||||||
|
loader_class = import_string(loader)
|
||||||
|
|
||||||
|
if getattr(loader_class, '_accepts_engine_in_init', False):
|
||||||
|
args.insert(0, self)
|
||||||
|
else:
|
||||||
|
warnings.warn(
|
||||||
|
"%s inherits from django.template.loader.BaseLoader "
|
||||||
|
"instead of django.template.loaders.base.Loader. " %
|
||||||
|
loader, RemovedInDjango20Warning, stacklevel=2)
|
||||||
|
|
||||||
|
loader_instance = loader_class(*args)
|
||||||
|
|
||||||
|
if not loader_instance.is_usable:
|
||||||
|
warnings.warn(
|
||||||
|
"Your template loaders configuration includes %r, but "
|
||||||
|
"your Python installation doesn't support that type of "
|
||||||
|
"template loading. Consider removing that line from "
|
||||||
|
"your settings." % loader)
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return loader_instance
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
"Invalid value in template loaders configuration: %r" % loader)
|
||||||
|
|
||||||
def find_template(self, name, dirs=None):
|
def find_template(self, name, dirs=None):
|
||||||
# Inner import to avoid circular dependency
|
# Inner import to avoid circular dependency
|
||||||
|
|
|
@ -48,10 +48,11 @@ from .loaders import base
|
||||||
|
|
||||||
|
|
||||||
class BaseLoader(base.Loader):
|
class BaseLoader(base.Loader):
|
||||||
|
_accepts_engine_in_init = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"django.template.loader.BaseLoader was renamed to "
|
"django.template.loader.BaseLoader was superseded by "
|
||||||
"django.template.loaders.base.Loader.",
|
"django.template.loaders.base.Loader.",
|
||||||
RemovedInDjango20Warning, stacklevel=2)
|
RemovedInDjango20Warning, stacklevel=2)
|
||||||
super(BaseLoader, self).__init__(*args, **kwargs)
|
super(BaseLoader, self).__init__(*args, **kwargs)
|
||||||
|
|
|
@ -4,10 +4,11 @@ from django.template.loader import get_template_from_string, make_origin
|
||||||
|
|
||||||
class Loader(object):
|
class Loader(object):
|
||||||
is_usable = False
|
is_usable = False
|
||||||
|
# Only used to raise a deprecation warning. Remove in Django 2.0.
|
||||||
|
_accepts_engine_in_init = True
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, engine):
|
||||||
# XXX dropping arguments silently may not be the best idea.
|
self.engine = engine
|
||||||
pass
|
|
||||||
|
|
||||||
def __call__(self, template_name, template_dirs=None):
|
def __call__(self, template_name, template_dirs=None):
|
||||||
return self.load_template(template_name, template_dirs)
|
return self.load_template(template_name, template_dirs)
|
||||||
|
|
|
@ -9,18 +9,16 @@ from django.template.loader import get_template_from_string, make_origin
|
||||||
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
|
||||||
from .utils import get_template_loaders
|
|
||||||
|
|
||||||
|
|
||||||
class Loader(BaseLoader):
|
class Loader(BaseLoader):
|
||||||
is_usable = True
|
is_usable = True
|
||||||
|
|
||||||
def __init__(self, loaders):
|
def __init__(self, engine, loaders):
|
||||||
self.template_cache = {}
|
self.template_cache = {}
|
||||||
self.find_template_cache = {}
|
self.find_template_cache = {}
|
||||||
# Use the private, non-caching version of get_template_loaders
|
self.loaders = engine.get_template_loaders(loaders)
|
||||||
# in case loaders isn't hashable.
|
super(Loader, self).__init__(engine)
|
||||||
self.loaders = get_template_loaders(loaders)
|
|
||||||
|
|
||||||
def cache_key(self, template_name, template_dirs):
|
def cache_key(self, template_name, template_dirs):
|
||||||
if template_dirs:
|
if template_dirs:
|
||||||
|
|
|
@ -10,8 +10,9 @@ from .base import Loader as BaseLoader
|
||||||
class Loader(BaseLoader):
|
class Loader(BaseLoader):
|
||||||
is_usable = True
|
is_usable = True
|
||||||
|
|
||||||
def __init__(self, templates_dict):
|
def __init__(self, engine, templates_dict):
|
||||||
self.templates_dict = templates_dict
|
self.templates_dict = templates_dict
|
||||||
|
super(Loader, self).__init__(engine)
|
||||||
|
|
||||||
def load_template_source(self, template_name, template_dirs=None):
|
def load_template_source(self, template_name, template_dirs=None):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
import warnings
|
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
from django.utils import six
|
|
||||||
from django.utils.module_loading import import_string
|
|
||||||
|
|
||||||
|
|
||||||
def get_template_loaders(template_loaders):
|
|
||||||
loaders = []
|
|
||||||
for template_loader in template_loaders:
|
|
||||||
loader = find_template_loader(template_loader)
|
|
||||||
if loader is not None:
|
|
||||||
loaders.append(loader)
|
|
||||||
return 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):
|
|
||||||
loader_class = import_string(loader)
|
|
||||||
loader_instance = loader_class(*args)
|
|
||||||
|
|
||||||
if not loader_instance.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 loader_instance
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise ImproperlyConfigured(
|
|
||||||
"Invalid value in TEMPLATE_LOADERS: %r" % loader)
|
|
|
@ -97,13 +97,6 @@ def clear_context_processors_cache(**kwargs):
|
||||||
get_standard_processors.cache_clear()
|
get_standard_processors.cache_clear()
|
||||||
|
|
||||||
|
|
||||||
@receiver(setting_changed)
|
|
||||||
def clear_template_loaders_cache(**kwargs):
|
|
||||||
if kwargs['setting'] == 'TEMPLATE_LOADERS':
|
|
||||||
from django.template.loaders.utils import get_template_loaders
|
|
||||||
get_template_loaders.cache_clear()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(setting_changed)
|
@receiver(setting_changed)
|
||||||
def clear_serializers_cache(**kwargs):
|
def clear_serializers_cache(**kwargs):
|
||||||
if kwargs['setting'] == 'SERIALIZATION_MODULES':
|
if kwargs['setting'] == 'SERIALIZATION_MODULES':
|
||||||
|
|
|
@ -12,7 +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.template.engine import Engine
|
||||||
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
|
||||||
|
@ -282,7 +282,8 @@ class ExceptionReporter(object):
|
||||||
# If Django fails in get_template_loaders, provide an empty list
|
# If Django fails in get_template_loaders, provide an empty list
|
||||||
# for the following loop to not fail.
|
# for the following loop to not fail.
|
||||||
try:
|
try:
|
||||||
template_loaders = get_template_loaders()
|
# TODO: handle multiple template engines.
|
||||||
|
template_loaders = Engine.get_default().template_loaders
|
||||||
except Exception:
|
except Exception:
|
||||||
template_loaders = []
|
template_loaders = []
|
||||||
for loader in template_loaders:
|
for loader in template_loaders:
|
||||||
|
|
|
@ -22,7 +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.engine import Engine
|
||||||
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.test.utils import IgnorePendingDeprecationWarningsMixin
|
from django.test.utils import IgnorePendingDeprecationWarningsMixin
|
||||||
|
@ -86,26 +86,26 @@ class EggLoaderTest(TestCase):
|
||||||
@override_settings(INSTALLED_APPS=['egg_empty'])
|
@override_settings(INSTALLED_APPS=['egg_empty'])
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
"Loading any template on an empty egg should fail"
|
"Loading any template on an empty egg should fail"
|
||||||
egg_loader = EggLoader()
|
egg_loader = EggLoader(Engine.get_default())
|
||||||
self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "not-existing.html")
|
self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "not-existing.html")
|
||||||
|
|
||||||
@override_settings(INSTALLED_APPS=['egg_1'])
|
@override_settings(INSTALLED_APPS=['egg_1'])
|
||||||
def test_non_existing(self):
|
def test_non_existing(self):
|
||||||
"Template loading fails if the template is not in the egg"
|
"Template loading fails if the template is not in the egg"
|
||||||
egg_loader = EggLoader()
|
egg_loader = EggLoader(Engine.get_default())
|
||||||
self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "not-existing.html")
|
self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "not-existing.html")
|
||||||
|
|
||||||
@override_settings(INSTALLED_APPS=['egg_1'])
|
@override_settings(INSTALLED_APPS=['egg_1'])
|
||||||
def test_existing(self):
|
def test_existing(self):
|
||||||
"A template can be loaded from an egg"
|
"A template can be loaded from an egg"
|
||||||
egg_loader = EggLoader()
|
egg_loader = EggLoader(Engine.get_default())
|
||||||
contents, template_name = egg_loader.load_template_source("y.html")
|
contents, template_name = egg_loader.load_template_source("y.html")
|
||||||
self.assertEqual(contents, "y")
|
self.assertEqual(contents, "y")
|
||||||
self.assertEqual(template_name, "egg:egg_1:templates/y.html")
|
self.assertEqual(template_name, "egg:egg_1:templates/y.html")
|
||||||
|
|
||||||
def test_not_installed(self):
|
def test_not_installed(self):
|
||||||
"Loading an existent template from an egg not included in any app should fail"
|
"Loading an existent template from an egg not included in any app should fail"
|
||||||
egg_loader = EggLoader()
|
egg_loader = EggLoader(Engine.get_default())
|
||||||
self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "y.html")
|
self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "y.html")
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,7 +129,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 = find_template_loader(settings.TEMPLATE_LOADERS[0])
|
template_loader = Engine.get_default().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,8 +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.engine import Engine
|
||||||
from django.template.loaders import app_directories, filesystem
|
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
|
||||||
|
@ -160,8 +160,8 @@ class UTF8Class:
|
||||||
class TemplateLoaderTests(TestCase):
|
class TemplateLoaderTests(TestCase):
|
||||||
|
|
||||||
def test_loaders_security(self):
|
def test_loaders_security(self):
|
||||||
ad_loader = app_directories.Loader()
|
ad_loader = app_directories.Loader(Engine.get_default())
|
||||||
fs_loader = filesystem.Loader()
|
fs_loader = filesystem.Loader(Engine.get_default())
|
||||||
|
|
||||||
def test_template_sources(path, template_dirs, expected_sources):
|
def test_template_sources(path, template_dirs, expected_sources):
|
||||||
if isinstance(expected_sources, list):
|
if isinstance(expected_sources, list):
|
||||||
|
@ -620,9 +620,7 @@ 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))
|
||||||
|
|
||||||
# This relies on get_template_loaders() memoizing its
|
Engine.get_default().template_loaders[0].reset()
|
||||||
# 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'
|
||||||
|
|
Loading…
Reference in New Issue