Commit 9eeb788c authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

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.
parent 1851dcf3
Loading
Loading
Loading
Loading
+3 −57
Original line number Diff line number Diff line
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))
+2 −1
Original line number Diff line number Diff line
@@ -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
+57 −0
Original line number Diff line number Diff line
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)
+2 −2
Original line number Diff line number Diff line
@@ -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)
+8 −6
Original line number Diff line number Diff line
@@ -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
Loading