summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2014-11-14 15:48:27 +0100
committerAymeric Augustin <aymeric.augustin@m4x.org>2014-11-23 09:29:53 +0100
commit572cdb43917ad860acaaffecd60b4e92599ae3eb (patch)
tree8bb0252292319d346d0b156384b9a3f61a0a8d10
parent17012b6936128fb771b98e4fa6d78caddd07a9a8 (diff)
Introduced a template engine class.
Moved Django templates loading infrastructure there.
-rw-r--r--django/template/base.py6
-rw-r--r--django/template/engine.py154
-rw-r--r--django/template/loader.py104
-rw-r--r--django/test/signals.py13
4 files changed, 184 insertions, 93 deletions
diff --git a/django/template/base.py b/django/template/base.py
index 7c69e26088..c8c9844a6a 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -124,7 +124,7 @@ class StringOrigin(Origin):
class Template(object):
- def __init__(self, template_string, origin=None, name=None):
+ def __init__(self, template_string, origin=None, name=None, engine=None):
try:
template_string = force_text(template_string)
except UnicodeDecodeError:
@@ -132,9 +132,13 @@ class Template(object):
"from unicode or UTF-8 strings.")
if settings.TEMPLATE_DEBUG and origin is None:
origin = StringOrigin(template_string)
+ if engine is None:
+ from .engine import Engine
+ engine = Engine.get_default()
self.nodelist = compile_string(template_string, origin)
self.name = name
self.origin = origin
+ self.engine = engine
def __iter__(self):
for node in self.nodelist:
diff --git a/django/template/engine.py b/django/template/engine.py
new file mode 100644
index 0000000000..c377dbbc83
--- /dev/null
+++ b/django/template/engine.py
@@ -0,0 +1,154 @@
+import warnings
+
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
+from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.functional import cached_property
+from django.utils import lru_cache
+
+from .base import Context, Template, TemplateDoesNotExist
+from .loaders.utils import _get_template_loaders
+
+
+_dirs_undefined = object()
+
+
+class Engine(object):
+
+ def __init__(self, dirs=None, app_dirs=False,
+ allowed_include_roots=None, context_processors=None,
+ loaders=None, string_if_invalid=''):
+ if dirs is None:
+ dirs = []
+ if allowed_include_roots is None:
+ allowed_include_roots = []
+ if context_processors is None:
+ context_processors = []
+ if loaders is None:
+ loaders = ['django.template.loaders.filesystem.Loader']
+ if app_dirs:
+ loaders += ['django.template.loaders.app_directories.Loader']
+ else:
+ if app_dirs:
+ raise ImproperlyConfigured(
+ "APP_DIRS must not be set when LOADERS is defined.")
+
+ self.dirs = dirs
+ self.app_dirs = app_dirs
+ self.allowed_include_roots = allowed_include_roots
+ self.context_processors = context_processors
+ self.loaders = loaders
+ self.string_if_invalid = string_if_invalid
+
+ @classmethod
+ @lru_cache.lru_cache()
+ def get_default(cls):
+ """Transitional method for refactoring."""
+ return cls(
+ dirs=settings.TEMPLATE_DIRS,
+ allowed_include_roots=settings.ALLOWED_INCLUDE_ROOTS,
+ context_processors=settings.TEMPLATE_CONTEXT_PROCESSORS,
+ loaders=settings.TEMPLATE_LOADERS,
+ string_if_invalid=settings.TEMPLATE_STRING_IF_INVALID,
+ )
+
+ @cached_property
+ def template_loaders(self):
+ return _get_template_loaders(self.loaders)
+
+ def find_template(self, name, dirs=None):
+ # Inner import to avoid circular dependency
+ from .loader import make_origin
+ for loader in self.template_loaders:
+ try:
+ source, display_name = loader(name, dirs)
+ return (source, make_origin(display_name, loader, name, dirs))
+ except TemplateDoesNotExist:
+ pass
+ raise TemplateDoesNotExist(name)
+
+ def get_template(self, template_name, dirs=_dirs_undefined):
+ """
+ Returns a compiled Template object for the given template name,
+ handling template inheritance recursively.
+ """
+ if dirs is _dirs_undefined:
+ dirs = None
+ else:
+ warnings.warn(
+ "The dirs argument of get_template is deprecated.",
+ RemovedInDjango20Warning, stacklevel=2)
+
+ template, origin = self.find_template(template_name, dirs)
+ if not hasattr(template, 'render'):
+ # template needs to be compiled
+ template = self.get_template_from_string(template, origin, template_name)
+ return template
+
+ def get_template_from_string(self, source, origin=None, name=None):
+ """
+ Returns a compiled Template object for the given template code,
+ handling template inheritance recursively.
+ """
+ return Template(source, origin, name)
+
+ def render_to_string(self, template_name, dictionary=None, context_instance=None,
+ dirs=_dirs_undefined):
+ """
+ Loads the given template_name and renders it with the given dictionary as
+ context. The template_name may be a string to load a single template using
+ get_template, or it may be a tuple to use select_template to find one of
+ the templates in the list. Returns a string.
+ """
+ if dirs is _dirs_undefined:
+ # Do not set dirs to None here to avoid triggering the deprecation
+ # warning in select_template or get_template.
+ pass
+ else:
+ warnings.warn(
+ "The dirs argument of render_to_string is deprecated.",
+ RemovedInDjango20Warning, stacklevel=2)
+
+ if isinstance(template_name, (list, tuple)):
+ t = self.select_template(template_name, dirs)
+ else:
+ t = self.get_template(template_name, dirs)
+ if not context_instance:
+ # Django < 1.8 accepted a Context in `dictionary` even though that's
+ # unintended. Preserve this ability but don't rewrap `dictionary`.
+ if isinstance(dictionary, Context):
+ return t.render(dictionary)
+ else:
+ return t.render(Context(dictionary))
+ if not dictionary:
+ return t.render(context_instance)
+ # Add the dictionary to the context stack, ensuring it gets removed again
+ # to keep the context_instance in the same state it started in.
+ with context_instance.push(dictionary):
+ return t.render(context_instance)
+
+ def select_template(self, template_name_list, dirs=_dirs_undefined):
+ """
+ Given a list of template names, returns the first that can be loaded.
+ """
+ if dirs is _dirs_undefined:
+ # Do not set dirs to None here to avoid triggering the deprecation
+ # warning in get_template.
+ pass
+ else:
+ warnings.warn(
+ "The dirs argument of select_template is deprecated.",
+ RemovedInDjango20Warning, stacklevel=2)
+
+ if not template_name_list:
+ raise TemplateDoesNotExist("No template names provided")
+ not_found = []
+ for template_name in template_name_list:
+ try:
+ return self.get_template(template_name, dirs)
+ except TemplateDoesNotExist as exc:
+ if exc.args[0] not in not_found:
+ not_found.append(exc.args[0])
+ continue
+ # If we get here, none of the templates could be loaded
+ raise TemplateDoesNotExist(', '.join(not_found))
diff --git a/django/template/loader.py b/django/template/loader.py
index 9641f32e78..0c24e3d373 100644
--- a/django/template/loader.py
+++ b/django/template/loader.py
@@ -1,12 +1,10 @@
import warnings
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
-
-_dirs_undefined = object()
+from .base import Origin
+from .engine import Engine
class LoaderOrigin(Origin):
@@ -25,102 +23,24 @@ def make_origin(display_name, loader, name, dirs):
return None
-def find_template(name, dirs=None):
- for loader in get_template_loaders():
- try:
- source, display_name = loader(name, dirs)
- return (source, make_origin(display_name, loader, name, dirs))
- except TemplateDoesNotExist:
- pass
- raise TemplateDoesNotExist(name)
-
-
-def get_template(template_name, dirs=_dirs_undefined):
- """
- Returns a compiled Template object for the given template name,
- handling template inheritance recursively.
- """
- if dirs is _dirs_undefined:
- dirs = None
- else:
- warnings.warn(
- "The dirs argument of get_template is deprecated.",
- RemovedInDjango20Warning, stacklevel=2)
-
- template, origin = find_template(template_name, dirs)
- if not hasattr(template, 'render'):
- # template needs to be compiled
- template = get_template_from_string(template, origin, template_name)
- return template
+def find_template(*args, **kwargs):
+ return Engine.get_default().find_template(*args, **kwargs)
-def get_template_from_string(source, origin=None, name=None):
- """
- Returns a compiled Template object for the given template code,
- handling template inheritance recursively.
- """
- return Template(source, origin, name)
+def get_template(*args, **kwargs):
+ return Engine.get_default().get_template(*args, **kwargs)
-def render_to_string(template_name, dictionary=None, context_instance=None,
- dirs=_dirs_undefined):
- """
- Loads the given template_name and renders it with the given dictionary as
- context. The template_name may be a string to load a single template using
- get_template, or it may be a tuple to use select_template to find one of
- the templates in the list. Returns a string.
- """
- if dirs is _dirs_undefined:
- # Do not set dirs to None here to avoid triggering the deprecation
- # warning in select_template or get_template.
- pass
- else:
- warnings.warn(
- "The dirs argument of render_to_string is deprecated.",
- RemovedInDjango20Warning, stacklevel=2)
+def get_template_from_string(*args, **kwargs):
+ return Engine.get_default().get_template_from_string(*args, **kwargs)
- if isinstance(template_name, (list, tuple)):
- t = select_template(template_name, dirs)
- else:
- t = get_template(template_name, dirs)
- if not context_instance:
- # Django < 1.8 accepted a Context in `dictionary` even though that's
- # unintended. Preserve this ability but don't rewrap `dictionary`.
- if isinstance(dictionary, Context):
- return t.render(dictionary)
- else:
- return t.render(Context(dictionary))
- if not dictionary:
- return t.render(context_instance)
- # Add the dictionary to the context stack, ensuring it gets removed again
- # to keep the context_instance in the same state it started in.
- with context_instance.push(dictionary):
- return t.render(context_instance)
+def render_to_string(*args, **kwargs):
+ return Engine.get_default().render_to_string(*args, **kwargs)
-def select_template(template_name_list, dirs=_dirs_undefined):
- "Given a list of template names, returns the first that can be loaded."
- if dirs is _dirs_undefined:
- # Do not set dirs to None here to avoid triggering the deprecation
- # warning in get_template.
- pass
- else:
- warnings.warn(
- "The dirs argument of select_template is deprecated.",
- RemovedInDjango20Warning, stacklevel=2)
- if not template_name_list:
- raise TemplateDoesNotExist("No template names provided")
- not_found = []
- for template_name in template_name_list:
- try:
- return get_template(template_name, dirs)
- except TemplateDoesNotExist as e:
- if e.args[0] not in not_found:
- not_found.append(e.args[0])
- continue
- # If we get here, none of the templates could be loaded
- raise TemplateDoesNotExist(', '.join(not_found))
+def select_template(*args, **kwargs):
+ return Engine.get_default().select_template(*args, **kwargs)
# This line must remain at the bottom to avoid import loops.
diff --git a/django/test/signals.py b/django/test/signals.py
index b8e7193087..013d293203 100644
--- a/django/test/signals.py
+++ b/django/test/signals.py
@@ -78,6 +78,19 @@ def update_connections_time_zone(**kwargs):
@receiver(setting_changed)
+def reset_default_template_engine(**kwargs):
+ if kwargs['setting'] in {
+ 'TEMPLATE_DIRS',
+ 'ALLOWED_INCLUDE_ROOTS',
+ 'TEMPLATE_CONTEXT_PROCESSORS',
+ 'TEMPLATE_LOADERS',
+ 'TEMPLATE_STRING_IF_INVALID',
+ }:
+ from django.template.engine import Engine
+ Engine.get_default.cache_clear()
+
+
+@receiver(setting_changed)
def clear_context_processors_cache(**kwargs):
if kwargs['setting'] == 'TEMPLATE_CONTEXT_PROCESSORS':
from django.template.context import get_standard_processors